From c730ca5222473ca3f9081e2cf49ffb67c890b4cb Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Tue, 26 Aug 2025 08:13:21 +0300 Subject: [PATCH] updates --- Game.cpp | 17 ++++- src/characters/controller.cpp | 122 +++++++++------------------------- src/characters/controller.h | 40 +++++++++++ src/terrain/terrain.h | 9 ++- 4 files changed, 96 insertions(+), 92 deletions(-) diff --git a/Game.cpp b/Game.cpp index 5d2eaca..b977369 100644 --- a/Game.cpp +++ b/Game.cpp @@ -244,7 +244,7 @@ public: float panel_width; void initGui(); }; -#undef WATER +#define WATER class App : public OgreBites::ApplicationContext { std::unique_ptr mDynWorld; std::unique_ptr mDbgDraw; @@ -262,6 +262,7 @@ class App : public OgreBites::ApplicationContext { #ifdef WATER Water m_water; #endif + bool mTerrainReady; class KeyboardListener : public OgreBites::InputListener, public Ogre::FrameListener { App *mApp; @@ -366,8 +367,10 @@ class App : public OgreBites::ApplicationContext { update(evt.timeSinceLastFrame); if (mApp->getCharacterController() && gui_active) mApp->getCharacterController()->disableUpdates(); - else if (mApp->getCharacterController() && !gui_active) + else if (mApp->getCharacterController() && !gui_active && mApp->isTerrainReady()) { + OgreAssert(mApp->isTerrainReady(), "terrain is not ready"); mApp->getCharacterController()->enableUpdates(); + } if (!gui_active) { mApp->updateSun(evt.timeSinceLastFrame); mApp->updateTerrain(evt.timeSinceLastFrame); @@ -387,6 +390,7 @@ public: Ogre::Vector3(0, -9.8, 0))) , m_terrain(mDynWorld->getBtWorld()) , mCharacterController(nullptr) + , mTerrainReady(false) { } virtual ~App() @@ -550,6 +554,15 @@ public: Ogre::Timer mTerrainUpd; void updateTerrain(float delta) { + Ogre::Vector3 pos = mCharacterController->getPosition(); + if (!mTerrainReady && m_terrain.isLoadedAt(pos) && mCharacterController->checkGround()) { + std::cout << "terrain ready\n"; + mTerrainReady = true; + } + } + bool isTerrainReady() + { + return mTerrainReady; } // TODO: implement rough water level calculation float getWaterLevel(const Ogre::Vector3 &position) diff --git a/src/characters/controller.cpp b/src/characters/controller.cpp index 6bf7f45..37cecb5 100644 --- a/src/characters/controller.cpp +++ b/src/characters/controller.cpp @@ -184,6 +184,7 @@ CharacterController::CharacterController(Ogre::SceneNode *camNode, , mWorld(world) , mCollisionShape(nullptr) , mGhostObject(nullptr) + , gvelocity(0.0f, 0.0f, 0.0f) , mUpdate(false) { setupBody(); @@ -231,12 +232,12 @@ void CharacterController::setupBody() mWorld->attachCollisionObject(mGhostObject, mBodyEnt, 1, 0x7FFFFFF); - Ogre::Bullet::KinematicMotionSimple *controller = + mController = new Ogre::Bullet::KinematicMotionSimple(mGhostObject, mBodyNode); OgreAssert(mGhostObject, "Need GhostObject"); - OgreAssert(controller, "Need controller"); - mWorld->getBtWorld()->addAction(controller); + OgreAssert(mController, "Need controller"); + mWorld->getBtWorld()->addAction(mController); OgreAssert(mCollisionShape, "No collision shape"); #if 0 @@ -555,105 +556,48 @@ struct EntityCollisionListener const Ogre::MovableObject* entity; Ogre::Bullet::CollisionListener* listener; }; -static bool is_on_floor = false; -static bool penetration = false; -static int is_on_floor_count = 0; void CharacterController::updateRootMotion(Ogre::Real delta) { int i, j, k; + Ogre::Quaternion rot = mBodyNode->getOrientation(); + Ogre::Vector3 pos = mBodyNode->getPosition(); Ogre::Vector3 boneMotion = mRootBone->getPosition(); + Ogre::Vector3 velocity = rot * boneMotion / delta; OgreAssert(delta > 0.0f, "Zero delta"); int maxPen = 0; Ogre::Vector3 colNormal; - btVector3 currentPosition = mGhostObject->getWorldTransform().getOrigin(); - for (i = 0; i < mGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++) { - btManifoldArray manifoldArray; - btBroadphasePair *collisionPair = &mGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; - if (collisionPair->m_algorithm) - collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); - for (j = 0; j < manifoldArray.size(); j++) { - btPersistentManifold * manifold = manifoldArray[j]; - btScalar directionSign = manifold->getBody0() == mGhostObject ? btScalar(-1) : btScalar(1); - for (k = 0; k < manifold->getNumContacts(); k++) { - const btManifoldPoint &pt = manifold->getContactPoint(k); - btScalar dist = pt.getDistance(); - if (dist < 0) { - maxPen = dist; - btVector3 touchingNormal = pt.m_normalWorldOnB; - colNormal = Ogre::Bullet::convert(touchingNormal); - if (touchingNormal.y() > 0 && ( - touchingNormal.y() > Ogre::Math::Abs(touchingNormal.x()) || - touchingNormal.y() > Ogre::Math::Abs(touchingNormal.z()))) - is_on_floor = true; - penetration = true; - } - currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2); - - } - } -#if 0 - std::cout << "overlapping: " << i << " " - << manifoldArray.size() - << " is_on_floor = " << is_on_floor - << " position: " << Ogre::Bullet::convert(currentPosition) - << " colNormal: " << colNormal - << " penetration: " << penetration - << " dist: " << maxPen << "\n"; -#endif - } - btTransform newTrans = mGhostObject->getWorldTransform(); - newTrans.setOrigin(currentPosition); -#if 0 - Ogre::Vector3 motion = boneMotion - rootMotion; - if (motion.squaredLength() > 0.1f * 0.1f) - motion = Ogre::Vector3(); - rootMotion = boneMotion; -#endif -#if 0 - float mass = mRigidBody->getMass(); - std::cout << "Root bone position: " << boneMotion << "\n"; - std::cout << "body mass: " << mass << "\n"; -#endif - /* Kinematic motion */ - if (is_on_floor) - is_on_floor_count++; - else - is_on_floor_count--; - Ogre::Quaternion rot = mBodyNode->getOrientation(); + bool is_on_floor = false; + bool penetration = false; Ogre::Vector3 gravity(0, -9.8, 0); - Ogre::Vector3 velocity = rot * boneMotion / delta; - if (is_on_floor_count < 0 && !penetration) - velocity += gravity; - if (is_on_floor_count < 0 && penetration) - velocity += gravity * delta; - if (is_on_floor_count < -1) - is_on_floor_count = -1; - if (is_on_floor_count > 1) - is_on_floor_count = 1; + gvelocity += gravity * delta; + velocity += gvelocity; Ogre::Vector3 rotMotion = velocity * delta; + btVector3 currentPosition = mGhostObject->getWorldTransform().getOrigin(); + is_on_floor = mController->isOnFloor(); + penetration = mController->isPenetrating(); + if (is_on_floor) + gvelocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); + btTransform from(convert(mBodyNode->getOrientation()), convert(mBodyNode->getPosition())); mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion); - // WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true, - // nullptr, false, std::set()); #if 0 - if (mGhostObject->getNumOverlappingObjects() > 0) { - int i; - std::cout << "overlaps: " << mGhostObject->getNumOverlappingObjects() << "\n"; - for (i = 0; i < mGhostObject->getNumOverlappingObjects(); i++) { - btCollisionObject *collided = nullptr; - EntityCollisionListener *a = nullptr; - collided = mGhostObject->getOverlappingObject(i); - if (collided) - a = static_cast(collided->getUserPointer()); - std::cout << i << " " << collided << " " << a << " "; - if (a) - std::cout << a->entity << " "; - if (a->entity) - std::cout << a->entity->getName(); - std::cout << "\n"; - } - } + std::cout << "rotMotion: " << rotMotion + << " isOnFloor: " << is_on_floor + << " pairs: " << mGhostObject->getOverlappingPairCache()->getNumOverlappingPairs() + << " manifolds: " << mController->getManifolds() + << " penetration: " << penetration << "\n"; + std::cout << " position: " << mBodyNode->getPosition() << "\n"; +#endif +#if 0 + std::cout + << " velocity: " << velocity + << " penetration: " << penetration + << " rotMotion: " << rotMotion + << "\n"; + std::cout << "old position: " << pos + << " new position: " << mBodyNode->getPosition() + << "\n"; #endif } void CharacterController::fadeAnimations(Ogre::Real delta) diff --git a/src/characters/controller.h b/src/characters/controller.h index 0a1a0bc..4c6e373 100644 --- a/src/characters/controller.h +++ b/src/characters/controller.h @@ -80,8 +80,10 @@ class CharacterController : public OgreBites::InputListener, // btRigidBody *mRigidBody; btCompoundShape *mCollisionShape; btPairCachingGhostObject *mGhostObject; + Ogre::Vector3 gvelocity; bool mUpdate; + Ogre::Bullet::KinematicMotionSimple *mController; public: CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -174,4 +176,42 @@ public: { return mUpdate; } + Ogre::Vector3 getPosition() const + { + return mBodyNode->getPosition(); + } + Ogre::Quaternion getRotation() const + { + return mBodyNode->getOrientation(); + } + class ClosestNotMeRayResultCallback: public btCollisionWorld::ClosestRayResultCallback { + btCollisionObject *mMe; + public: + ClosestNotMeRayResultCallback( + btCollisionObject *me, + const btVector3 &from, + const btVector3 &to): + btCollisionWorld::ClosestRayResultCallback(from, to) { + mMe = me; + } + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace) + { + if (rayResult.m_collisionObject == mMe) + return 1.0f; + return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); + } + }; + bool checkGround() + { + btVector3 from = mGhostObject->getWorldTransform().getOrigin() + btVector3(0, 0.2f, 0); + btVector3 to = from + btVector3(0, -3000.0f, 0); + ClosestNotMeRayResultCallback + resultCallback(mGhostObject, from, to); + mGhostObject->rayTest(from, to, resultCallback); + // std::cout << (resultCallback.hasHit() ? "hit" : "no hit"); + // if (resultCallback.hasHit()) + // std::cout << " " << Ogre::Bullet::convert(resultCallback.m_hitPointWorld); + // std::cout << "\n"; + return resultCallback.hasHit(); + } }; diff --git a/src/terrain/terrain.h b/src/terrain/terrain.h index 4ad50ec..32cb946 100644 --- a/src/terrain/terrain.h +++ b/src/terrain/terrain.h @@ -404,5 +404,12 @@ public: return body; } #endif + bool isLoadedAt(const Ogre::Vector3 &position) const { + long x, y; + mTerrainGroup->convertWorldPositionToTerrainSlot(position, &x, &y); + if (mTerrainGroup->getTerrain(x, y)) + return mTerrainGroup->getTerrain(x, y)->isLoaded(); + return false; + } }; -#endif \ No newline at end of file +#endif