From 35f50f7f51941492e92f7dd870b8d65655865c88 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Wed, 22 Apr 2026 18:57:33 +0300 Subject: [PATCH] Fixed buoyancy --- src/features/editScene/EditorApp.cpp | 26 ++---- .../editScene/components/WaterPhysics.hpp | 6 +- .../editScene/systems/BuoyancySystem.cpp | 34 +------- .../editScene/systems/CharacterSystem.cpp | 19 +++-- .../editScene/systems/EditorUISystem.cpp | 81 ++----------------- .../editScene/systems/SceneSerializer.cpp | 6 +- 6 files changed, 37 insertions(+), 135 deletions(-) diff --git a/src/features/editScene/EditorApp.cpp b/src/features/editScene/EditorApp.cpp index eda5799..bc0c726 100644 --- a/src/features/editScene/EditorApp.cpp +++ b/src/features/editScene/EditorApp.cpp @@ -501,20 +501,6 @@ void EditorApp::setupECS() void EditorApp::createDefaultEntities() { // Start with empty scene - user creates entities as needed - - // Create global WaterPhysics component for buoyancy system - flecs::entity waterPhysicsEntity = m_world.entity("WaterPhysics"); - WaterPhysics waterPhysics; - waterPhysics.enabled = true; - waterPhysics.waterSurfaceY = - 1.0f; // Raise water surface to 1.0m above ground - waterPhysics.waterDensity = 1000.0f; // kg/m³ - waterPhysics.gravity = 9.81f; // m/s² - waterPhysics.defaultBuoyancy = 1.0f; - waterPhysics.defaultLinearDrag = 0.1f; - waterPhysics.defaultAngularDrag = 0.05f; - waterPhysics.defaultSubmergedThreshold = 0.3f; - waterPhysicsEntity.set(waterPhysics); } void EditorApp::setupLights() @@ -674,12 +660,7 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt) m_characterSystem->update(evt.timeSinceLastFrame); } - /* --- Main physics step --- */ - if (m_physicsSystem) { - m_physicsSystem->update(evt.timeSinceLastFrame); - } - - /* --- Buoyancy system (after physics) --- */ + /* --- Buoyancy system (before physics so impulse is integrated) --- */ if (m_buoyancySystem) { // Update camera position for water detection area if (m_camera) { @@ -689,6 +670,11 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt) m_buoyancySystem->update(evt.timeSinceLastFrame); } + /* --- Main physics step --- */ + if (m_physicsSystem) { + m_physicsSystem->update(evt.timeSinceLastFrame); + } + /* --- Rendering support systems --- */ if (m_lightSystem) { m_lightSystem->update(); diff --git a/src/features/editScene/components/WaterPhysics.hpp b/src/features/editScene/components/WaterPhysics.hpp index a62c89a..e968a35 100644 --- a/src/features/editScene/components/WaterPhysics.hpp +++ b/src/features/editScene/components/WaterPhysics.hpp @@ -8,9 +8,9 @@ struct WaterPhysics { float waterSurfaceY = -0.1f; // Default buoyancy parameters (used when entity has no BuoyancyInfo) - float defaultBuoyancy = 1.0f; // 1.0 = neutral buoyancy - float defaultLinearDrag = 0.1f; - float defaultAngularDrag = 0.05f; + float defaultBuoyancy = 1.5f; // 1.0 = neutral, >1 floats (Jolt convention) + float defaultLinearDrag = 0.5f; // Jolt recommends ~0.5 + float defaultAngularDrag = 0.01f; // Jolt recommends ~0.01 float defaultSubmergedThreshold = 0.1f; // Minimum submerged fraction to apply buoyancy diff --git a/src/features/editScene/systems/BuoyancySystem.cpp b/src/features/editScene/systems/BuoyancySystem.cpp index 1628eec..8a789a5 100644 --- a/src/features/editScene/systems/BuoyancySystem.cpp +++ b/src/features/editScene/systems/BuoyancySystem.cpp @@ -1,6 +1,4 @@ #include "BuoyancySystem.hpp" -#include "../components/EditorMarker.hpp" -#include "../components/EntityName.hpp" #include "../components/Character.hpp" #include #include @@ -10,20 +8,6 @@ BuoyancySystem::BuoyancySystem(flecs::world &world, JoltPhysicsWrapper *physics) , m_physics(physics) , m_buoyancyInfoQuery(world.query()) { - // Create default WaterPhysics entity if none exists - bool hasWaterPhysics = false; - world.query().each( - [&](flecs::entity, WaterPhysics &) { hasWaterPhysics = true; }); - - if (!hasWaterPhysics) { - flecs::entity waterEntity = world.entity("WaterPhysics"); - waterEntity.set({}); - waterEntity.add(); - waterEntity.set( - EntityNameComponent("Water Physics")); - Ogre::LogManager::getSingleton().logMessage( - "BuoyancySystem: Created default WaterPhysics entity"); - } } void BuoyancySystem::initialize() @@ -162,7 +146,6 @@ void BuoyancySystem::applyBuoyancyToBody(JPH::BodyID bodyID, float deltaTime, float buoyancy = waterPhysics->defaultBuoyancy; float linearDrag = waterPhysics->defaultLinearDrag; float angularDrag = waterPhysics->defaultAngularDrag; - float submergedThreshold = waterPhysics->defaultSubmergedThreshold; // Override with per-entity settings if available if (buoyancyInfo) { @@ -172,25 +155,14 @@ void BuoyancySystem::applyBuoyancyToBody(JPH::BodyID bodyID, float deltaTime, buoyancy = buoyancyInfo->buoyancy; linearDrag = buoyancyInfo->linearDrag; angularDrag = buoyancyInfo->angularDrag; - submergedThreshold = buoyancyInfo->submergedThreshold; - } - - // Check if body is sufficiently submerged - if (!isBodySubmerged(bodyID, waterSurfaceY)) { - return; } // Get body position Ogre::Vector3 bodyPos = m_physics->getPosition(bodyID); - // Calculate submerged depth (how far below water surface) - float submergedDepth = waterSurfaceY - bodyPos.y; - if (submergedDepth <= 0.0f) { - return; // Not submerged - } - - // Simple approximation: use body's bounding sphere radius - // In a more advanced implementation, we would calculate actual submerged volume + // Let Jolt handle submersion detection internally via ApplyBuoyancyImpulse. + // The previous center-point check prevented partial submersion from working, + // which caused characters standing in shallow water to receive no buoyancy. Ogre::Vector3 surfacePosition(bodyPos.x, waterSurfaceY, bodyPos.z); Ogre::Vector3 surfaceNormal(0.0f, 1.0f, 0.0f); // Water surface faces up Ogre::Vector3 fluidVelocity(0.0f, 0.0f, 0.0f); // Still water for now diff --git a/src/features/editScene/systems/CharacterSystem.cpp b/src/features/editScene/systems/CharacterSystem.cpp index b7faa93..74baf7f 100644 --- a/src/features/editScene/systems/CharacterSystem.cpp +++ b/src/features/editScene/systems/CharacterSystem.cpp @@ -231,11 +231,20 @@ void CharacterSystem::update(float deltaTime) JoltPhysics::convert(nodePos)); } - /* Apply velocity via Jolt linear velocity (matches - * original game code) */ - state.character->SetLinearVelocity( - JoltPhysics::convert( - cc.linearVelocity)); + /* Apply velocity via Jolt linear velocity. + * Preserve physics-driven Y velocity when no explicit + * vertical input is given so gravity/buoyancy/jumps + * are not overwritten every frame. */ + JPH::Vec3 currentVel = state.character->GetLinearVelocity(); + JPH::Vec3 desiredVel = JoltPhysics::convert( + cc.linearVelocity); + JPH::Vec3 finalVel; + finalVel.SetX(desiredVel.GetX()); + finalVel.SetZ(desiredVel.GetZ()); + finalVel.SetY(desiredVel.GetY() != 0.0f ? + desiredVel.GetY() : + currentVel.GetY()); + state.character->SetLinearVelocity(finalVel); if (cc.linearVelocity.squaredLength() > 0.0001f) { std::cout << "CharacterSystem::update: entity=" << e.id() diff --git a/src/features/editScene/systems/EditorUISystem.cpp b/src/features/editScene/systems/EditorUISystem.cpp index 575ba7b..52339ff 100644 --- a/src/features/editScene/systems/EditorUISystem.cpp +++ b/src/features/editScene/systems/EditorUISystem.cpp @@ -7,6 +7,7 @@ #include "../components/PhysicsCollider.hpp" #include "../components/RigidBody.hpp" #include "../components/BuoyancyInfo.hpp" +#include "../components/WaterPhysics.hpp" #include "../components/Light.hpp" #include "../components/Camera.hpp" #include "../components/Lod.hpp" @@ -311,79 +312,6 @@ void EditorUISystem::renderHierarchyWindow() } } - ImGui::Separator(); - ImGui::Text("Water Physics"); - ImGui::Separator(); - - // Find and edit WaterPhysics component - WaterPhysics *waterPhysics = nullptr; - m_world.query().each( - [&](flecs::entity entity, - WaterPhysics &wp) { - waterPhysics = ℘ - }); - - if (waterPhysics) { - if (ImGui::Checkbox( - "Water Physics Enabled", - &waterPhysics->enabled)) { - } - if (ImGui::SliderFloat( - "Water Surface Y", - &waterPhysics->waterSurfaceY, - -10.0f, 10.0f, "%.2f")) { - } - if (ImGui::SliderFloat( - "Water Density (kg/m³)", - &waterPhysics->waterDensity, - 500.0f, 2000.0f, "%.0f")) { - } - if (ImGui::SliderFloat( - "Gravity (m/s²)", - &waterPhysics->gravity, - 0.0f, 20.0f, "%.2f")) { - } - - ImGui::Separator(); - ImGui::Text( - "Default Buoyancy Parameters"); - - if (ImGui::SliderFloat( - "Default Buoyancy", - &waterPhysics - ->defaultBuoyancy, - 0.0f, 5.0f, "%.2f")) { - } - ImGui::SameLine(); - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "Default buoyancy density multiplier (1.0 = neutral buoyancy)"); - } - - if (ImGui::SliderFloat( - "Default Linear Drag", - &waterPhysics - ->defaultLinearDrag, - 0.0f, 1.0f, "%.3f")) { - } - if (ImGui::SliderFloat( - "Default Angular Drag", - &waterPhysics - ->defaultAngularDrag, - 0.0f, 1.0f, "%.3f")) { - } - if (ImGui::SliderFloat( - "Submerged Threshold", - &waterPhysics - ->defaultSubmergedThreshold, - 0.0f, 1.0f, "%.2f")) { - } - } else { - ImGui::TextDisabled( - "No WaterPhysics component found"); - } - ImGui::EndMenu(); } ImGui::EndMenuBar(); @@ -683,6 +611,13 @@ void EditorUISystem::renderComponentList(flecs::entity entity) componentCount++; } + // Render WaterPhysics if present + if (entity.has()) { + auto &wp = entity.get_mut(); + m_componentRegistry.render(entity, wp); + componentCount++; + } + // Render LOD Settings if present if (entity.has()) { auto &lodSettings = entity.get_mut(); diff --git a/src/features/editScene/systems/SceneSerializer.cpp b/src/features/editScene/systems/SceneSerializer.cpp index ac8d181..0373fd6 100644 --- a/src/features/editScene/systems/SceneSerializer.cpp +++ b/src/features/editScene/systems/SceneSerializer.cpp @@ -2287,9 +2287,9 @@ void SceneSerializer::deserializeWaterPhysics(flecs::entity entity, water.waterSurfaceY = json.value("waterSurfaceY", -0.1f); water.waterDensity = json.value("waterDensity", 1000.0f); water.gravity = json.value("gravity", 9.81f); - water.defaultBuoyancy = json.value("defaultBuoyancy", 1.0f); - water.defaultLinearDrag = json.value("defaultLinearDrag", 0.1f); - water.defaultAngularDrag = json.value("defaultAngularDrag", 0.05f); + water.defaultBuoyancy = json.value("defaultBuoyancy", 1.5f); + water.defaultLinearDrag = json.value("defaultLinearDrag", 0.5f); + water.defaultAngularDrag = json.value("defaultAngularDrag", 0.01f); water.defaultSubmergedThreshold = json.value("defaultSubmergedThreshold", 0.3f);