Blender V2.61 - r43446

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, const btVector3& up, btScalar minSlopeDot)
00068     : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
00069     , m_me(me)
00070     , m_up(up)
00071     , m_minSlopeDot(minSlopeDot)
00072     {
00073     }
00074 
00075     virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
00076     {
00077         if (convexResult.m_hitCollisionObject == m_me)
00078             return btScalar(1.0);
00079 
00080         btVector3 hitNormalWorld;
00081         if (normalInWorldSpace)
00082         {
00083             hitNormalWorld = convexResult.m_hitNormalLocal;
00084         } else
00085         {
00087             hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
00088         }
00089 
00090         btScalar dotUp = m_up.dot(hitNormalWorld);
00091         if (dotUp < m_minSlopeDot) {
00092             return btScalar(1.0);
00093         }
00094 
00095         return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
00096     }
00097 protected:
00098     btCollisionObject* m_me;
00099     const btVector3 m_up;
00100     btScalar m_minSlopeDot;
00101 };
00102 
00103 /*
00104  * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
00105  *
00106  * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
00107  */
00108 btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal)
00109 {
00110     return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
00111 }
00112 
00113 /*
00114  * Returns the portion of 'direction' that is parallel to 'normal'
00115  */
00116 btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal)
00117 {
00118     btScalar magnitude = direction.dot(normal);
00119     return normal * magnitude;
00120 }
00121 
00122 /*
00123  * Returns the portion of 'direction' that is perpindicular to 'normal'
00124  */
00125 btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal)
00126 {
00127     return direction - parallelComponent(direction, normal);
00128 }
00129 
00130 btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis)
00131 {
00132     m_upAxis = upAxis;
00133     m_addedMargin = 0.02;
00134     m_walkDirection.setValue(0,0,0);
00135     m_useGhostObjectSweepTest = true;
00136     m_ghostObject = ghostObject;
00137     m_stepHeight = stepHeight;
00138     m_turnAngle = btScalar(0.0);
00139     m_convexShape=convexShape;  
00140     m_useWalkDirection = true;  // use walk direction by default, legacy behavior
00141     m_velocityTimeInterval = 0.0;
00142     m_verticalVelocity = 0.0;
00143     m_verticalOffset = 0.0;
00144     m_gravity = 9.8 * 3 ; // 3G acceleration.
00145     m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s.
00146     m_jumpSpeed = 10.0; // ?
00147     m_wasOnGround = false;
00148     m_wasJumping = false;
00149     setMaxSlope(btRadians(45.0));
00150 }
00151 
00152 btKinematicCharacterController::~btKinematicCharacterController ()
00153 {
00154 }
00155 
00156 btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
00157 {
00158     return m_ghostObject;
00159 }
00160 
00161 bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld)
00162 {
00163 
00164     bool penetration = false;
00165 
00166     collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
00167 
00168     m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
00169     
00170     btScalar maxPen = btScalar(0.0);
00171     for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
00172     {
00173         m_manifoldArray.resize(0);
00174 
00175         btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
00176         
00177         if (collisionPair->m_algorithm)
00178             collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
00179 
00180         
00181         for (int j=0;j<m_manifoldArray.size();j++)
00182         {
00183             btPersistentManifold* manifold = m_manifoldArray[j];
00184             btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
00185             for (int p=0;p<manifold->getNumContacts();p++)
00186             {
00187                 const btManifoldPoint&pt = manifold->getContactPoint(p);
00188 
00189                 btScalar dist = pt.getDistance();
00190 
00191                 if (dist < 0.0)
00192                 {
00193                     if (dist < maxPen)
00194                     {
00195                         maxPen = dist;
00196                         m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
00197 
00198                     }
00199                     m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
00200                     penetration = true;
00201                 } else {
00202                     //printf("touching %f\n", dist);
00203                 }
00204             }
00205             
00206             //manifold->clearManifold();
00207         }
00208     }
00209     btTransform newTrans = m_ghostObject->getWorldTransform();
00210     newTrans.setOrigin(m_currentPosition);
00211     m_ghostObject->setWorldTransform(newTrans);
00212 //  printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
00213     return penetration;
00214 }
00215 
00216 void btKinematicCharacterController::stepUp ( btCollisionWorld* world)
00217 {
00218     // phase 1: up
00219     btTransform start, end;
00220     m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.f?m_verticalOffset:0.f));
00221 
00222     start.setIdentity ();
00223     end.setIdentity ();
00224 
00225     /* FIXME: Handle penetration properly */
00226     start.setOrigin (m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_convexShape->getMargin() + m_addedMargin));
00227     end.setOrigin (m_targetPosition);
00228 
00229     btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, -getUpAxisDirections()[m_upAxis], btScalar(0.7071));
00230     callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
00231     callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
00232     
00233     if (m_useGhostObjectSweepTest)
00234     {
00235         m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
00236     }
00237     else
00238     {
00239         world->convexSweepTest (m_convexShape, start, end, callback);
00240     }
00241     
00242     if (callback.hasHit())
00243     {
00244         // Only modify the position if the hit was a slope and not a wall or ceiling.
00245         if(callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > 0.0)
00246         {
00247             // we moved up only a fraction of the step height
00248             m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
00249             m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
00250         }
00251         m_verticalVelocity = 0.0;
00252         m_verticalOffset = 0.0;
00253     } else {
00254         m_currentStepOffset = m_stepHeight;
00255         m_currentPosition = m_targetPosition;
00256     }
00257 }
00258 
00259 void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
00260 {
00261     btVector3 movementDirection = m_targetPosition - m_currentPosition;
00262     btScalar movementLength = movementDirection.length();
00263     if (movementLength>SIMD_EPSILON)
00264     {
00265         movementDirection.normalize();
00266 
00267         btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
00268         reflectDir.normalize();
00269 
00270         btVector3 parallelDir, perpindicularDir;
00271 
00272         parallelDir = parallelComponent (reflectDir, hitNormal);
00273         perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
00274 
00275         m_targetPosition = m_currentPosition;
00276         if (0)//tangentMag != 0.0)
00277         {
00278             btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
00279 //          printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
00280             m_targetPosition +=  parComponent;
00281         }
00282 
00283         if (normalMag != 0.0)
00284         {
00285             btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
00286 //          printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
00287             m_targetPosition += perpComponent;
00288         }
00289     } else
00290     {
00291 //      printf("movementLength don't normalize a zero vector\n");
00292     }
00293 }
00294 
00295 void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove)
00296 {
00297     // printf("m_normalizedDirection=%f,%f,%f\n",
00298     //  m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
00299     // phase 2: forward and strafe
00300     btTransform start, end;
00301     m_targetPosition = m_currentPosition + walkMove;
00302 
00303     start.setIdentity ();
00304     end.setIdentity ();
00305     
00306     btScalar fraction = 1.0;
00307     btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
00308 //  printf("distance2=%f\n",distance2);
00309 
00310     if (m_touchingContact)
00311     {
00312         if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0))
00313         {
00314             updateTargetPositionBasedOnCollision (m_touchingNormal);
00315         }
00316     }
00317 
00318     int maxIter = 10;
00319 
00320     while (fraction > btScalar(0.01) && maxIter-- > 0)
00321     {
00322         start.setOrigin (m_currentPosition);
00323         end.setOrigin (m_targetPosition);
00324         btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
00325 
00326         btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0));
00327         callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
00328         callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
00329 
00330 
00331         btScalar margin = m_convexShape->getMargin();
00332         m_convexShape->setMargin(margin + m_addedMargin);
00333 
00334 
00335         if (m_useGhostObjectSweepTest)
00336         {
00337             m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00338         } else
00339         {
00340             collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00341         }
00342         
00343         m_convexShape->setMargin(margin);
00344 
00345         
00346         fraction -= callback.m_closestHitFraction;
00347 
00348         if (callback.hasHit())
00349         {   
00350             // we moved only a fraction
00351             btScalar hitDistance;
00352             hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
00353 
00354 //          m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
00355 
00356             updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
00357             btVector3 currentDir = m_targetPosition - m_currentPosition;
00358             distance2 = currentDir.length2();
00359             if (distance2 > SIMD_EPSILON)
00360             {
00361                 currentDir.normalize();
00362                 /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
00363                 if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
00364                 {
00365                     break;
00366                 }
00367             } else
00368             {
00369 //              printf("currentDir: don't normalize a zero vector\n");
00370                 break;
00371             }
00372 
00373         } else {
00374             // we moved whole way
00375             m_currentPosition = m_targetPosition;
00376         }
00377 
00378     //  if (callback.m_closestHitFraction == 0.f)
00379     //      break;
00380 
00381     }
00382 }
00383 
00384 void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt)
00385 {
00386     btTransform start, end;
00387 
00388     // phase 3: down
00389     /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
00390     btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + additionalDownStep);
00391     btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt;
00392     btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downVelocity; 
00393     m_targetPosition -= (step_drop + gravity_drop);*/
00394 
00395     btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
00396     if(downVelocity > 0.0 && downVelocity < m_stepHeight
00397         && (m_wasOnGround || !m_wasJumping))
00398     {
00399         downVelocity = m_stepHeight;
00400     }
00401 
00402     btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity);
00403     m_targetPosition -= step_drop;
00404 
00405     start.setIdentity ();
00406     end.setIdentity ();
00407 
00408     start.setOrigin (m_currentPosition);
00409     end.setOrigin (m_targetPosition);
00410 
00411     btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine);
00412     callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
00413     callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
00414     
00415     if (m_useGhostObjectSweepTest)
00416     {
00417         m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00418     } else
00419     {
00420         collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
00421     }
00422 
00423     if (callback.hasHit())
00424     {
00425         // we dropped a fraction of the height -> hit floor
00426         m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
00427         m_verticalVelocity = 0.0;
00428         m_verticalOffset = 0.0;
00429         m_wasJumping = false;
00430     } else {
00431         // we dropped the full height
00432         
00433         m_currentPosition = m_targetPosition;
00434     }
00435 }
00436 
00437 
00438 
00439 void btKinematicCharacterController::setWalkDirection
00440 (
00441 const btVector3& walkDirection
00442 )
00443 {
00444     m_useWalkDirection = true;
00445     m_walkDirection = walkDirection;
00446     m_normalizedDirection = getNormalizedVector(m_walkDirection);
00447 }
00448 
00449 
00450 
00451 void btKinematicCharacterController::setVelocityForTimeInterval
00452 (
00453 const btVector3& velocity,
00454 btScalar timeInterval
00455 )
00456 {
00457 //  printf("setVelocity!\n");
00458 //  printf("  interval: %f\n", timeInterval);
00459 //  printf("  velocity: (%f, %f, %f)\n",
00460 //       velocity.x(), velocity.y(), velocity.z());
00461 
00462     m_useWalkDirection = false;
00463     m_walkDirection = velocity;
00464     m_normalizedDirection = getNormalizedVector(m_walkDirection);
00465     m_velocityTimeInterval = timeInterval;
00466 }
00467 
00468 
00469 
00470 void btKinematicCharacterController::reset ()
00471 {
00472 }
00473 
00474 void btKinematicCharacterController::warp (const btVector3& origin)
00475 {
00476     btTransform xform;
00477     xform.setIdentity();
00478     xform.setOrigin (origin);
00479     m_ghostObject->setWorldTransform (xform);
00480 }
00481 
00482 
00483 void btKinematicCharacterController::preStep (  btCollisionWorld* collisionWorld)
00484 {
00485     
00486     int numPenetrationLoops = 0;
00487     m_touchingContact = false;
00488     while (recoverFromPenetration (collisionWorld))
00489     {
00490         numPenetrationLoops++;
00491         m_touchingContact = true;
00492         if (numPenetrationLoops > 4)
00493         {
00494             //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
00495             break;
00496         }
00497     }
00498 
00499     m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
00500     m_targetPosition = m_currentPosition;
00501 //  printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
00502 
00503     
00504 }
00505 
00506 #include <stdio.h>
00507 
00508 void btKinematicCharacterController::playerStep (  btCollisionWorld* collisionWorld, btScalar dt)
00509 {
00510 //  printf("playerStep(): ");
00511 //  printf("  dt = %f", dt);
00512 
00513     // quick check...
00514     if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) {
00515 //      printf("\n");
00516         return;     // no motion
00517     }
00518 
00519     m_wasOnGround = onGround();
00520 
00521     // Update fall velocity.
00522     m_verticalVelocity -= m_gravity * dt;
00523     if(m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
00524     {
00525         m_verticalVelocity = m_jumpSpeed;
00526     }
00527     if(m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
00528     {
00529         m_verticalVelocity = -btFabs(m_fallSpeed);
00530     }
00531     m_verticalOffset = m_verticalVelocity * dt;
00532 
00533 
00534     btTransform xform;
00535     xform = m_ghostObject->getWorldTransform ();
00536 
00537 //  printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
00538 //  printf("walkSpeed=%f\n",walkSpeed);
00539 
00540     stepUp (collisionWorld);
00541     if (m_useWalkDirection) {
00542         stepForwardAndStrafe (collisionWorld, m_walkDirection);
00543     } else {
00544         //printf("  time: %f", m_velocityTimeInterval);
00545         // still have some time left for moving!
00546         btScalar dtMoving =
00547             (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval;
00548         m_velocityTimeInterval -= dt;
00549 
00550         // how far will we move while we are moving?
00551         btVector3 move = m_walkDirection * dtMoving;
00552 
00553         //printf("  dtMoving: %f", dtMoving);
00554 
00555         // okay, step
00556         stepForwardAndStrafe(collisionWorld, move);
00557     }
00558     stepDown (collisionWorld, dt);
00559 
00560     // printf("\n");
00561 
00562     xform.setOrigin (m_currentPosition);
00563     m_ghostObject->setWorldTransform (xform);
00564 }
00565 
00566 void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed)
00567 {
00568     m_fallSpeed = fallSpeed;
00569 }
00570 
00571 void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed)
00572 {
00573     m_jumpSpeed = jumpSpeed;
00574 }
00575 
00576 void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight)
00577 {
00578     m_maxJumpHeight = maxJumpHeight;
00579 }
00580 
00581 bool btKinematicCharacterController::canJump () const
00582 {
00583     return onGround();
00584 }
00585 
00586 void btKinematicCharacterController::jump ()
00587 {
00588     if (!canJump())
00589         return;
00590 
00591     m_verticalVelocity = m_jumpSpeed;
00592     m_wasJumping = true;
00593 
00594 #if 0
00595     currently no jumping.
00596     btTransform xform;
00597     m_rigidBody->getMotionState()->getWorldTransform (xform);
00598     btVector3 up = xform.getBasis()[1];
00599     up.normalize ();
00600     btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
00601     m_rigidBody->applyCentralImpulse (up * magnitude);
00602 #endif
00603 }
00604 
00605 void btKinematicCharacterController::setGravity(btScalar gravity)
00606 {
00607     m_gravity = gravity;
00608 }
00609 
00610 btScalar btKinematicCharacterController::getGravity() const
00611 {
00612     return m_gravity;
00613 }
00614 
00615 void btKinematicCharacterController::setMaxSlope(btScalar slopeRadians)
00616 {
00617     m_maxSlopeRadians = slopeRadians;
00618     m_maxSlopeCosine = btCos(slopeRadians);
00619 }
00620 
00621 btScalar btKinematicCharacterController::getMaxSlope() const
00622 {
00623     return m_maxSlopeRadians;
00624 }
00625 
00626 bool btKinematicCharacterController::onGround () const
00627 {
00628     return m_verticalVelocity == 0.0 && m_verticalOffset == 0.0;
00629 }
00630 
00631 
00632 btVector3* btKinematicCharacterController::getUpAxisDirections()
00633 {
00634     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) };
00635     
00636     return sUpAxisDirection;
00637 }
00638 
00639 void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)
00640 {
00641 }