2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
11 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.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
17 #include "LinearMath/btIDebugDraw.h"
18 #include "BulletCollision/CollisionDispatch/btGhostObject.h"
19 #include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
20 #include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
21 #include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
22 #include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
23 #include "LinearMath/btDefaultMotionState.h"
24 #include "btKinematicCharacterController.h"
27 // static helper method
29 getNormalizedVector(const btVector3& v)
31 btVector3 n = v.normalized();
32 if (n.length() < SIMD_EPSILON) {
39 ///@todo Interact with dynamic objects,
40 ///Ride kinematicly animated platforms properly
41 ///More realistic (or maybe just a config option) falling
42 /// -> Should integrate falling velocity manually and use that in stepDown()
45 class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
48 btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
53 virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
55 if (rayResult.m_collisionObject == m_me)
58 return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
61 btCollisionObject* m_me;
64 class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
67 btKinematicClosestNotMeConvexResultCallback (btCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
68 : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
71 , m_minSlopeDot(minSlopeDot)
75 virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
77 if (convexResult.m_hitCollisionObject == m_me)
80 btVector3 hitNormalWorld;
81 if (normalInWorldSpace)
83 hitNormalWorld = convexResult.m_hitNormalLocal;
86 ///need to transform normal into worldspace
87 hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
90 btScalar dotUp = m_up.dot(hitNormalWorld);
91 if (dotUp < m_minSlopeDot) {
95 return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
98 btCollisionObject* m_me;
100 btScalar m_minSlopeDot;
104 * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
106 * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
108 btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal)
110 return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
114 * Returns the portion of 'direction' that is parallel to 'normal'
116 btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal)
118 btScalar magnitude = direction.dot(normal);
119 return normal * magnitude;
123 * Returns the portion of 'direction' that is perpindicular to 'normal'
125 btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal)
127 return direction - parallelComponent(direction, normal);
130 btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis)
133 m_addedMargin = 0.02;
134 m_walkDirection.setValue(0,0,0);
135 m_useGhostObjectSweepTest = true;
136 m_ghostObject = ghostObject;
137 m_stepHeight = stepHeight;
138 m_turnAngle = btScalar(0.0);
139 m_convexShape=convexShape;
140 m_useWalkDirection = true; // use walk direction by default, legacy behavior
141 m_velocityTimeInterval = 0.0;
142 m_verticalVelocity = 0.0;
143 m_verticalOffset = 0.0;
144 m_gravity = 9.8 * 3 ; // 3G acceleration.
145 m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s.
146 m_jumpSpeed = 10.0; // ?
147 m_wasOnGround = false;
148 m_wasJumping = false;
149 setMaxSlope(btRadians(45.0));
152 btKinematicCharacterController::~btKinematicCharacterController ()
156 btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
158 return m_ghostObject;
161 bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld)
164 bool penetration = false;
166 collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
168 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
170 btScalar maxPen = btScalar(0.0);
171 for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
173 m_manifoldArray.resize(0);
175 btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
177 if (collisionPair->m_algorithm)
178 collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
181 for (int j=0;j<m_manifoldArray.size();j++)
183 btPersistentManifold* manifold = m_manifoldArray[j];
184 btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
185 for (int p=0;p<manifold->getNumContacts();p++)
187 const btManifoldPoint&pt = manifold->getContactPoint(p);
189 btScalar dist = pt.getDistance();
196 m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
199 m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
202 //printf("touching %f\n", dist);
206 //manifold->clearManifold();
209 btTransform newTrans = m_ghostObject->getWorldTransform();
210 newTrans.setOrigin(m_currentPosition);
211 m_ghostObject->setWorldTransform(newTrans);
212 // printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
216 void btKinematicCharacterController::stepUp ( btCollisionWorld* world)
219 btTransform start, end;
220 m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.f?m_verticalOffset:0.f));
222 start.setIdentity ();
225 /* FIXME: Handle penetration properly */
226 start.setOrigin (m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_convexShape->getMargin() + m_addedMargin));
227 end.setOrigin (m_targetPosition);
229 btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, -getUpAxisDirections()[m_upAxis], btScalar(0.7071));
230 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
231 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
233 if (m_useGhostObjectSweepTest)
235 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
239 world->convexSweepTest (m_convexShape, start, end, callback);
242 if (callback.hasHit())
244 // Only modify the position if the hit was a slope and not a wall or ceiling.
245 if(callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > 0.0)
247 // we moved up only a fraction of the step height
248 m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
249 m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
251 m_verticalVelocity = 0.0;
252 m_verticalOffset = 0.0;
254 m_currentStepOffset = m_stepHeight;
255 m_currentPosition = m_targetPosition;
259 void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
261 btVector3 movementDirection = m_targetPosition - m_currentPosition;
262 btScalar movementLength = movementDirection.length();
263 if (movementLength>SIMD_EPSILON)
265 movementDirection.normalize();
267 btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
268 reflectDir.normalize();
270 btVector3 parallelDir, perpindicularDir;
272 parallelDir = parallelComponent (reflectDir, hitNormal);
273 perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
275 m_targetPosition = m_currentPosition;
276 if (0)//tangentMag != 0.0)
278 btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
279 // printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
280 m_targetPosition += parComponent;
283 if (normalMag != 0.0)
285 btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
286 // printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
287 m_targetPosition += perpComponent;
291 // printf("movementLength don't normalize a zero vector\n");
295 void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove)
297 // printf("m_normalizedDirection=%f,%f,%f\n",
298 // m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
299 // phase 2: forward and strafe
300 btTransform start, end;
301 m_targetPosition = m_currentPosition + walkMove;
303 start.setIdentity ();
306 btScalar fraction = 1.0;
307 btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
308 // printf("distance2=%f\n",distance2);
310 if (m_touchingContact)
312 if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0))
314 updateTargetPositionBasedOnCollision (m_touchingNormal);
320 while (fraction > btScalar(0.01) && maxIter-- > 0)
322 start.setOrigin (m_currentPosition);
323 end.setOrigin (m_targetPosition);
324 btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
326 btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0));
327 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
328 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
331 btScalar margin = m_convexShape->getMargin();
332 m_convexShape->setMargin(margin + m_addedMargin);
335 if (m_useGhostObjectSweepTest)
337 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
340 collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
343 m_convexShape->setMargin(margin);
346 fraction -= callback.m_closestHitFraction;
348 if (callback.hasHit())
350 // we moved only a fraction
351 btScalar hitDistance;
352 hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
354 // m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
356 updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
357 btVector3 currentDir = m_targetPosition - m_currentPosition;
358 distance2 = currentDir.length2();
359 if (distance2 > SIMD_EPSILON)
361 currentDir.normalize();
362 /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
363 if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
369 // printf("currentDir: don't normalize a zero vector\n");
374 // we moved whole way
375 m_currentPosition = m_targetPosition;
378 // if (callback.m_closestHitFraction == 0.f)
384 void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt)
386 btTransform start, end;
389 /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
390 btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + additionalDownStep);
391 btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt;
392 btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downVelocity;
393 m_targetPosition -= (step_drop + gravity_drop);*/
395 btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
396 if(downVelocity > 0.0 && downVelocity < m_stepHeight
397 && (m_wasOnGround || !m_wasJumping))
399 downVelocity = m_stepHeight;
402 btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity);
403 m_targetPosition -= step_drop;
405 start.setIdentity ();
408 start.setOrigin (m_currentPosition);
409 end.setOrigin (m_targetPosition);
411 btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine);
412 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
413 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
415 if (m_useGhostObjectSweepTest)
417 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
420 collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
423 if (callback.hasHit())
425 // we dropped a fraction of the height -> hit floor
426 m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
427 m_verticalVelocity = 0.0;
428 m_verticalOffset = 0.0;
429 m_wasJumping = false;
431 // we dropped the full height
433 m_currentPosition = m_targetPosition;
439 void btKinematicCharacterController::setWalkDirection
441 const btVector3& walkDirection
444 m_useWalkDirection = true;
445 m_walkDirection = walkDirection;
446 m_normalizedDirection = getNormalizedVector(m_walkDirection);
451 void btKinematicCharacterController::setVelocityForTimeInterval
453 const btVector3& velocity,
454 btScalar timeInterval
457 // printf("setVelocity!\n");
458 // printf(" interval: %f\n", timeInterval);
459 // printf(" velocity: (%f, %f, %f)\n",
460 // velocity.x(), velocity.y(), velocity.z());
462 m_useWalkDirection = false;
463 m_walkDirection = velocity;
464 m_normalizedDirection = getNormalizedVector(m_walkDirection);
465 m_velocityTimeInterval = timeInterval;
470 void btKinematicCharacterController::reset ()
474 void btKinematicCharacterController::warp (const btVector3& origin)
478 xform.setOrigin (origin);
479 m_ghostObject->setWorldTransform (xform);
483 void btKinematicCharacterController::preStep ( btCollisionWorld* collisionWorld)
486 int numPenetrationLoops = 0;
487 m_touchingContact = false;
488 while (recoverFromPenetration (collisionWorld))
490 numPenetrationLoops++;
491 m_touchingContact = true;
492 if (numPenetrationLoops > 4)
494 //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
499 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
500 m_targetPosition = m_currentPosition;
501 // printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
508 void btKinematicCharacterController::playerStep ( btCollisionWorld* collisionWorld, btScalar dt)
510 // printf("playerStep(): ");
511 // printf(" dt = %f", dt);
514 if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) {
519 m_wasOnGround = onGround();
521 // Update fall velocity.
522 m_verticalVelocity -= m_gravity * dt;
523 if(m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
525 m_verticalVelocity = m_jumpSpeed;
527 if(m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
529 m_verticalVelocity = -btFabs(m_fallSpeed);
531 m_verticalOffset = m_verticalVelocity * dt;
535 xform = m_ghostObject->getWorldTransform ();
537 // printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
538 // printf("walkSpeed=%f\n",walkSpeed);
540 stepUp (collisionWorld);
541 if (m_useWalkDirection) {
542 stepForwardAndStrafe (collisionWorld, m_walkDirection);
544 //printf(" time: %f", m_velocityTimeInterval);
545 // still have some time left for moving!
547 (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval;
548 m_velocityTimeInterval -= dt;
550 // how far will we move while we are moving?
551 btVector3 move = m_walkDirection * dtMoving;
553 //printf(" dtMoving: %f", dtMoving);
556 stepForwardAndStrafe(collisionWorld, move);
558 stepDown (collisionWorld, dt);
562 xform.setOrigin (m_currentPosition);
563 m_ghostObject->setWorldTransform (xform);
566 void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed)
568 m_fallSpeed = fallSpeed;
571 void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed)
573 m_jumpSpeed = jumpSpeed;
576 void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight)
578 m_maxJumpHeight = maxJumpHeight;
581 bool btKinematicCharacterController::canJump () const
586 void btKinematicCharacterController::jump ()
591 m_verticalVelocity = m_jumpSpeed;
595 currently no jumping.
597 m_rigidBody->getMotionState()->getWorldTransform (xform);
598 btVector3 up = xform.getBasis()[1];
600 btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
601 m_rigidBody->applyCentralImpulse (up * magnitude);
605 void btKinematicCharacterController::setGravity(btScalar gravity)
610 btScalar btKinematicCharacterController::getGravity() const
615 void btKinematicCharacterController::setMaxSlope(btScalar slopeRadians)
617 m_maxSlopeRadians = slopeRadians;
618 m_maxSlopeCosine = btCos(slopeRadians);
621 btScalar btKinematicCharacterController::getMaxSlope() const
623 return m_maxSlopeRadians;
626 bool btKinematicCharacterController::onGround () const
628 return m_verticalVelocity == 0.0 && m_verticalOffset == 0.0;
632 btVector3* btKinematicCharacterController::getUpAxisDirections()
634 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) };
636 return sUpAxisDirection;
639 void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)