This commit is contained in:
2025-08-26 08:13:21 +03:00
parent 2349bbc77e
commit c730ca5222
4 changed files with 96 additions and 92 deletions

View File

@@ -244,7 +244,7 @@ public:
float panel_width;
void initGui();
};
#undef WATER
#define WATER
class App : public OgreBites::ApplicationContext {
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> 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)

View File

@@ -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<btCollisionObject *>());
#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<EntityCollisionListener*>(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)

View File

@@ -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();
}
};

View File

@@ -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
#endif