# Buoyancy System Analysis

## Problem: Characters are not affected by buoyancy

After analyzing the code in `src/features/editScene`, I've identified several potential issues:

## 1. Character Gravity Factor Issue

**Root Cause**: Characters have gravity factor set to 0.0f by default.

In `CharacterSystem.cpp` line 163:
```cpp
m_physics->setGravityFactor(ch->GetBodyID(), 0.0f);
```

This means characters won't sink into water naturally. The `BuoyancySystem` tries to handle this by setting gravity factor to 1.0f when characters are in water (line 127 in `BuoyancySystem.cpp`), but there may be timing or detection issues.

## 2. Broadphase Query Area Settings

The `broadphaseQuery` function in `physics.cpp` uses these settings:

```cpp
JPH::AABox water_box(-JPH::Vec3(1000, 1000, 1000),
                     JPH::Vec3(1000, 0.1f, 1000));
water_box.Translate(JPH::Vec3(surface_point));
```

Where `surface_point = position + Ogre::Vector3(0, -0.1f, 0)` (position is the water surface Y level).

**Dimensions**:
- X: -1000 to 1000 (2000 units wide, centered at surface_point.x)
- Y: -1000 to 0.1f (1000.1 units tall, but centered 0.1 units BELOW water surface)
- Z: -1000 to 1000 (2000 units deep, centered at surface_point.z)

**Issue**: The water box extends 1000 units BELOW the surface point, but only 0.1 units ABOVE it. Since `surface_point` is 0.1 units below the actual water surface, the box effectively covers:
- From 1000.1 units below water surface
- To 0.0 units at water surface (not above it)

This means bodies need to be at or below the water surface to be detected.

## 3. Character Detection in Broadphase

Characters are `JPH::Character` objects, not regular dynamic bodies. The broadphase query filters for:
- `BroadPhaseLayers::MOVING` layer
- `Layers::MOVING` object layer

Characters should be in these layers, but there may be issues with how character bodies are registered in the broadphase.

## 4. Debugging Approach

### 4.1 Enable Debug Logging

Modify `BuoyancySystem.cpp` to add debug logging:

```cpp
// In update() method, after broadphaseQuery call:
Ogre::LogManager::getSingleton().logMessage(
    "BuoyancySystem: Found " + Ogre::StringConverter::toString(m_bodiesInWater.size()) + 
    " bodies in water");

// In the loop applying buoyancy:
for (JPH::BodyID bodyID : m_bodiesInWater) {
    Ogre::SceneNode *node = m_physics->getSceneNodeFromBodyID(bodyID);
    if (node) {
        Ogre::LogManager::getSingleton().logMessage(
            "Body " + Ogre::StringConverter::toString(bodyID.GetIndex()) + 
            " at position: " + Ogre::StringConverter::toString(m_physics->getPosition(bodyID)));
    }
    
    // Check if it's a character
    if (m_physics->bodyIsCharacter(bodyID)) {
        Ogre::LogManager::getSingleton().logMessage(
            "Body " + Ogre::StringConverter::toString(bodyID.GetIndex()) + " is a character");
    }
}
```

### 4.2 Visual Debugging - Draw Water Box

Add debug rendering to visualize the water detection area:

```cpp
// In BuoyancySystem::update(), after broadphaseQuery:
void drawDebugWaterBox(const Ogre::Vector3& waterSurfacePos) {
    // Create a manual object to visualize the water box
    static Ogre::ManualObject* waterBoxDebug = nullptr;
    if (!waterBoxDebug) {
        waterBoxDebug = m_sceneManager->createManualObject("WaterBoxDebug");
        Ogre::SceneNode* debugNode = m_sceneManager->getRootSceneNode()->createChildSceneNode();
        debugNode->attachObject(waterBoxDebug);
    }
    
    waterBoxDebug->clear();
    waterBoxDebug->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST);
    
    // Water box dimensions (matching broadphaseQuery)
    float halfSize = 1000.0f;
    float top = waterSurfacePos.y - 0.1f + 0.1f; // surface_point.y + 0.1f
    float bottom = waterSurfacePos.y - 0.1f - 1000.0f; // surface_point.y - 1000.0f
    
    // Draw box edges
    Ogre::Vector3 corners[8] = {
        {waterSurfacePos.x - halfSize, bottom, waterSurfacePos.z - halfSize},
        {waterSurfacePos.x + halfSize, bottom, waterSurfacePos.z - halfSize},
        {waterSurfacePos.x + halfSize, bottom, waterSurfacePos.z + halfSize},
        {waterSurfacePos.x - halfSize, bottom, waterSurfacePos.z + halfSize},
        {waterSurfacePos.x - halfSize, top, waterSurfacePos.z - halfSize},
        {waterSurfacePos.x + halfSize, top, waterSurfacePos.z - halfSize},
        {waterSurfacePos.x + halfSize, top, waterSurfacePos.z + halfSize},
        {waterSurfacePos.x - halfSize, top, waterSurfacePos.z + halfSize}
    };
    
    // Bottom square
    for (int i = 0; i < 4; i++) {
        waterBoxDebug->position(corners[i]);
        waterBoxDebug->position(corners[(i+1)%4]);
    }
    
    // Top square
    for (int i = 4; i < 8; i++) {
        waterBoxDebug->position(corners[i]);
        waterBoxDebug->position(corners[4 + (i-3)%4]);
    }
    
    // Vertical edges
    for (int i = 0; i < 4; i++) {
        waterBoxDebug->position(corners[i]);
        waterBoxDebug->position(corners[i+4]);
    }
    
    waterBoxDebug->end();
}
```

### 4.3 Check Character Position Relative to Water

Add a debug function to check character positions:

```cpp
void debugCharacterPositions() {
    m_world.query<CharacterComponent, TransformComponent>().each(
        [&](flecs::entity entity, CharacterComponent &cc, TransformComponent &transform) {
            if (transform.node) {
                Ogre::Vector3 worldPos = transform.node->_getDerivedPosition();
                Ogre::LogManager::getSingleton().logMessage(
                    "Character entity " + Ogre::StringConverter::toString(entity.id()) +
                    " at Y: " + Ogre::StringConverter::toString(worldPos.y));
                
                // Check if character has physics body
                auto it = m_states.find(entity.id());
                if (it != m_states.end() && it->second.character) {
                    JPH::BodyID bodyID = it->second.character->GetBodyID();
                    Ogre::Vector3 bodyPos = m_physics->getPosition(bodyID);
                    Ogre::LogManager::getSingleton().logMessage(
                        "Character body at Y: " + Ogre::StringConverter::toString(bodyPos.y) +
                        ", gravity factor: " + Ogre::StringConverter::toString(m_physics->getGravityFactor(bodyID)));
                }
            }
        });
}
```

## 5. Recommended Fixes

### 5.1 Adjust Water Box Parameters

The current water box may be too shallow (only 0.1 units at the top). Consider adjusting:

```cpp
// In broadphaseQuery function:
JPH::AABox water_box(-JPH::Vec3(1000, 1.0f, 1000),  // Increased from 0.1f to 1.0f
                     JPH::Vec3(1000, 1000, 1000));  // Symmetrical above/below
```

This creates a 2-unit tall detection area centered on the surface point.

### 5.2 Fix Character Gravity Handling

Modify `BuoyancySystem::update()` to better handle character gravity:

```cpp
// Current issue: characters with gravity factor 0 won't sink into water
// Even if buoyancy is applied, they need gravity to sink first

// Potential fix: Always give characters some minimal gravity when near water
// or modify CharacterSystem to not set gravity factor to 0
```

### 5.3 Verify Character Body Registration

Ensure character bodies are properly registered in the physics system and included in broadphase queries. Check that:
1. Characters are added to the physics system (`ch->AddToPhysicsSystem()`)
2. They are in the `MOVING` broadphase layer
3. Their body IDs are valid for queries

## 6. Testing Procedure

1. **Enable debug logging** as shown above
2. **Place a character in water** (Y position below water surface)
3. **Check console output** for:
   - Number of bodies detected in water
   - Character body positions
   - Gravity factor changes
4. **Use visual debug** to see water box
5. **Adjust water surface Y** in WaterPhysics component to ensure it's above character position

## 7. Water Physics Settings

Default `WaterPhysics` component has:
- `waterSurfaceY = -0.1f` (slightly below origin)
- `defaultBuoyancy = 1.0f` (neutral buoyancy)
- `enabled = true`

Make sure:
1. WaterPhysics entity exists (BuoyancySystem creates one if missing)
2. `waterSurfaceY` is above character positions for testing
3. Water physics is enabled (`enabled = true`)