# 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().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`)