From 1c56387c3552f4cae6ae0f6f5479c24fd0f33706 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Wed, 3 Sep 2025 22:16:53 +0300 Subject: [PATCH] Fixed water body presence physics --- Game.cpp | 2 + src/gamedata/CharacterModule.cpp | 62 +++++++++- src/gamedata/Components.h | 1 + src/gamedata/GameData.cpp | 3 +- src/gamedata/WaterModule.cpp | 193 +++++++++++++++++++++++++++++-- src/gamedata/WaterModule.h | 6 +- 6 files changed, 249 insertions(+), 18 deletions(-) diff --git a/Game.cpp b/Game.cpp index d861a02..ded5ed1 100644 --- a/Game.cpp +++ b/Game.cpp @@ -620,6 +620,8 @@ public: void updateWorld(float delta) { mDynWorld->getBtWorld()->stepSimulation(delta, 4); + mDbgDraw->update(); + ECS::update(delta); } void updateWater(float delta) diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp index 5ae0fcb..980e21c 100644 --- a/src/gamedata/CharacterModule.cpp +++ b/src/gamedata/CharacterModule.cpp @@ -3,6 +3,7 @@ #include #include "GameData.h" #include "CharacterModule.h" +#include "WaterModule.h" #include "Components.h" namespace ECS { @@ -191,7 +192,19 @@ CharacterModule::CharacterModule(flecs::world &ecs) bool is_on_floor = false; bool penetration = false; Ogre::Vector3 gravity(0, -9.8, 0); - body.gvelocity += gravity * delta; + if (e.has()) { + float full_subm = 2.0f; + float current_subm = -Ogre::Math::Clamp( + pos.y, -full_subm, 0.0f); + float b = 9.0 * current_subm / full_subm; + body.gvelocity += + (gravity + Ogre::Vector3(0, b, 0)) * + delta; + body.gvelocity.y = Ogre::Math::Clamp( + body.gvelocity.y, -1.0f, 1.0f); + std::cout << "InWater!!!!!!!\n"; + } else + body.gvelocity += gravity * delta; velocity += body.gvelocity; Ogre::Vector3 rotMotion = velocity * delta; btVector3 currentPosition = @@ -293,9 +306,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) inertia); } body.mGhostObject->setCollisionFlags( - btCollisionObject::CF_KINEMATIC_OBJECT | - btCollisionObject:: - CF_NO_CONTACT_RESPONSE); + btCollisionObject::CF_KINEMATIC_OBJECT /*| + btCollisionObject::CF_NO_CONTACT_RESPONSE */); body.mGhostObject->setActivationState( DISABLE_DEACTIVATION); eng.mWorld->attachCollisionObject( @@ -459,6 +471,48 @@ CharacterModule::CharacterModule(flecs::world &ecs) body.checkGround = false; } }); + ecs.system( + "CharacterWater1") + .kind(flecs::OnUpdate) + .with() + .without() + .each([](flecs::entity e, const WaterBody &waterb, + const CharacterBase &ch, CharacterBody &body) { + if (waterb.mInWater.find(body.mGhostObject) != + waterb.mInWater.end() && + ch.mBodyNode->_getDerivedPosition().y < -0.05f) { + e.add(); + std::cout << "Big Splash\n"; + } +#if 0 + if (waterb.mInWater.find(body.mGhostObject) == + waterb.mInWater.end()) + e.add(); + std::cout << waterb.mInWater.size() << " InWater\n"; +#endif + }); + ecs.system( + "CharacterWater2") + .kind(flecs::OnUpdate) + .with() + .with() + .each([](flecs::entity e, const WaterBody &waterb, + const CharacterBase &ch, CharacterBody &body) { + if (waterb.mInWater.find(body.mGhostObject) == + waterb.mInWater.end() && + ch.mBodyNode->_getDerivedPosition().y > 0.05f) + e.remove(); + }); + ecs.system( + "DisplayPlayerPos") + .kind(flecs::OnUpdate) + .with() + .with() + .each([](const EngineData &eng, CharacterBase &ch, + CharacterBody &body) { + std::cout << "player: " << ch.mBodyNode->getPosition() + << "\n"; + }); } void CharacterModule::setAnimation(AnimationControl &anim) diff --git a/src/gamedata/Components.h b/src/gamedata/Components.h index 79e4970..e6b31ba 100644 --- a/src/gamedata/Components.h +++ b/src/gamedata/Components.h @@ -46,5 +46,6 @@ struct Camera { Ogre::SceneNode *mCameraGoal; Ogre::Real mPivotPitch; }; +struct InWater {}; } #endif \ No newline at end of file diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 96e84d3..c7cb827 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -15,6 +15,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); + ecs.component(); ecs.set({ scnMgr, world, 0.0f }); ecs.set({ cameraNode, camera, false }); ecs.add(); @@ -24,8 +25,8 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, .each([](EngineData &eng) { eng.delta = ECS::get().delta_time(); }); - ecs.import (); ecs.import (); + ecs.import (); } void update(float delta) { diff --git a/src/gamedata/WaterModule.cpp b/src/gamedata/WaterModule.cpp index 729f5f6..28be7e4 100644 --- a/src/gamedata/WaterModule.cpp +++ b/src/gamedata/WaterModule.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -164,12 +165,23 @@ WaterModule::WaterModule(flecs::world &ecs) texture_unit->setTextureFiltering( Ogre::FT_MIP, Ogre::FO_LINEAR); #if 0 + bool success = + Ogre::RTShader::ShaderGenerator::getSingletonPtr() + ->createShaderBasedTechnique( + *mat, + Ogre::MSN_DEFAULT, + Ogre::MSN_SHADERGEN); + OgreAssert( + success, + "createShaderBasedTechnique"); Ogre::RTShader::RenderState *renderState = Ogre::RTShader::ShaderGenerator:: getSingletonPtr() - ->createOrRetrieveRenderState( - Ogre::MSN_SHADERGEN) - .first; + ->getRenderState( + Ogre::MSN_SHADERGEN, + *mat, + 0); + Ogre::RTShader::SubRenderState *perPixelLightModel = Ogre::RTShader::ShaderGenerator::getSingletonPtr() ->createSubRenderState( @@ -283,26 +295,183 @@ WaterModule::WaterModule(flecs::world &ecs) ecs.system( "UpdateWaterBody") .kind(flecs::OnUpdate) - .each([](const EngineData &eng, const WaterSurface &water, - WaterBody &body) { + .each([this](const EngineData &eng, const WaterSurface &water, + WaterBody &body) { + int i; if (!body.mWaterBody) { - body.mWaterBody = new btGhostObject; - btBoxShape *boxShape = new btBoxShape( - btVector3(1000, 1000, 1000)); btCompoundShape *shape = new btCompoundShape; - shape->addChildShape( - btTransform(btQuaternion(), - btVector3(0, -1000, 0)), - boxShape); + btBoxShape *boxShape1 = new btBoxShape( + btVector3(1000, 20, 1000)); + btBoxShape *boxShape2 = new btBoxShape( + btVector3(1000, 100, 1000)); + btBoxShape *boxShape3 = new btBoxShape( + btVector3(1000, 1000, 1000)); + btTransform boxShapeXform1(btQuaternion(), + btVector3(0, -20.2f, + 0)); + btTransform boxShapeXform2(btQuaternion(), + btVector3(0, -120.2f, + 0)); + btTransform boxShapeXform3( + btQuaternion(), + btVector3(0, -1120.2f, 0)); + shape->addChildShape(boxShapeXform1, boxShape1); + shape->addChildShape(boxShapeXform2, boxShape2); + shape->addChildShape(boxShapeXform3, boxShape3); + body.mWaterBody = new btPairCachingGhostObject; body.mWaterBody->setCollisionShape(shape); body.mWaterBody->setCollisionFlags( body.mWaterBody->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE | btCollisionObject::CF_STATIC_OBJECT); + body.mWaterBody->setActivationState( + DISABLE_DEACTIVATION); + body.mWaterBody->getWorldTransform().setOrigin( + btVector3(0, 0, 0)); eng.mWorld->attachCollisionObject( body.mWaterBody, water.mWaterEnt, 16, 0x7fffffff & ~2); } + Ogre::Vector3 waterPos = + water.mWaterNode->_getDerivedPosition(); + Ogre::Vector3 waterBodyPos = Ogre::Bullet::convert( + body.mWaterBody->getWorldTransform() + .getOrigin()); + waterPos.y = 0; + waterBodyPos.y = 0; + Ogre::Vector3 d = waterPos - waterBodyPos; + d.y = 0; + std::cout << "aabb: " + << Ogre::Bullet::convert(body.mShapeAabbMin) + << " " + << Ogre::Bullet::convert(body.mShapeAabbMax) + << "\n"; + // Ogre::Vector3 waterPosition = mCameraPos; + // mWaterNode->setPosition(waterPosition); + if (d.squaredLength() > 10.0f * 10.0f) + body.mWaterBody->getWorldTransform().setOrigin( + Ogre::Bullet::convert(waterBodyPos + + d)); + std::cout + << "node: " << water.mWaterNode->getPosition() + << " body: " + << Ogre::Bullet::convert( + body.mWaterBody->getWorldTransform() + .getOrigin()) + << "\n"; + btCompoundShape *mshape = + static_cast( + body.mWaterBody->getCollisionShape()); + body.mShapeAabbMin = + body.mWaterBody->getWorldTransform() + .getOrigin() + + btVector3(-1000, -1000, -1000); + body.mShapeAabbMax = + body.mWaterBody->getWorldTransform() + .getOrigin() + + btVector3(1000, -0.2, 1000); +#if 0 + mshape->getChildShape(0)->getAabb( + body.mWaterBody->getWorldTransform() * + mshape->getChildTransform(0), + body.mShapeAabbMin, body.mShapeAabbMax); +#endif + btDispatcher *dispatch = + eng.mWorld->getBtWorld()->getDispatcher(); + eng.mWorld->getBtWorld()->getBroadphase()->setAabb( + body.mWaterBody->getBroadphaseHandle(), + body.mShapeAabbMin, body.mShapeAabbMax, + eng.mWorld->getBtWorld()->getDispatcher()); + dispatch->dispatchAllCollisionPairs( + body.mWaterBody->getOverlappingPairCache(), + eng.mWorld->getBtWorld()->getDispatchInfo(), + dispatch); + btBroadphasePairArray &collisionPairs = + body.mWaterBody->getOverlappingPairCache() + ->getOverlappingPairArray(); + std::set currentOverlaps; + std::set::iterator it; + const int numObjects = collisionPairs.size(); + for (int i = 0; i < numObjects; i++) { + body.mManifoldArray.resize(0); + const btBroadphasePair &collisionPair = + collisionPairs[i]; + if (collisionPair.m_algorithm) + collisionPair.m_algorithm + ->getAllContactManifolds( + body.mManifoldArray); + for (int j = 0; j < body.mManifoldArray.size(); + j++) { + btPersistentManifold *manifold = + body.mManifoldArray[j]; + if (manifold->getNumContacts() == 0) + continue; + const btCollisionObject *obj = + (manifold->getBody0() == + body.mWaterBody) ? + manifold->getBody1() : + manifold->getBody0(); + btCollisionObject *nobj = + const_cast( + obj); + if (obj->getCollisionFlags() & + btCollisionObject::CF_STATIC_OBJECT) + continue; + bool ok = false; + Ogre::Vector3 contactPosA, contactPosB; + float minDist = 0.0f; + for (int p = 0; + p < manifold->getNumContacts(); + p++) { + const btManifoldPoint &pt = + manifold->getContactPoint( + p); + float dist = pt.getDistance(); + if (dist < minDist) { + minDist = dist; + std::cout + << " distance: " + << dist << "\n"; + contactPosA = Ogre::Bullet::convert( + pt.getPositionWorldOnA()); + contactPosB = Ogre::Bullet::convert( + pt.getPositionWorldOnB()); + std::cout + << "positionA: " + << contactPosA + << "\n"; + std::cout + << "positionB: " + << contactPosA + << "\n"; + ok = true; + } + } + if (ok) { + currentOverlaps.insert(nobj); + if (body.mInWater.find(nobj) == + body.mInWater.end()) { + /* new body */ + body.mInWater.insert( + nobj); + /* calculate proj surface */ + body.mSurface[nobj] = + 100.0f; + } + } + } + } + for (it = body.mInWater.begin(); + it != body.mInWater.end();) { + btCollisionObject *obj = *it; + if (currentOverlaps.find(obj) == + currentOverlaps.end()) { + /* remove body */ + it = body.mInWater.erase(it); + body.mSurface.erase(obj); + } else + it++; + } }); } void WaterSurface::RenderTextureListener::preRenderTargetUpdate( diff --git a/src/gamedata/WaterModule.h b/src/gamedata/WaterModule.h index b483401..447a536 100644 --- a/src/gamedata/WaterModule.h +++ b/src/gamedata/WaterModule.h @@ -25,7 +25,11 @@ struct WaterSurface { Ogre::Viewport *mViewports[2]; }; struct WaterBody { - btGhostObject *mWaterBody; + btPairCachingGhostObject *mWaterBody; + std::set mInWater; + std::unordered_map mSurface; + btManifoldArray mManifoldArray; + btVector3 mShapeAabbMin, mShapeAabbMax; }; struct WaterModule { WaterModule(flecs::world &ecs);