btKinematicCharacterController.cpp

Go to the documentation of this file.
00001 /*
00002 Bullet Continuous Collision Detection and Physics Library
00003 Copyright (c) 2003-2008 Erwin Coumans  http://bulletphysics.com
00004 
00005 This software is provided 'as-is', without any express or implied warranty.
00006 In no event will the authors be held liable for any damages arising from the use of this software.
00007 Permission is granted to anyone to use this software for any purpose, 
00008 including commercial applications, and to alter it and redistribute it freely, 
00009 subject to the following restrictions:
00010 
00011 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
00012 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
00013 3. This notice may not be removed or altered from any source distribution.
00014 */
00015 
00016 
00017 #include "LinearMath/btIDebugDraw.h"
00018 #include "BulletCollision/CollisionDispatch/btGhostObject.h"
00019 #include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
00020 #include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
00021 #include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
00022 #include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
00023 #include "LinearMath/btDefaultMotionState.h"
00024 #include "btKinematicCharacterController.h"
00025 
00026 
00027 // static helper method
00028 static btVector3
00029 getNormalizedVector(const btVector3& v)
00030 {
00031         btVector3 n = v.normalized();
00032         if (n.length() < SIMD_EPSILON) {
00033                 n.setValue(0, 0, 0);
00034         }
00035         return n;
00036 }
00037 
00038 
00045 class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
00046 {
00047 public:
00048         btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
00049         {
00050                 m_me = me;
00051         }
00052 
00053         virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
00054         {
00055                 if (rayResult.m_collisionObject == m_me)
00056                         return 1.0;
00057 
00058                 return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
00059         }
00060 protected:
00061         btCollisionObject* m_me;
00062 };
00063 
00064 class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
00065 {
00066 public:
00067         btKinematicClosestNotMeConvexResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
00068         {
00069                 m_me = me;
00070         }
00071 
00072         virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
00073         {
00074                 if (convexResult.m_hitCollisionObject == m_me)
00075                         return 1.0;
00076 
00077                 return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
00078         }
00079 protected:
00080         btCollisionObject* m_me;
00081 };
00082 
00083 /*
00084  * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
00085  *
00086  * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
00087  */
00088 btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal)
00089 {
00090         return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
00091 }
00092 
00093 /*
00094  * Returns the portion of 'direction' that is parallel to 'normal'
00095  */
00096 btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal)
00097 {
00098         btScalar magnitude = direction.dot(normal);
00099         return normal * magnitude;
00100 }
00101 
00102 /*
00103  * Returns the portion of 'direction' that is perpindicular to 'normal'
00104  */
00105 btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal)
00106 {
00107         return direction - parallelComponent(direction, normal);
00108 }
00109 
00110 btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis)
00111 {
00112         m_upAxis = upAxis;
00113         m_addedMargin = 0.02f;
00114         m_walkDirection.setValue(0,0,0);
00115         m_useGhostObjectSweepTest = true;
00116         m_ghostObject = ghostObject;
00117         m_stepHeight = stepHeight;
00118         m_turnAngle = btScalar(0.0);
00119         m_convexShape=convexShape;      
00120         m_useWalkDirection = true;      // use walk direction by default, legacy behavior
00121         m_velocityTimeInterval = 0.0;
00122 }
00123 
00124 btKinematicCharacterController::~btKinematicCharacterController ()
00125 {
00126 }
00127 
00128 btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
00129 {
00130         return m_ghostObject;
00131 }
00132 
00133 bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld)
00134 {
00135 
00136         bool penetration = false;
00137 
00138         collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
00139 
00140         m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
00141         
00142         btScalar maxPen = btScalar(0.0);
00143         for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
00144         {
00145                 m_manifoldArray.resize(0);
00146 
00147                 btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
00148                 
00149                 if (collisionPair->m_algorithm)
00150                         collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
00151 
00152                 
00153                 for (int j=0;j<m_manifoldArray.size();j++)
00154                 {
00155                         btPersistentManifold* manifold = m_manifoldArray[j];
00156                         btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
00157                         for (int p=0;p<manifold->getNumContacts();p++)
00158                         {
00159                                 const btManifoldPoint&pt = manifold->getContactPoint(p);
00160 
00161                                 if (pt.getDistance() < 0.0)
00162                                 {
00163                                         if (pt.getDistance() < maxPen)
00164                                         {
00165                                                 maxPen = pt.getDistance();
00166                                                 m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
00167 
00168                                         }
00169                                         m_currentPosition += pt.m_normalWorldOnB * directionSign * pt.getDistance() * btScalar(0.2);
00170                                         penetration = true;
00171                                 } else {
00172                                         //printf("touching %f\n", pt.getDistance());
00173                                 }
00174                         }
00175                         
00176                         //manifold->clearManifold();
00177                 }
00178         }
00179         btTransform newTrans = m_ghostObject->getWorldTransform();
00180         newTrans.setOrigin(m_currentPosition);
00181         m_ghostObject->setWorldTransform(newTrans);
00182 //      printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
00183         return penetration;
00184 }
00185 
00186 void btKinematicCharacterController::stepUp ( btCollisionWorld* world)
00187 {
00188         // phase 1: up
00189         btTransform start, end;
00190         m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * m_stepHeight;
00191 
00192         start.setIdentity ();
00193         end.setIdentity ();
00194 
00195         /* FIXME: Handle penetration properly */
00196         start.setOrigin (m_currentPosition + getUpAxisDirections()[m_upAxis] * btScalar(0.1f));
00197         end.setOrigin (m_targetPosition);
00198 
00199         btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject);
00200         callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
00201         callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
00202         
00203         if (m_useGhostObjectSweepTest)
00204         {
00205                 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
00206         }
00207         else
00208         {
00209                 world->convexSweepTest (m_convexShape, start, end, callback);
00210         }
00211         
00212         if (callback.hasHit())
00213         {
00214                 // we moved up only a fraction of the step height
00215                 m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
00216                 m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
00217         } else {
00218                 m_currentStepOffset = m_stepHeight;
00219                 m_currentPosition = m_targetPosition;
00220         }
00221 }
00222 
00223 void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
00224 {
00225         btVector3 movementDirection = m_targetPosition - m_currentPosition;
00226         btScalar movementLength = movementDirection.length();
00227         if (movementLength>SIMD_EPSILON)
00228         {
00229                 movementDirection.normalize();
00230 
00231                 btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
00232                 reflectDir.normalize();
00233 
00234                 btVector3 parallelDir, perpindicularDir;
00235 
00236                 parallelDir = parallelComponent (reflectDir, hitNormal);
00237                 perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
00238 
00239                 m_targetPosition = m_currentPosition;
00240                 if (0)//tangentMag != 0.0)
00241                 {
00242                         btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
00243 //                      printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
00244                         m_targetPosition +=  parComponent;
00245                 }
00246 
00247                 if (normalMag != 0.0)
00248                 {
00249                         btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
00250 //                      printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
00251                         m_targetPosition += perpComponent;
00252                 }
00253         } else
00254         {
00255 //              printf("movementLength don't normalize a zero vector\n");
00256         }
00257 }
00258 
00259 void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove)
00260 {
00261         // printf("m_normalizedDirection=%f,%f,%f\n",
00262         //      m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
00263         // phase 2: forward and strafe
00264         btTransform start, end;
00265         m_targetPosition = m_currentPosition + walkMove;
00266         start.setIdentity ();
00267         end.setIdentity ();
00268         
00269         btScalar fraction = 1.0;
00270         btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
00271 //      printf("distance2=%f\n",distance2);
00272 
00273         if (m_touchingContact)
00274         {
00275                 if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0))
00276                         updateTargetPositionBasedOnCollision (m_touchingNormal);
00277         }
00278 
00279         int maxIter = 10;
00280 
00281         while (fraction > btScalar(0.01) && maxIter-- > 0)
00282         {
00283                 start.setOrigin (m_currentPosition);
00284                 end.setOrigin (m_targetPosition);
00285 
00286                 btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject);
00287                 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
00288                 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
00289 
00290 
00291                 btScalar margin = m_convexShape->getMargin();
00292                 m_convexShape->setMargin(margin + m_addedMargin);
00293 
00294 
00295                 if (m_useGhostObjectSweepTest)
00296                 {
00297                         m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00298                 } else
00299                 {
00300                         collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00301                 }
00302                 
00303                 m_convexShape->setMargin(margin);
00304 
00305                 
00306                 fraction -= callback.m_closestHitFraction;
00307 
00308                 if (callback.hasHit())
00309                 {       
00310                         // we moved only a fraction
00311                         btScalar hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
00312                         if (hitDistance<0.f)
00313                         {
00314 //                              printf("neg dist?\n");
00315                         }
00316 
00317                         /* If the distance is farther than the collision margin, move */
00318                         if (hitDistance > m_addedMargin)
00319                         {
00320 //                              printf("callback.m_closestHitFraction=%f\n",callback.m_closestHitFraction);
00321                                 m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
00322                         }
00323 
00324                         updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
00325                         btVector3 currentDir = m_targetPosition - m_currentPosition;
00326                         distance2 = currentDir.length2();
00327                         if (distance2 > SIMD_EPSILON)
00328                         {
00329                                 currentDir.normalize();
00330                                 /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
00331                                 if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
00332                                 {
00333                                         break;
00334                                 }
00335                         } else
00336                         {
00337 //                              printf("currentDir: don't normalize a zero vector\n");
00338                                 break;
00339                         }
00340                 } else {
00341                         // we moved whole way
00342                         m_currentPosition = m_targetPosition;
00343                 }
00344 
00345         //      if (callback.m_closestHitFraction == 0.f)
00346         //              break;
00347 
00348         }
00349 }
00350 
00351 void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt)
00352 {
00353         btTransform start, end;
00354 
00355         // phase 3: down
00356         btVector3 step_drop = getUpAxisDirections()[m_upAxis] * m_currentStepOffset;
00357         btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * m_stepHeight; 
00358         m_targetPosition -= (step_drop + gravity_drop);
00359 
00360         start.setIdentity ();
00361         end.setIdentity ();
00362 
00363         start.setOrigin (m_currentPosition);
00364         end.setOrigin (m_targetPosition);
00365 
00366         btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject);
00367         callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
00368         callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
00369         
00370         if (m_useGhostObjectSweepTest)
00371         {
00372                 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00373         } else
00374         {
00375                 collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00376         }
00377 
00378         if (callback.hasHit())
00379         {
00380                 // we dropped a fraction of the height -> hit floor
00381                 m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
00382         } else {
00383                 // we dropped the full height
00384                 
00385                 m_currentPosition = m_targetPosition;
00386         }
00387 }
00388 
00389 
00390 
00391 void btKinematicCharacterController::setWalkDirection
00392 (
00393 const btVector3& walkDirection
00394 )
00395 {
00396         m_useWalkDirection = true;
00397         m_walkDirection = walkDirection;
00398         m_normalizedDirection = getNormalizedVector(m_walkDirection);
00399 }
00400 
00401 
00402 
00403 void btKinematicCharacterController::setVelocityForTimeInterval
00404 (
00405 const btVector3& velocity,
00406 btScalar timeInterval
00407 )
00408 {
00409 //      printf("setVelocity!\n");
00410 //      printf("  interval: %f\n", timeInterval);
00411 //      printf("  velocity: (%f, %f, %f)\n",
00412 //          velocity.x(), velocity.y(), velocity.z());
00413 
00414         m_useWalkDirection = false;
00415         m_walkDirection = velocity;
00416         m_normalizedDirection = getNormalizedVector(m_walkDirection);
00417         m_velocityTimeInterval = timeInterval;
00418 }
00419 
00420 
00421 
00422 void btKinematicCharacterController::reset ()
00423 {
00424 }
00425 
00426 void btKinematicCharacterController::warp (const btVector3& origin)
00427 {
00428         btTransform xform;
00429         xform.setIdentity();
00430         xform.setOrigin (origin);
00431         m_ghostObject->setWorldTransform (xform);
00432 }
00433 
00434 
00435 void btKinematicCharacterController::preStep (  btCollisionWorld* collisionWorld)
00436 {
00437         
00438         int numPenetrationLoops = 0;
00439         m_touchingContact = false;
00440         while (recoverFromPenetration (collisionWorld))
00441         {
00442                 numPenetrationLoops++;
00443                 m_touchingContact = true;
00444                 if (numPenetrationLoops > 4)
00445                 {
00446 //                      printf("character could not recover from penetration = %d\n", numPenetrationLoops);
00447                         break;
00448                 }
00449         }
00450 
00451         m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
00452         m_targetPosition = m_currentPosition;
00453 //      printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
00454 
00455         
00456 }
00457 
00458 void btKinematicCharacterController::playerStep (  btCollisionWorld* collisionWorld, btScalar dt)
00459 {
00460 //      printf("playerStep(): ");
00461 //      printf("  dt = %f", dt);
00462 
00463         // quick check...
00464         if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) {
00465 //              printf("\n");
00466                 return;         // no motion
00467         }
00468 
00469         btTransform xform;
00470         xform = m_ghostObject->getWorldTransform ();
00471 
00472 //      printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
00473 //      printf("walkSpeed=%f\n",walkSpeed);
00474 
00475         stepUp (collisionWorld);
00476         if (m_useWalkDirection) {
00477                 stepForwardAndStrafe (collisionWorld, m_walkDirection);
00478         } else {
00479                 //printf("  time: %f", m_velocityTimeInterval);
00480                 // still have some time left for moving!
00481                 btScalar dtMoving =
00482                    (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval;
00483                 m_velocityTimeInterval -= dt;
00484 
00485                 // how far will we move while we are moving?
00486                 btVector3 move = m_walkDirection * dtMoving;
00487 
00488                 // printf("  dtMoving: %f", dtMoving);
00489 
00490                 // okay, step
00491                 stepForwardAndStrafe(collisionWorld, move);
00492         }
00493         stepDown (collisionWorld, dt);
00494 
00495         // printf("\n");
00496 
00497         xform.setOrigin (m_currentPosition);
00498         m_ghostObject->setWorldTransform (xform);
00499 }
00500 
00501 void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed)
00502 {
00503         m_fallSpeed = fallSpeed;
00504 }
00505 
00506 void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed)
00507 {
00508         m_jumpSpeed = jumpSpeed;
00509 }
00510 
00511 void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight)
00512 {
00513         m_maxJumpHeight = maxJumpHeight;
00514 }
00515 
00516 bool btKinematicCharacterController::canJump () const
00517 {
00518         return onGround();
00519 }
00520 
00521 void btKinematicCharacterController::jump ()
00522 {
00523         if (!canJump())
00524                 return;
00525 
00526 #if 0
00527         currently no jumping.
00528         btTransform xform;
00529         m_rigidBody->getMotionState()->getWorldTransform (xform);
00530         btVector3 up = xform.getBasis()[1];
00531         up.normalize ();
00532         btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
00533         m_rigidBody->applyCentralImpulse (up * magnitude);
00534 #endif
00535 }
00536 
00537 bool btKinematicCharacterController::onGround () const
00538 {
00539         return true;
00540 }
00541 
00542 
00543 void    btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)
00544 {
00545 }
00546 
00547 
00548 btVector3* btKinematicCharacterController::getUpAxisDirections()
00549 {
00550         static btVector3 sUpAxisDirection[3] = { btVector3(1.0f, 0.0f, 0.0f), btVector3(0.0f, 1.0f, 0.0f), btVector3(0.0f, 0.0f, 1.0f) };
00551         
00552         return sUpAxisDirection;
00553 }

Generated on Mon Feb 15 22:17:05 2010 for Bullet Collision Detection & Physics Library by  doxygen 1.6.1