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"
26 // static helper method
28 getNormalizedVector(const btVector3& v)
32 if (v.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);
62 btCollisionObject* m_me;
65 class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
68 btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
69 : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), m_me(me), m_up(up), m_minSlopeDot(minSlopeDot)
73 virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
75 if (convexResult.m_hitCollisionObject == m_me)
78 if (!convexResult.m_hitCollisionObject->hasContactResponse())
81 btVector3 hitNormalWorld;
82 if (normalInWorldSpace)
84 hitNormalWorld = convexResult.m_hitNormalLocal;
88 ///need to transform normal into worldspace
89 hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal;
92 btScalar dotUp = m_up.dot(hitNormalWorld);
93 if (dotUp < m_minSlopeDot)
98 return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
102 btCollisionObject* m_me;
103 const btVector3 m_up;
104 btScalar m_minSlopeDot;
108 * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
110 * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
112 btVector3 btKinematicCharacterController::computeReflectionDirection(const btVector3& direction, const btVector3& normal)
114 return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
118 * Returns the portion of 'direction' that is parallel to 'normal'
120 btVector3 btKinematicCharacterController::parallelComponent(const btVector3& direction, const btVector3& normal)
122 btScalar magnitude = direction.dot(normal);
123 return normal * magnitude;
127 * Returns the portion of 'direction' that is perpindicular to 'normal'
129 btVector3 btKinematicCharacterController::perpindicularComponent(const btVector3& direction, const btVector3& normal)
131 return direction - parallelComponent(direction, normal);
134 btKinematicCharacterController::btKinematicCharacterController(btPairCachingGhostObject* ghostObject, btConvexShape* convexShape, btScalar stepHeight, const btVector3& up)
136 m_ghostObject = ghostObject;
137 m_up.setValue(0.0f, 0.0f, 1.0f);
138 m_jumpAxis.setValue(0.0f, 0.0f, 1.0f);
139 m_addedMargin = 0.02;
140 m_walkDirection.setValue(0.0, 0.0, 0.0);
141 m_AngVel.setValue(0.0, 0.0, 0.0);
142 m_useGhostObjectSweepTest = true;
143 m_turnAngle = btScalar(0.0);
144 m_convexShape = convexShape;
145 m_useWalkDirection = true; // use walk direction by default, legacy behavior
146 m_velocityTimeInterval = 0.0;
147 m_verticalVelocity = 0.0;
148 m_verticalOffset = 0.0;
149 m_gravity = 9.8 * 3.0; // 3G acceleration.
150 m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s.
151 m_jumpSpeed = 10.0; // ?
152 m_SetjumpSpeed = m_jumpSpeed;
153 m_wasOnGround = false;
154 m_wasJumping = false;
155 m_interpolateUp = true;
156 m_currentStepOffset = 0.0;
157 m_maxPenetrationDepth = 0.2;
160 m_linearDamping = btScalar(0.0);
161 m_angularDamping = btScalar(0.0);
164 setStepHeight(stepHeight);
165 setMaxSlope(btRadians(45.0));
168 btKinematicCharacterController::~btKinematicCharacterController()
172 btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
174 return m_ghostObject;
177 bool btKinematicCharacterController::recoverFromPenetration(btCollisionWorld* collisionWorld)
179 // Here we must refresh the overlapping paircache as the penetrating movement itself or the
180 // previous recovery iteration might have used setWorldTransform and pushed us into an object
181 // that is not in the previous cache contents from the last timestep, as will happen if we
182 // are pushed into a new AABB overlap. Unhandled this means the next convex sweep gets stuck.
184 // Do this by calling the broadphase's setAabb with the moved AABB, this will update the broadphase
185 // paircache and the ghostobject's internal paircache at the same time. /BW
187 btVector3 minAabb, maxAabb;
188 m_convexShape->getAabb(m_ghostObject->getWorldTransform(), minAabb, maxAabb);
189 collisionWorld->getBroadphase()->setAabb(m_ghostObject->getBroadphaseHandle(),
192 collisionWorld->getDispatcher());
194 bool penetration = false;
196 collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
198 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
200 // btScalar maxPen = btScalar(0.0);
201 for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
203 m_manifoldArray.resize(0);
205 btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
207 btCollisionObject* obj0 = static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject);
208 btCollisionObject* obj1 = static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject);
210 if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse()))
213 if (!needsCollision(obj0, obj1))
216 if (collisionPair->m_algorithm)
217 collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
219 for (int j = 0; j < m_manifoldArray.size(); j++)
221 btPersistentManifold* manifold = m_manifoldArray[j];
222 btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
223 for (int p = 0; p < manifold->getNumContacts(); p++)
225 const btManifoldPoint& pt = manifold->getContactPoint(p);
227 btScalar dist = pt.getDistance();
229 if (dist < -m_maxPenetrationDepth)
231 // TODO: cause problems on slopes, not sure if it is needed
235 // m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
238 m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
243 //printf("touching %f\n", dist);
247 //manifold->clearManifold();
250 btTransform newTrans = m_ghostObject->getWorldTransform();
251 newTrans.setOrigin(m_currentPosition);
252 m_ghostObject->setWorldTransform(newTrans);
253 // printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
257 void btKinematicCharacterController::stepUp(btCollisionWorld* world)
259 btScalar stepHeight = 0.0f;
260 if (m_verticalVelocity < 0.0)
261 stepHeight = m_stepHeight;
264 btTransform start, end;
269 /* FIXME: Handle penetration properly */
270 start.setOrigin(m_currentPosition);
272 m_targetPosition = m_currentPosition + m_up * (stepHeight) + m_jumpAxis * ((m_verticalOffset > 0.f ? m_verticalOffset : 0.f));
273 m_currentPosition = m_targetPosition;
275 end.setOrigin(m_targetPosition);
277 start.setRotation(m_currentOrientation);
278 end.setRotation(m_targetOrientation);
280 btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, -m_up, m_maxSlopeCosine);
281 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
282 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
284 if (m_useGhostObjectSweepTest)
286 m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
290 world->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
293 if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))
295 // Only modify the position if the hit was a slope and not a wall or ceiling.
296 if (callback.m_hitNormalWorld.dot(m_up) > 0.0)
298 // we moved up only a fraction of the step height
299 m_currentStepOffset = stepHeight * callback.m_closestHitFraction;
300 if (m_interpolateUp == true)
301 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
303 m_currentPosition = m_targetPosition;
306 btTransform& xform = m_ghostObject->getWorldTransform();
307 xform.setOrigin(m_currentPosition);
308 m_ghostObject->setWorldTransform(xform);
310 // fix penetration if we hit a ceiling for example
311 int numPenetrationLoops = 0;
312 m_touchingContact = false;
313 while (recoverFromPenetration(world))
315 numPenetrationLoops++;
316 m_touchingContact = true;
317 if (numPenetrationLoops > 4)
319 //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
323 m_targetPosition = m_ghostObject->getWorldTransform().getOrigin();
324 m_currentPosition = m_targetPosition;
326 if (m_verticalOffset > 0)
328 m_verticalOffset = 0.0;
329 m_verticalVelocity = 0.0;
330 m_currentStepOffset = m_stepHeight;
335 m_currentStepOffset = stepHeight;
336 m_currentPosition = m_targetPosition;
340 bool btKinematicCharacterController::needsCollision(const btCollisionObject* body0, const btCollisionObject* body1)
342 bool collides = (body0->getBroadphaseHandle()->m_collisionFilterGroup & body1->getBroadphaseHandle()->m_collisionFilterMask) != 0;
343 collides = collides && (body1->getBroadphaseHandle()->m_collisionFilterGroup & body0->getBroadphaseHandle()->m_collisionFilterMask);
347 void btKinematicCharacterController::updateTargetPositionBasedOnCollision(const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
349 btVector3 movementDirection = m_targetPosition - m_currentPosition;
350 btScalar movementLength = movementDirection.length();
351 if (movementLength > SIMD_EPSILON)
353 movementDirection.normalize();
355 btVector3 reflectDir = computeReflectionDirection(movementDirection, hitNormal);
356 reflectDir.normalize();
358 btVector3 parallelDir, perpindicularDir;
360 parallelDir = parallelComponent(reflectDir, hitNormal);
361 perpindicularDir = perpindicularComponent(reflectDir, hitNormal);
363 m_targetPosition = m_currentPosition;
364 if (0) //tangentMag != 0.0)
366 btVector3 parComponent = parallelDir * btScalar(tangentMag * movementLength);
367 // printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
368 m_targetPosition += parComponent;
371 if (normalMag != 0.0)
373 btVector3 perpComponent = perpindicularDir * btScalar(normalMag * movementLength);
374 // printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
375 m_targetPosition += perpComponent;
380 // printf("movementLength don't normalize a zero vector\n");
384 void btKinematicCharacterController::stepForwardAndStrafe(btCollisionWorld* collisionWorld, const btVector3& walkMove)
386 // printf("m_normalizedDirection=%f,%f,%f\n",
387 // m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
388 // phase 2: forward and strafe
389 btTransform start, end;
391 m_targetPosition = m_currentPosition + walkMove;
396 btScalar fraction = 1.0;
397 btScalar distance2 = (m_currentPosition - m_targetPosition).length2();
398 // printf("distance2=%f\n",distance2);
402 while (fraction > btScalar(0.01) && maxIter-- > 0)
404 start.setOrigin(m_currentPosition);
405 end.setOrigin(m_targetPosition);
406 btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
408 start.setRotation(m_currentOrientation);
409 end.setRotation(m_targetOrientation);
411 btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, sweepDirNegative, btScalar(0.0));
412 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
413 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
415 btScalar margin = m_convexShape->getMargin();
416 m_convexShape->setMargin(margin + m_addedMargin);
420 if (m_useGhostObjectSweepTest)
422 m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
426 collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
429 m_convexShape->setMargin(margin);
431 fraction -= callback.m_closestHitFraction;
433 if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))
435 // we moved only a fraction
436 //btScalar hitDistance;
437 //hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
439 // m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
441 updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld);
442 btVector3 currentDir = m_targetPosition - m_currentPosition;
443 distance2 = currentDir.length2();
444 if (distance2 > SIMD_EPSILON)
446 currentDir.normalize();
447 /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
448 if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
455 // printf("currentDir: don't normalize a zero vector\n");
461 m_currentPosition = m_targetPosition;
466 void btKinematicCharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt)
468 btTransform start, end, end_double;
469 bool runonce = false;
472 /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
473 btVector3 step_drop = m_up * (m_currentStepOffset + additionalDownStep);
474 btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt;
475 btVector3 gravity_drop = m_up * downVelocity;
476 m_targetPosition -= (step_drop + gravity_drop);*/
478 btVector3 orig_position = m_targetPosition;
480 btScalar downVelocity = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f) * dt;
482 if (m_verticalVelocity > 0.0)
485 if (downVelocity > 0.0 && downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping))
486 downVelocity = m_fallSpeed;
488 btVector3 step_drop = m_up * (m_currentStepOffset + downVelocity);
489 m_targetPosition -= step_drop;
491 btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, m_up, m_maxSlopeCosine);
492 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
493 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
495 btKinematicClosestNotMeConvexResultCallback callback2(m_ghostObject, m_up, m_maxSlopeCosine);
496 callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
497 callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
504 end_double.setIdentity();
506 start.setOrigin(m_currentPosition);
507 end.setOrigin(m_targetPosition);
509 start.setRotation(m_currentOrientation);
510 end.setRotation(m_targetOrientation);
512 //set double test for 2x the step drop, to check for a large drop vs small drop
513 end_double.setOrigin(m_targetPosition - step_drop);
515 if (m_useGhostObjectSweepTest)
517 m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
519 if (!callback.hasHit() && m_ghostObject->hasContactResponse())
521 //test a double fall height, to see if the character should interpolate it's fall (full) or not (partial)
522 m_ghostObject->convexSweepTest(m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
527 collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
529 if (!callback.hasHit() && m_ghostObject->hasContactResponse())
531 //test a double fall height, to see if the character should interpolate it's fall (large) or not (small)
532 collisionWorld->convexSweepTest(m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
536 btScalar downVelocity2 = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f) * dt;
538 if (bounce_fix == true)
539 has_hit = (callback.hasHit() || callback2.hasHit()) && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject);
541 has_hit = callback2.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback2.m_hitCollisionObject);
543 btScalar stepHeight = 0.0f;
544 if (m_verticalVelocity < 0.0)
545 stepHeight = m_stepHeight;
547 if (downVelocity2 > 0.0 && downVelocity2 < stepHeight && has_hit == true && runonce == false && (m_wasOnGround || !m_wasJumping))
549 //redo the velocity calculation when falling a small amount, for fast stairs motion
550 //for larger falls, use the smoother/slower interpolated movement by not touching the target position
552 m_targetPosition = orig_position;
553 downVelocity = stepHeight;
555 step_drop = m_up * (m_currentStepOffset + downVelocity);
556 m_targetPosition -= step_drop;
558 continue; //re-run previous tests
563 if ((m_ghostObject->hasContactResponse() && (callback.hasHit() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))) || runonce == true)
565 // we dropped a fraction of the height -> hit floor
566 btScalar fraction = (m_currentPosition.getY() - callback.m_hitPointWorld.getY()) / 2;
568 //printf("hitpoint: %g - pos %g\n", callback.m_hitPointWorld.getY(), m_currentPosition.getY());
570 if (bounce_fix == true)
572 if (full_drop == true)
573 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
575 //due to errors in the closestHitFraction variable when used with large polygons, calculate the hit fraction manually
576 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, fraction);
579 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
583 m_verticalVelocity = 0.0;
584 m_verticalOffset = 0.0;
585 m_wasJumping = false;
589 // we dropped the full height
593 if (bounce_fix == true)
595 downVelocity = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f) * dt;
596 if (downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping))
598 m_targetPosition += step_drop; //undo previous target change
599 downVelocity = m_fallSpeed;
600 step_drop = m_up * (m_currentStepOffset + downVelocity);
601 m_targetPosition -= step_drop;
604 //printf("full drop - %g, %g\n", m_currentPosition.getY(), m_targetPosition.getY());
606 m_currentPosition = m_targetPosition;
610 void btKinematicCharacterController::setWalkDirection(
611 const btVector3& walkDirection)
613 m_useWalkDirection = true;
614 m_walkDirection = walkDirection;
615 m_normalizedDirection = getNormalizedVector(m_walkDirection);
618 void btKinematicCharacterController::setVelocityForTimeInterval(
619 const btVector3& velocity,
620 btScalar timeInterval)
622 // printf("setVelocity!\n");
623 // printf(" interval: %f\n", timeInterval);
624 // printf(" velocity: (%f, %f, %f)\n",
625 // velocity.x(), velocity.y(), velocity.z());
627 m_useWalkDirection = false;
628 m_walkDirection = velocity;
629 m_normalizedDirection = getNormalizedVector(m_walkDirection);
630 m_velocityTimeInterval += timeInterval;
633 void btKinematicCharacterController::setAngularVelocity(const btVector3& velocity)
638 const btVector3& btKinematicCharacterController::getAngularVelocity() const
643 void btKinematicCharacterController::setLinearVelocity(const btVector3& velocity)
645 m_walkDirection = velocity;
647 // HACK: if we are moving in the direction of the up, treat it as a jump :(
648 if (m_walkDirection.length2() > 0)
650 btVector3 w = velocity.normalized();
651 btScalar c = w.dot(m_up);
654 //there is a component in walkdirection for vertical velocity
655 btVector3 upComponent = m_up * (btSin(SIMD_HALF_PI - btAcos(c)) * m_walkDirection.length());
656 m_walkDirection -= upComponent;
657 m_verticalVelocity = (c < 0.0f ? -1 : 1) * upComponent.length();
662 m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin();
667 m_verticalVelocity = 0.0f;
670 btVector3 btKinematicCharacterController::getLinearVelocity() const
672 return m_walkDirection + (m_verticalVelocity * m_up);
675 void btKinematicCharacterController::reset(btCollisionWorld* collisionWorld)
677 m_verticalVelocity = 0.0;
678 m_verticalOffset = 0.0;
679 m_wasOnGround = false;
680 m_wasJumping = false;
681 m_walkDirection.setValue(0, 0, 0);
682 m_velocityTimeInterval = 0.0;
685 btHashedOverlappingPairCache* cache = m_ghostObject->getOverlappingPairCache();
686 while (cache->getOverlappingPairArray().size() > 0)
688 cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher());
692 void btKinematicCharacterController::warp(const btVector3& origin)
696 xform.setOrigin(origin);
697 m_ghostObject->setWorldTransform(xform);
700 void btKinematicCharacterController::preStep(btCollisionWorld* collisionWorld)
702 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
703 m_targetPosition = m_currentPosition;
705 m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
706 m_targetOrientation = m_currentOrientation;
707 // printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
710 void btKinematicCharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt)
712 // printf("playerStep(): ");
713 // printf(" dt = %f", dt);
715 if (m_AngVel.length2() > 0.0f)
717 m_AngVel *= btPow(btScalar(1) - m_angularDamping, dt);
720 // integrate for angular velocity
721 if (m_AngVel.length2() > 0.0f)
724 xform = m_ghostObject->getWorldTransform();
726 btQuaternion rot(m_AngVel.normalized(), m_AngVel.length() * dt);
728 btQuaternion orn = rot * xform.getRotation();
730 xform.setRotation(orn);
731 m_ghostObject->setWorldTransform(xform);
733 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
734 m_targetPosition = m_currentPosition;
735 m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
736 m_targetOrientation = m_currentOrientation;
740 if (!m_useWalkDirection && (m_velocityTimeInterval <= 0.0 || m_walkDirection.fuzzyZero()))
746 m_wasOnGround = onGround();
748 //btVector3 lvel = m_walkDirection;
751 if (m_walkDirection.length2() > 0)
754 m_walkDirection *= btPow(btScalar(1) - m_linearDamping, dt);
757 m_verticalVelocity *= btPow(btScalar(1) - m_linearDamping, dt);
759 // Update fall velocity.
760 m_verticalVelocity -= m_gravity * dt;
761 if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
763 m_verticalVelocity = m_jumpSpeed;
765 if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
767 m_verticalVelocity = -btFabs(m_fallSpeed);
769 m_verticalOffset = m_verticalVelocity * dt;
772 xform = m_ghostObject->getWorldTransform();
774 // printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
775 // printf("walkSpeed=%f\n",walkSpeed);
777 stepUp(collisionWorld);
778 //todo: Experimenting with behavior of controller when it hits a ceiling..
779 //bool hitUp = stepUp (collisionWorld);
782 // m_verticalVelocity -= m_gravity * dt;
783 // if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
785 // m_verticalVelocity = m_jumpSpeed;
787 // if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
789 // m_verticalVelocity = -btFabs(m_fallSpeed);
791 // m_verticalOffset = m_verticalVelocity * dt;
793 // xform = m_ghostObject->getWorldTransform();
796 if (m_useWalkDirection)
798 stepForwardAndStrafe(collisionWorld, m_walkDirection);
802 //printf(" time: %f", m_velocityTimeInterval);
803 // still have some time left for moving!
805 (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval;
806 m_velocityTimeInterval -= dt;
808 // how far will we move while we are moving?
809 btVector3 move = m_walkDirection * dtMoving;
811 //printf(" dtMoving: %f", dtMoving);
814 stepForwardAndStrafe(collisionWorld, move);
816 stepDown(collisionWorld, dt);
818 //todo: Experimenting with max jump height
821 // btScalar ds = m_currentPosition[m_upAxis] - m_jumpPosition[m_upAxis];
822 // if (ds > m_maxJumpHeight)
824 // // substract the overshoot
825 // m_currentPosition[m_upAxis] -= ds - m_maxJumpHeight;
827 // // max height was reached, so potential energy is at max
828 // // and kinematic energy is 0, thus velocity is 0.
829 // if (m_verticalVelocity > 0.0)
830 // m_verticalVelocity = 0.0;
835 xform.setOrigin(m_currentPosition);
836 m_ghostObject->setWorldTransform(xform);
838 int numPenetrationLoops = 0;
839 m_touchingContact = false;
840 while (recoverFromPenetration(collisionWorld))
842 numPenetrationLoops++;
843 m_touchingContact = true;
844 if (numPenetrationLoops > 4)
846 //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
852 void btKinematicCharacterController::setFallSpeed(btScalar fallSpeed)
854 m_fallSpeed = fallSpeed;
857 void btKinematicCharacterController::setJumpSpeed(btScalar jumpSpeed)
859 m_jumpSpeed = jumpSpeed;
860 m_SetjumpSpeed = m_jumpSpeed;
863 void btKinematicCharacterController::setMaxJumpHeight(btScalar maxJumpHeight)
865 m_maxJumpHeight = maxJumpHeight;
868 bool btKinematicCharacterController::canJump() const
873 void btKinematicCharacterController::jump(const btVector3& v)
875 m_jumpSpeed = v.length2() == 0 ? m_SetjumpSpeed : v.length();
876 m_verticalVelocity = m_jumpSpeed;
879 m_jumpAxis = v.length2() == 0 ? m_up : v.normalized();
881 m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin();
884 currently no jumping.
886 m_rigidBody->getMotionState()->getWorldTransform (xform);
887 btVector3 up = xform.getBasis()[1];
889 btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
890 m_rigidBody->applyCentralImpulse (up * magnitude);
894 void btKinematicCharacterController::setGravity(const btVector3& gravity)
896 if (gravity.length2() > 0) setUpVector(-gravity);
898 m_gravity = gravity.length();
901 btVector3 btKinematicCharacterController::getGravity() const
903 return -m_gravity * m_up;
906 void btKinematicCharacterController::setMaxSlope(btScalar slopeRadians)
908 m_maxSlopeRadians = slopeRadians;
909 m_maxSlopeCosine = btCos(slopeRadians);
912 btScalar btKinematicCharacterController::getMaxSlope() const
914 return m_maxSlopeRadians;
917 void btKinematicCharacterController::setMaxPenetrationDepth(btScalar d)
919 m_maxPenetrationDepth = d;
922 btScalar btKinematicCharacterController::getMaxPenetrationDepth() const
924 return m_maxPenetrationDepth;
927 bool btKinematicCharacterController::onGround() const
929 return (fabs(m_verticalVelocity) < SIMD_EPSILON) && (fabs(m_verticalOffset) < SIMD_EPSILON);
932 void btKinematicCharacterController::setStepHeight(btScalar h)
937 btVector3* btKinematicCharacterController::getUpAxisDirections()
939 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)};
941 return sUpAxisDirection;
944 void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)
948 void btKinematicCharacterController::setUpInterpolate(bool value)
950 m_interpolateUp = value;
953 void btKinematicCharacterController::setUp(const btVector3& up)
955 if (up.length2() > 0 && m_gravity > 0.0f)
957 setGravity(-m_gravity * up.normalized());
964 void btKinematicCharacterController::setUpVector(const btVector3& up)
971 if (up.length2() > 0)
972 m_up = up.normalized();
974 m_up = btVector3(0.0, 0.0, 0.0);
976 if (!m_ghostObject) return;
977 btQuaternion rot = getRotation(m_up, u);
979 //set orientation with new up
981 xform = m_ghostObject->getWorldTransform();
982 btQuaternion orn = rot.inverse() * xform.getRotation();
983 xform.setRotation(orn);
984 m_ghostObject->setWorldTransform(xform);
987 btQuaternion btKinematicCharacterController::getRotation(btVector3& v0, btVector3& v1) const
989 if (v0.length2() == 0.0f || v1.length2() == 0.0f)
995 return shortestArcQuatNormalize2(v0, v1);