diff --git a/Demos/CharacterDemo/CharacterControllerInterface.h b/Demos/CharacterDemo/CharacterControllerInterface.h index f56aa0952..5c5ac8bab 100644 --- a/Demos/CharacterDemo/CharacterControllerInterface.h +++ b/Demos/CharacterDemo/CharacterControllerInterface.h @@ -15,14 +15,18 @@ public: virtual void setup (btDynamicsWorld* dynamicsWorld, btScalar height = 2.0, btScalar width = 0.25, btScalar stepHeight = 0.25) = 0; virtual void destroy (btDynamicsWorld* dynamicsWorld) = 0; - virtual btRigidBody* getRigidBody () = 0; + virtual btCollisionObject* getCollisionObject () = 0; + + virtual void reset () = 0; + virtual void warp (const btVector3& origin) = 0; virtual void preStep (btDynamicsWorld* dynamicsWorld) = 0; virtual void playerStep (btDynamicsWorld* dynamicsWorld, btScalar dt, int forward, int backward, int left, - int right) = 0; + int right, + int jump) = 0; virtual bool canJump () const = 0; virtual void jump () = 0; diff --git a/Demos/CharacterDemo/CharacterDemo.cpp b/Demos/CharacterDemo/CharacterDemo.cpp index f611d7b50..7bdc41251 100644 --- a/Demos/CharacterDemo/CharacterDemo.cpp +++ b/Demos/CharacterDemo/CharacterDemo.cpp @@ -159,6 +159,15 @@ class MyCustomOverlappingPairCallback : public btOverlappingPairCallback btHashedOverlappingPairCache* m_hashPairCache; + struct customOverlapFilterCallback : public btOverlapFilterCallback + { + bool needBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const + { + // we already know that the character proxy is either proxy0 or proxy1 + return true; + } + } myCustomOverlapFilterCallback; + public: MyCustomOverlappingPairCallback(CharacterDemo* demo,btCollisionObject* characterCollider) @@ -166,6 +175,7 @@ public: m_characterCollider(characterCollider) { m_hashPairCache = new btHashedOverlappingPairCache(); + m_hashPairCache->setOverlapFilterCallback (&myCustomOverlapFilterCallback); } virtual ~MyCustomOverlappingPairCallback() @@ -177,7 +187,7 @@ public: { if (proxy0->m_clientObject==m_characterCollider || proxy1->m_clientObject==m_characterCollider) { - printf("addOverlappingPair (%x,%x)\n",proxy0,proxy1); + //printf("addOverlappingPair (%p,%p)\n",proxy0,proxy1); return m_hashPairCache->addOverlappingPair(proxy0,proxy1); } return 0; @@ -187,7 +197,7 @@ public: { if (proxy0->m_clientObject==m_characterCollider || proxy1->m_clientObject==m_characterCollider) { - printf("removeOverlappingPair (%x,%x)\n",proxy0,proxy1); + //printf("removeOverlappingPair (%p,%p)\n",proxy0,proxy1); return m_hashPairCache->removeOverlappingPair(proxy0,proxy1,dispatcher); } return 0; @@ -197,7 +207,7 @@ public: { if (proxy0->m_clientObject==m_characterCollider) { - printf("removeOverlappingPairsContainingProxy (%x)\n",proxy0); + //printf("removeOverlappingPairsContainingProxy (%p)\n",proxy0); m_hashPairCache->removeOverlappingPairsContainingProxy(proxy0,dispatcher); } } @@ -469,12 +479,9 @@ const float TRIANGLE_SIZE=20.f; #endif m_character->setup (m_dynamicsWorld); - //we need to remove the rigid body from the broadphase in order to register all collisions - m_dynamicsWorld->removeRigidBody(m_character->getRigidBody()); //some custom callback sample - m_customPairCallback = new MyCustomOverlappingPairCallback(this,m_character->getRigidBody()); + m_customPairCallback = new MyCustomOverlappingPairCallback(this,m_character->getCollisionObject()); sweepBP->setOverlappingPairUserCallback(m_customPairCallback); - m_dynamicsWorld->addRigidBody(m_character->getRigidBody()); m_character->registerPairCache (m_customPairCallback->getOverlappingPairCache()); clientResetScene(); @@ -502,14 +509,10 @@ void CharacterDemo::clientMoveAndDisplay() if (m_character) { m_character->preStep (m_dynamicsWorld); - m_character->playerStep (m_dynamicsWorld, dt, gForward, gBackward, gLeft, gRight); - if (gJump) - { - gJump = 0; - m_character->jump (); - } + m_character->playerStep (m_dynamicsWorld, dt, gForward, gBackward, gLeft, gRight, gJump); } +#if 0 printf("numPairs = %d\n",m_customPairCallback->getOverlappingPairArray().size()); { btManifoldArray manifoldArray; @@ -520,6 +523,9 @@ void CharacterDemo::clientMoveAndDisplay() const btBroadphasePair& pair = m_customPairCallback->getOverlappingPairArray()[i]; btBroadphasePair* collisionPair = m_overlappingPairCache->getOverlappingPairCache()->findPair(pair.m_pProxy0,pair.m_pProxy1); + if (!collisionPair) + continue; + if (collisionPair->m_algorithm) collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); @@ -535,6 +541,7 @@ void CharacterDemo::clientMoveAndDisplay() } } } +#endif @@ -610,20 +617,12 @@ void CharacterDemo::displayCallback(void) glutSwapBuffers(); } - - void CharacterDemo::clientResetScene() { - m_dynamicsWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(m_character->getRigidBody()->getBroadphaseHandle(),getDynamicsWorld()->getDispatcher()); + m_dynamicsWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(m_character->getCollisionObject()->getBroadphaseHandle(),getDynamicsWorld()->getDispatcher()); - btTransform startTransform; - startTransform.setIdentity (); - startTransform.setOrigin (btVector3(0.0, 2.0, 0.0)); - - m_character->getRigidBody()->getMotionState()->setWorldTransform(startTransform); - m_character->getRigidBody()->setLinearVelocity(btVector3(0,0,0)); - m_character->getRigidBody()->setAngularVelocity(btVector3(0,0,0)); - + m_character->reset (); + m_character->warp (btVector3(0.0, 1.75, 0.0)); } void CharacterDemo::specialKeyboardUp(int key, int x, int y) @@ -715,14 +714,14 @@ void CharacterDemo::updateCamera() btTransform characterWorldTrans; //look at the vehicle - m_character->getRigidBody()->getMotionState()->getWorldTransform(characterWorldTrans); + characterWorldTrans = m_character->getCollisionObject()->getWorldTransform(); btVector3 up = characterWorldTrans.getBasis()[1]; btVector3 backward = -characterWorldTrans.getBasis()[2]; up.normalize (); backward.normalize (); m_cameraTargetPosition = characterWorldTrans.getOrigin(); - m_cameraPosition = m_cameraTargetPosition + up * 5.0 + backward * 5.0; + m_cameraPosition = m_cameraTargetPosition + up * 2.0 + backward * 2.0; //update OpenGL camera settings glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10000.0); diff --git a/Demos/CharacterDemo/DynamicCharacterController.cpp b/Demos/CharacterDemo/DynamicCharacterController.cpp index cddb2c416..ad00e8232 100644 --- a/Demos/CharacterDemo/DynamicCharacterController.cpp +++ b/Demos/CharacterDemo/DynamicCharacterController.cpp @@ -62,7 +62,7 @@ void DynamicCharacterController::destroy (btDynamicsWorld* dynamicsWorld) } } -btRigidBody* DynamicCharacterController::getRigidBody () +btCollisionObject* DynamicCharacterController::getCollisionObject () { return m_rigidBody; } @@ -122,7 +122,8 @@ void DynamicCharacterController::playerStep (btScalar dt, int forward, int backward, int left, - int right) + int right, + int jump) { btTransform xform; m_rigidBody->getMotionState()->getWorldTransform (xform); diff --git a/Demos/CharacterDemo/DynamicCharacterController.h b/Demos/CharacterDemo/DynamicCharacterController.h index 0933317fa..bdca1032d 100644 --- a/Demos/CharacterDemo/DynamicCharacterController.h +++ b/Demos/CharacterDemo/DynamicCharacterController.h @@ -32,14 +32,15 @@ public: void setup (btDynamicsWorld* dynamicsWorld, btScalar height = 2.0, btScalar width = 0.25, btScalar stepHeight = 0.25); void destroy (btDynamicsWorld* dynamicsWorld); - btRigidBody* getRigidBody (); + btCollisionObject* getCollisionObject (); void preStep (btDynamicsWorld* dynamicsWorld); void playerStep (btScalar dt, int forward, int backward, int left, - int right); + int right, + int jump); bool canJump () const; void jump (); diff --git a/Demos/CharacterDemo/KinematicCharacterController.cpp b/Demos/CharacterDemo/KinematicCharacterController.cpp index e80aa2187..4216db61d 100644 --- a/Demos/CharacterDemo/KinematicCharacterController.cpp +++ b/Demos/CharacterDemo/KinematicCharacterController.cpp @@ -11,16 +11,18 @@ #include "KinematicCharacterController.h" /* TODO: - * Handle projecting/slide along surfaces - * Deal with starting in penetration + * Fix jitter * Interact with dynamic objects * Ride kinematicly animated platforms properly - * Step climbing + * More realistic (or maybe just a config option) falling + * -> Should integrate falling velocity manually and use that in stepDown() + * Support jumping + * Support ducking */ class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: - ClosestNotMeRayResultCallback (btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + ClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) { m_me = me; } @@ -33,13 +35,13 @@ public: return ClosestRayResultCallback::AddSingleResult (rayResult, normalInWorldSpace); } protected: - btRigidBody* m_me; + btCollisionObject* m_me; }; class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: - ClosestNotMeConvexResultCallback (btRigidBody* me) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + ClosestNotMeConvexResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) { m_me = me; } @@ -52,7 +54,7 @@ public: return ClosestConvexResultCallback::AddSingleResult (convexResult, normalInWorldSpace); } protected: - btRigidBody* m_me; + btCollisionObject* m_me; }; /* @@ -88,7 +90,7 @@ KinematicCharacterController::KinematicCharacterController () m_walkVelocity = btScalar(1.1) * 4.0; // 4 km/h -> 1.1 m/s m_shape = NULL; m_pairCache = NULL; - m_rigidBody = NULL; + m_collisionObject = NULL; } KinematicCharacterController::~KinematicCharacterController () @@ -116,19 +118,18 @@ void KinematicCharacterController::setup (btDynamicsWorld* dynamicsWorld, btScal startTransform.setOrigin (btVector3(0.0, 4.0, 0.0)); btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform); btRigidBody::btRigidBodyConstructionInfo cInfo(1.0, myMotionState, m_shape); - m_rigidBody = new btRigidBody(cInfo); - m_rigidBody->setCollisionFlags( m_rigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE); - m_rigidBody->setSleepingThresholds (0.0, 0.0); - m_rigidBody->setAngularFactor (0.0); - dynamicsWorld->addRigidBody (m_rigidBody); + m_collisionObject = new btCollisionObject (); + m_collisionObject->setCollisionShape (m_shape); + m_collisionObject->setCollisionFlags (btCollisionObject::CF_KINEMATIC_OBJECT); + dynamicsWorld->addCollisionObject (m_collisionObject); } void KinematicCharacterController::destroy (btDynamicsWorld* dynamicsWorld) { - if (m_rigidBody) + if (m_collisionObject) { - dynamicsWorld->removeRigidBody (m_rigidBody); - delete m_rigidBody; + dynamicsWorld->removeCollisionObject (m_collisionObject); + delete m_collisionObject; } if (m_shape) @@ -137,23 +138,24 @@ void KinematicCharacterController::destroy (btDynamicsWorld* dynamicsWorld) } } -btRigidBody* KinematicCharacterController::getRigidBody () +btCollisionObject* KinematicCharacterController::getCollisionObject () { - return m_rigidBody; + return m_collisionObject; } -void KinematicCharacterController::recoverFromPenetration (btDynamicsWorld* dynamicsWorld) +bool KinematicCharacterController::recoverFromPenetration (btDynamicsWorld* dynamicsWorld) { if (m_pairCache == NULL) - return; + return false; - printf("%d\n", m_pairCache->getNumOverlappingPairs()); + bool penetration = false; + dynamicsWorld->getDispatcher()->dispatchAllCollisionPairs (m_pairCache, dynamicsWorld->getDispatchInfo(), dynamicsWorld->getDispatcher()); btManifoldArray manifoldArray; + btScalar maxPen = btScalar(0.0); for (int i = 0; i < m_pairCache->getNumOverlappingPairs(); i++) { - printf("%d\n",i); manifoldArray.clear(); btBroadphasePair* collisionPair = &m_pairCache->getOverlappingPairArray()[i]; @@ -164,19 +166,28 @@ void KinematicCharacterController::recoverFromPenetration (btDynamicsWorld* dyna for (int j=0;jgetBody0() == m_collisionObject ? btScalar(-1.0) : btScalar(1.0); for (int p=0;pgetNumContacts();p++) { const btManifoldPoint&pt = manifold->getContactPoint(p); if (pt.getDistance() < 0.0) { - printf("penetration %f\n", pt.getDistance()); + if (pt.getDistance() < maxPen) + { + maxPen = pt.getDistance(); + m_touchingNormal = pt.m_normalWorldOnB * directionSign; + } + m_currentPosition += pt.m_normalWorldOnB * directionSign * pt.getDistance() * btScalar(0.2); + penetration = true; } else { - printf("touching %f\n", pt.getDistance()); + //printf("touching %f\n", pt.getDistance()); } } + manifold->clearManifold(); } } + return penetration; } void KinematicCharacterController::stepUp (btDynamicsWorld* dynamicsWorld) @@ -192,7 +203,7 @@ void KinematicCharacterController::stepUp (btDynamicsWorld* dynamicsWorld) start.setOrigin (m_currentPosition + btVector3(btScalar(0.0), btScalar(0.1), btScalar(0.0))); end.setOrigin (m_targetPosition); - ClosestNotMeConvexResultCallback callback (m_rigidBody); + ClosestNotMeConvexResultCallback callback (m_collisionObject); dynamicsWorld->convexSweepTest (m_shape, start, end, callback); @@ -235,6 +246,7 @@ void KinematicCharacterController::updateTargetPositionBasedOnCollision (const b void KinematicCharacterController::stepForwardAndStrafe (btDynamicsWorld* dynamicsWorld, const btVector3& walkMove) { + btVector3 originalDir = walkMove.normalized(); // phase 2: forward and strafe btTransform start, end; m_targetPosition = m_currentPosition + walkMove; @@ -244,12 +256,18 @@ void KinematicCharacterController::stepForwardAndStrafe (btDynamicsWorld* dynami btScalar fraction = 1.0; btScalar distance2 = (m_currentPosition-m_targetPosition).length2(); + if (m_touchingContact) + { + if (originalDir.dot(m_touchingNormal) > btScalar(0.0)) + updateTargetPositionBasedOnCollision (m_touchingNormal); + } + while (fraction > btScalar(0.01)) { start.setOrigin (m_currentPosition); end.setOrigin (m_targetPosition); - ClosestNotMeConvexResultCallback callback (m_rigidBody); + ClosestNotMeConvexResultCallback callback (m_collisionObject); dynamicsWorld->convexSweepTest (m_shape, start, end, callback); fraction -= callback.m_closestHitFraction; @@ -257,9 +275,23 @@ void KinematicCharacterController::stepForwardAndStrafe (btDynamicsWorld* dynami if (callback.HasHit()) { // we moved only a fraction - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + btScalar hitDistance = (callback.m_hitPointWorld - m_currentPosition).length(); + + /* If the distance is farther than the collision margin, move */ + if (hitDistance > 0.05) + { + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + } + updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld); - distance2 = (m_currentPosition-m_targetPosition).length2(); + btVector3 currentDir = m_targetPosition - m_currentPosition; + distance2 = currentDir.length2(); + currentDir.normalize(); + /* Ageia's C.C. took this test from Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */ + if (currentDir.dot(originalDir) <= btScalar(0.0)) + { + break; + } } else { // we moved whole way m_currentPosition = m_targetPosition; @@ -282,7 +314,7 @@ void KinematicCharacterController::stepDown (btDynamicsWorld* dynamicsWorld, btS start.setOrigin (m_currentPosition); end.setOrigin (m_targetPosition); - ClosestNotMeConvexResultCallback callback (m_rigidBody); + ClosestNotMeConvexResultCallback callback (m_collisionObject); dynamicsWorld->convexSweepTest (m_shape, start, end, callback); @@ -297,6 +329,18 @@ void KinematicCharacterController::stepDown (btDynamicsWorld* dynamicsWorld, btS } } +void KinematicCharacterController::reset () +{ +} + +void KinematicCharacterController::warp (const btVector3& origin) +{ + btTransform xform; + xform.setIdentity(); + xform.setOrigin (origin); + m_collisionObject->setWorldTransform (xform); +} + void KinematicCharacterController::registerPairCache (btOverlappingPairCache* pairCache) { m_pairCache = pairCache; @@ -304,8 +348,20 @@ void KinematicCharacterController::registerPairCache (btOverlappingPairCache* pa void KinematicCharacterController::preStep (btDynamicsWorld* dynamicsWorld) { + int numPenetrationLoops = 0; + m_touchingContact = false; + while (recoverFromPenetration (dynamicsWorld)) + { + numPenetrationLoops++; + m_touchingContact = true; + if (numPenetrationLoops > 4) + { + printf("character could not recover from penetration = %d\n", numPenetrationLoops); + break; + } + } btTransform xform; - m_rigidBody->getMotionState()->getWorldTransform (xform); + xform = m_collisionObject->getWorldTransform (); btVector3 forwardDir = xform.getBasis()[2]; btVector3 upDir = xform.getBasis()[1]; @@ -320,7 +376,8 @@ void KinematicCharacterController::preStep (btDynamicsWorld* dynamicsWorld) m_currentPosition = xform.getOrigin(); m_targetPosition = m_currentPosition; - recoverFromPenetration (dynamicsWorld); + + } void KinematicCharacterController::playerStep (btDynamicsWorld* dynamicsWorld, @@ -328,7 +385,8 @@ void KinematicCharacterController::playerStep (btDynamicsWorld* dynamicsWorld, int forward, int backward, int left, - int right) + int right, + int jump) { btVector3 walkDirection = btVector3(0.0, 0.0, 0.0); btScalar walkSpeed = m_walkVelocity * dt; @@ -346,15 +404,29 @@ void KinematicCharacterController::playerStep (btDynamicsWorld* dynamicsWorld, walkDirection -= m_forwardDirection; btTransform xform; - m_rigidBody->getMotionState()->getWorldTransform (xform); + xform = m_collisionObject->getWorldTransform (); stepUp (dynamicsWorld); stepForwardAndStrafe (dynamicsWorld, walkDirection * walkSpeed); stepDown (dynamicsWorld, dt); xform.setOrigin (m_currentPosition); - m_rigidBody->getMotionState()->setWorldTransform (xform); - m_rigidBody->setCenterOfMassTransform (xform); + m_collisionObject->setWorldTransform (xform); +} + +void KinematicCharacterController::setFallSpeed (btScalar fallSpeed) +{ + m_fallSpeed = fallSpeed; +} + +void KinematicCharacterController::setJumpSpeed (btScalar jumpSpeed) +{ + m_jumpSpeed = jumpSpeed; +} + +void KinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight) +{ + m_maxJumpHeight = maxJumpHeight; } bool KinematicCharacterController::canJump () const diff --git a/Demos/CharacterDemo/KinematicCharacterController.h b/Demos/CharacterDemo/KinematicCharacterController.h index f3a658013..bebf2ac49 100644 --- a/Demos/CharacterDemo/KinematicCharacterController.h +++ b/Demos/CharacterDemo/KinematicCharacterController.h @@ -14,9 +14,13 @@ class KinematicCharacterController : public CharacterControllerInterface protected: btScalar m_halfHeight; btConvexShape* m_shape; - btRigidBody* m_rigidBody; + btCollisionObject* m_collisionObject; btOverlappingPairCache* m_pairCache; + btScalar m_fallSpeed; + btScalar m_jumpSpeed; + btScalar m_maxJumpHeight; + btScalar m_turnAngle; btScalar m_walkVelocity; @@ -31,8 +35,11 @@ protected: btVector3 m_currentPosition; btScalar m_currentStepOffset; btVector3 m_targetPosition; + + bool m_touchingContact; + btVector3 m_touchingNormal; - void recoverFromPenetration (btDynamicsWorld* dynamicsWorld); + bool recoverFromPenetration (btDynamicsWorld* dynamicsWorld); void stepUp (btDynamicsWorld* dynamicsWorld); void updateTargetPositionBasedOnCollision (const btVector3& hit_normal, btScalar tangentMag = btScalar(1.0), btScalar normalMag = btScalar(0.0)); void stepForwardAndStrafe (btDynamicsWorld* dynamicsWorld, const btVector3& walkMove); @@ -43,7 +50,10 @@ public: void setup (btDynamicsWorld* dynamicsWorld, btScalar height = btScalar(1.75), btScalar width = btScalar(0.4), btScalar stepHeight = btScalar(0.35)); void destroy (btDynamicsWorld* dynamicsWorld); - btRigidBody* getRigidBody (); + btCollisionObject* getCollisionObject (); + + void reset (); + void warp (const btVector3& origin); void registerPairCache (btOverlappingPairCache* pairCache); void preStep (btDynamicsWorld* dynamicsWorld); @@ -51,7 +61,12 @@ public: int forward, int backward, int left, - int right); + int right, + int jump); + + void setFallSpeed (btScalar fallSpeed); + void setJumpSpeed (btScalar jumpSpeed); + void setMaxJumpHeight (btScalar maxJumpHeight); bool canJump () const; void jump ();