Bullet Collision Detection & Physics Library
btKinematicCharacterController.cpp
Go to the documentation of this file.
1/*
2Bullet Continuous Collision Detection and Physics Library
3Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
4
5This software is provided 'as-is', without any express or implied warranty.
6In no event will the authors be held liable for any damages arising from the use of this software.
7Permission is granted to anyone to use this software for any purpose,
8including commercial applications, and to alter it and redistribute it freely,
9subject to the following restrictions:
10
111. 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.
122. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
133. This notice may not be removed or altered from any source distribution.
14*/
15
16
17#include <stdio.h>
26
27
28// static helper method
29static btVector3
31{
32 btVector3 n(0, 0, 0);
33
34 if (v.length() > SIMD_EPSILON) {
35 n = v.normalized();
36 }
37 return n;
38}
39
40
48{
49public:
54
55 virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
56 {
57 if (rayResult.m_collisionObject == m_me)
58 return 1.0;
59
60 return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
61 }
62protected:
64};
65
67{
68public:
70 : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
71 , m_me(me)
72 , m_up(up)
73 , m_minSlopeDot(minSlopeDot)
74 {
75 }
76
77 virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
78 {
79 if (convexResult.m_hitCollisionObject == m_me)
80 return btScalar(1.0);
81
82 if (!convexResult.m_hitCollisionObject->hasContactResponse())
83 return btScalar(1.0);
84
85 btVector3 hitNormalWorld;
86 if (normalInWorldSpace)
87 {
88 hitNormalWorld = convexResult.m_hitNormalLocal;
89 } else
90 {
92 hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
93 }
94
95 btScalar dotUp = m_up.dot(hitNormalWorld);
96 if (dotUp < m_minSlopeDot) {
97 return btScalar(1.0);
98 }
99
100 return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
101 }
102protected:
106};
107
108/*
109 * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
110 *
111 * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
112 */
114{
115 return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
116}
117
118/*
119 * Returns the portion of 'direction' that is parallel to 'normal'
120 */
122{
123 btScalar magnitude = direction.dot(normal);
124 return normal * magnitude;
125}
126
127/*
128 * Returns the portion of 'direction' that is perpindicular to 'normal'
129 */
131{
132 return direction - parallelComponent(direction, normal);
133}
134
136{
137 m_ghostObject = ghostObject;
138 m_up.setValue(0.0f, 0.0f, 1.0f);
139 m_jumpAxis.setValue(0.0f, 0.0f, 1.0f);
140 m_addedMargin = 0.02;
141 m_walkDirection.setValue(0.0,0.0,0.0);
142 m_AngVel.setValue(0.0, 0.0, 0.0);
144 m_turnAngle = btScalar(0.0);
145 m_convexShape=convexShape;
146 m_useWalkDirection = true; // use walk direction by default, legacy behavior
148 m_verticalVelocity = 0.0;
149 m_verticalOffset = 0.0;
150 m_gravity = 9.8 * 3.0 ; // 3G acceleration.
151 m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s.
152 m_jumpSpeed = 10.0; // ?
154 m_wasOnGround = false;
155 m_wasJumping = false;
156 m_interpolateUp = true;
159 full_drop = false;
160 bounce_fix = false;
163
164 setUp(up);
165 setStepHeight(stepHeight);
166 setMaxSlope(btRadians(45.0));
167}
168
172
177
179{
180 // Here we must refresh the overlapping paircache as the penetrating movement itself or the
181 // previous recovery iteration might have used setWorldTransform and pushed us into an object
182 // that is not in the previous cache contents from the last timestep, as will happen if we
183 // are pushed into a new AABB overlap. Unhandled this means the next convex sweep gets stuck.
184 //
185 // Do this by calling the broadphase's setAabb with the moved AABB, this will update the broadphase
186 // paircache and the ghostobject's internal paircache at the same time. /BW
187
188 btVector3 minAabb, maxAabb;
189 m_convexShape->getAabb(m_ghostObject->getWorldTransform(), minAabb,maxAabb);
190 collisionWorld->getBroadphase()->setAabb(m_ghostObject->getBroadphaseHandle(),
191 minAabb,
192 maxAabb,
193 collisionWorld->getDispatcher());
194
195 bool penetration = false;
196
197 collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
198
199 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
200
201// btScalar maxPen = btScalar(0.0);
202 for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
203 {
204 m_manifoldArray.resize(0);
205
206 btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
207
208 btCollisionObject* obj0 = static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject);
209 btCollisionObject* obj1 = static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject);
210
211 if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse()))
212 continue;
213
214 if (!needsCollision(obj0, obj1))
215 continue;
216
217 if (collisionPair->m_algorithm)
219
220
221 for (int j=0;j<m_manifoldArray.size();j++)
222 {
224 btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
225 for (int p=0;p<manifold->getNumContacts();p++)
226 {
227 const btManifoldPoint&pt = manifold->getContactPoint(p);
228
229 btScalar dist = pt.getDistance();
230
231 if (dist < -m_maxPenetrationDepth)
232 {
233 // TODO: cause problems on slopes, not sure if it is needed
234 //if (dist < maxPen)
235 //{
236 // maxPen = dist;
237 // m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
238
239 //}
240 m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
241 penetration = true;
242 } else {
243 //printf("touching %f\n", dist);
244 }
245 }
246
247 //manifold->clearManifold();
248 }
249 }
250 btTransform newTrans = m_ghostObject->getWorldTransform();
252 m_ghostObject->setWorldTransform(newTrans);
253// printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
254 return penetration;
255}
256
258{
259 btScalar stepHeight = 0.0f;
260 if (m_verticalVelocity < 0.0)
261 stepHeight = m_stepHeight;
262
263 // phase 1: up
264 btTransform start, end;
265
266 start.setIdentity ();
267 end.setIdentity ();
268
269 /* FIXME: Handle penetration properly */
271
274
276
279
283
285 {
286 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
287 }
288 else
289 {
290 world->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
291 }
292
293 if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))
294 {
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)
297 {
298 // we moved up only a fraction of the step height
299 m_currentStepOffset = stepHeight * callback.m_closestHitFraction;
300 if (m_interpolateUp == true)
302 else
304 }
305
306 btTransform& xform = m_ghostObject->getWorldTransform();
308 m_ghostObject->setWorldTransform(xform);
309
310 // fix penetration if we hit a ceiling for example
311 int numPenetrationLoops = 0;
312 m_touchingContact = false;
313 while (recoverFromPenetration(world))
314 {
315 numPenetrationLoops++;
316 m_touchingContact = true;
317 if (numPenetrationLoops > 4)
318 {
319 //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
320 break;
321 }
322 }
323 m_targetPosition = m_ghostObject->getWorldTransform().getOrigin();
325
326 if (m_verticalOffset > 0)
327 {
328 m_verticalOffset = 0.0;
329 m_verticalVelocity = 0.0;
331 }
332 } else {
333 m_currentStepOffset = stepHeight;
335 }
336}
337
339{
340 bool collides = (body0->getBroadphaseHandle()->m_collisionFilterGroup & body1->getBroadphaseHandle()->m_collisionFilterMask) != 0;
341 collides = collides && (body1->getBroadphaseHandle()->m_collisionFilterGroup & body0->getBroadphaseHandle()->m_collisionFilterMask);
342 return collides;
343}
344
346{
347 btVector3 movementDirection = m_targetPosition - m_currentPosition;
348 btScalar movementLength = movementDirection.length();
349 if (movementLength>SIMD_EPSILON)
350 {
351 movementDirection.normalize();
352
353 btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
354 reflectDir.normalize();
355
356 btVector3 parallelDir, perpindicularDir;
357
358 parallelDir = parallelComponent (reflectDir, hitNormal);
359 perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
360
362 if (0)//tangentMag != 0.0)
363 {
364 btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
365// printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
366 m_targetPosition += parComponent;
367 }
368
369 if (normalMag != 0.0)
370 {
371 btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
372// printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
373 m_targetPosition += perpComponent;
374 }
375 } else
376 {
377// printf("movementLength don't normalize a zero vector\n");
378 }
379}
380
382{
383 // printf("m_normalizedDirection=%f,%f,%f\n",
384 // m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
385 // phase 2: forward and strafe
386 btTransform start, end;
387
389
390 start.setIdentity ();
391 end.setIdentity ();
392
393 btScalar fraction = 1.0;
394 btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
395// printf("distance2=%f\n",distance2);
396
397 int maxIter = 10;
398
399 while (fraction > btScalar(0.01) && maxIter-- > 0)
400 {
403 btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
404
407
408 btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0));
411
412
413 btScalar margin = m_convexShape->getMargin();
414 m_convexShape->setMargin(margin + m_addedMargin);
415
416 if (!(start == end))
417 {
419 {
420 m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
421 }
422 else
423 {
424 collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
425 }
426 }
427 m_convexShape->setMargin(margin);
428
429
430 fraction -= callback.m_closestHitFraction;
431
432 if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))
433 {
434 // we moved only a fraction
435 //btScalar hitDistance;
436 //hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
437
438// m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
439
442 distance2 = currentDir.length2();
443 if (distance2 > SIMD_EPSILON)
444 {
445 currentDir.normalize();
446 /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
447 if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
448 {
449 break;
450 }
451 } else
452 {
453// printf("currentDir: don't normalize a zero vector\n");
454 break;
455 }
456
457 }
458 else
459 {
461 }
462 }
463}
464
466{
467 btTransform start, end, end_double;
468 bool runonce = false;
469
470 // phase 3: down
471 /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
472 btVector3 step_drop = m_up * (m_currentStepOffset + additionalDownStep);
473 btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt;
474 btVector3 gravity_drop = m_up * downVelocity;
475 m_targetPosition -= (step_drop + gravity_drop);*/
476
477 btVector3 orig_position = m_targetPosition;
478
479 btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
480
481 if (m_verticalVelocity > 0.0)
482 return;
483
484 if(downVelocity > 0.0 && downVelocity > m_fallSpeed
486 downVelocity = m_fallSpeed;
487
488 btVector3 step_drop = m_up * (m_currentStepOffset + downVelocity);
489 m_targetPosition -= step_drop;
490
494
498
499 while (1)
500 {
501 start.setIdentity ();
502 end.setIdentity ();
503
504 end_double.setIdentity ();
505
508
511
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);
514
516 {
517 m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
518
519 if (!callback.hasHit() && m_ghostObject->hasContactResponse())
520 {
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);
523 }
524 } else
525 {
526 collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
527
528 if (!callback.hasHit() && m_ghostObject->hasContactResponse())
529 {
530 //test a double fall height, to see if the character should interpolate it's fall (large) or not (small)
531 collisionWorld->convexSweepTest (m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
532 }
533 }
534
535 btScalar downVelocity2 = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
536 bool has_hit;
537 if (bounce_fix == true)
538 has_hit = (callback.hasHit() || callback2.hasHit()) && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject);
539 else
540 has_hit = callback2.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback2.m_hitCollisionObject);
541
542 btScalar stepHeight = 0.0f;
543 if (m_verticalVelocity < 0.0)
544 stepHeight = m_stepHeight;
545
546 if (downVelocity2 > 0.0 && downVelocity2 < stepHeight && has_hit == true && runonce == false
548 {
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
551
552 m_targetPosition = orig_position;
553 downVelocity = stepHeight;
554
555 step_drop = m_up * (m_currentStepOffset + downVelocity);
556 m_targetPosition -= step_drop;
557 runonce = true;
558 continue; //re-run previous tests
559 }
560 break;
561 }
562
563 if ((m_ghostObject->hasContactResponse() && (callback.hasHit() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))) || runonce == true)
564 {
565 // we dropped a fraction of the height -> hit floor
566 btScalar fraction = (m_currentPosition.getY() - callback.m_hitPointWorld.getY()) / 2;
567
568 //printf("hitpoint: %g - pos %g\n", callback.m_hitPointWorld.getY(), m_currentPosition.getY());
569
570 if (bounce_fix == true)
571 {
572 if (full_drop == true)
574 else
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);
577 }
578 else
580
581 full_drop = false;
582
583 m_verticalVelocity = 0.0;
584 m_verticalOffset = 0.0;
585 m_wasJumping = false;
586 } else {
587 // we dropped the full height
588
589 full_drop = true;
590
591 if (bounce_fix == true)
592 {
593 downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
594 if (downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping))
595 {
596 m_targetPosition += step_drop; //undo previous target change
597 downVelocity = m_fallSpeed;
598 step_drop = m_up * (m_currentStepOffset + downVelocity);
599 m_targetPosition -= step_drop;
600 }
601 }
602 //printf("full drop - %g, %g\n", m_currentPosition.getY(), m_targetPosition.getY());
603
605 }
606}
607
608
609
619
620
621
623(
624const btVector3& velocity,
625btScalar timeInterval
626)
627{
628// printf("setVelocity!\n");
629// printf(" interval: %f\n", timeInterval);
630// printf(" velocity: (%f, %f, %f)\n",
631// velocity.x(), velocity.y(), velocity.z());
632
633 m_useWalkDirection = false;
634 m_walkDirection = velocity;
636 m_velocityTimeInterval += timeInterval;
637}
638
640{
641 m_AngVel = velocity;
642}
643
648
650{
651 m_walkDirection = velocity;
652
653 // HACK: if we are moving in the direction of the up, treat it as a jump :(
654 if (m_walkDirection.length2() > 0)
655 {
656 btVector3 w = velocity.normalized();
657 btScalar c = w.dot(m_up);
658 if (c != 0)
659 {
660 //there is a component in walkdirection for vertical velocity
661 btVector3 upComponent = m_up * (btSin(SIMD_HALF_PI - btAcos(c)) * m_walkDirection.length());
662 m_walkDirection -= upComponent;
663 m_verticalVelocity = (c < 0.0f ? -1 : 1) * upComponent.length();
664
665 if (c > 0.0f)
666 {
667 m_wasJumping = true;
668 m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin();
669 }
670 }
671 }
672 else
673 m_verticalVelocity = 0.0f;
674}
675
680
682{
683 m_verticalVelocity = 0.0;
684 m_verticalOffset = 0.0;
685 m_wasOnGround = false;
686 m_wasJumping = false;
687 m_walkDirection.setValue(0,0,0);
689
690 //clear pair cache
691 btHashedOverlappingPairCache *cache = m_ghostObject->getOverlappingPairCache();
692 while (cache->getOverlappingPairArray().size() > 0)
693 {
694 cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher());
695 }
696}
697
699{
700 btTransform xform;
701 xform.setIdentity();
702 xform.setOrigin (origin);
703 m_ghostObject->setWorldTransform (xform);
704}
705
706
708{
709 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
711
712 m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
714// printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
715}
716
718{
719// printf("playerStep(): ");
720// printf(" dt = %f", dt);
721
722 if (m_AngVel.length2() > 0.0f)
723 {
725 }
726
727 // integrate for angular velocity
728 if (m_AngVel.length2() > 0.0f)
729 {
730 btTransform xform;
731 xform = m_ghostObject->getWorldTransform();
732
733 btQuaternion rot(m_AngVel.normalized(), m_AngVel.length() * dt);
734
735 btQuaternion orn = rot * xform.getRotation();
736
737 xform.setRotation(orn);
738 m_ghostObject->setWorldTransform(xform);
739
740 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
742 m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
744 }
745
746 // quick check...
748// printf("\n");
749 return; // no motion
750 }
751
753
754 //btVector3 lvel = m_walkDirection;
755 //btScalar c = 0.0f;
756
757 if (m_walkDirection.length2() > 0)
758 {
759 // apply damping
761 }
762
764
765 // Update fall velocity.
768 {
770 }
772 {
774 }
776
777 btTransform xform;
778 xform = m_ghostObject->getWorldTransform();
779
780// printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
781// printf("walkSpeed=%f\n",walkSpeed);
782
783 stepUp(collisionWorld);
784 //todo: Experimenting with behavior of controller when it hits a ceiling..
785 //bool hitUp = stepUp (collisionWorld);
786 //if (hitUp)
787 //{
788 // m_verticalVelocity -= m_gravity * dt;
789 // if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
790 // {
791 // m_verticalVelocity = m_jumpSpeed;
792 // }
793 // if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
794 // {
795 // m_verticalVelocity = -btFabs(m_fallSpeed);
796 // }
797 // m_verticalOffset = m_verticalVelocity * dt;
798
799 // xform = m_ghostObject->getWorldTransform();
800 //}
801
802 if (m_useWalkDirection) {
803 stepForwardAndStrafe (collisionWorld, m_walkDirection);
804 } else {
805 //printf(" time: %f", m_velocityTimeInterval);
806 // still have some time left for moving!
807 btScalar dtMoving =
810
811 // how far will we move while we are moving?
812 btVector3 move = m_walkDirection * dtMoving;
813
814 //printf(" dtMoving: %f", dtMoving);
815
816 // okay, step
817 stepForwardAndStrafe(collisionWorld, move);
818 }
819 stepDown (collisionWorld, dt);
820
821 //todo: Experimenting with max jump height
822 //if (m_wasJumping)
823 //{
824 // btScalar ds = m_currentPosition[m_upAxis] - m_jumpPosition[m_upAxis];
825 // if (ds > m_maxJumpHeight)
826 // {
827 // // substract the overshoot
828 // m_currentPosition[m_upAxis] -= ds - m_maxJumpHeight;
829
830 // // max height was reached, so potential energy is at max
831 // // and kinematic energy is 0, thus velocity is 0.
832 // if (m_verticalVelocity > 0.0)
833 // m_verticalVelocity = 0.0;
834 // }
835 //}
836 // printf("\n");
837
839 m_ghostObject->setWorldTransform (xform);
840
841 int numPenetrationLoops = 0;
842 m_touchingContact = false;
843 while (recoverFromPenetration(collisionWorld))
844 {
845 numPenetrationLoops++;
846 m_touchingContact = true;
847 if (numPenetrationLoops > 4)
848 {
849 //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
850 break;
851 }
852 }
853}
854
856{
857 m_fallSpeed = fallSpeed;
858}
859
865
867{
868 m_maxJumpHeight = maxJumpHeight;
869}
870
872{
873 return onGround();
874}
875
877{
878 m_jumpSpeed = v.length2() == 0 ? m_SetjumpSpeed : v.length();
880 m_wasJumping = true;
881
882 m_jumpAxis = v.length2() == 0 ? m_up : v.normalized();
883
884 m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin();
885
886#if 0
887 currently no jumping.
888 btTransform xform;
889 m_rigidBody->getMotionState()->getWorldTransform (xform);
890 btVector3 up = xform.getBasis()[1];
891 up.normalize ();
892 btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
893 m_rigidBody->applyCentralImpulse (up * magnitude);
894#endif
895}
896
898{
899 if (gravity.length2() > 0) setUpVector(-gravity);
900
901 m_gravity = gravity.length();
902}
903
908
910{
911 m_maxSlopeRadians = slopeRadians;
912 m_maxSlopeCosine = btCos(slopeRadians);
913}
914
919
924
929
934
939
941{
942 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) };
943
944 return sUpAxisDirection;
945}
946
950
955
957{
958 if (up.length2() > 0 && m_gravity > 0.0f)
959 {
961 return;
962 }
963
964 setUpVector(up);
965}
966
968{
969 if (m_up == up)
970 return;
971
972 btVector3 u = m_up;
973
974 if (up.length2() > 0)
975 m_up = up.normalized();
976 else
977 m_up = btVector3(0.0, 0.0, 0.0);
978
979 if (!m_ghostObject) return;
980 btQuaternion rot = getRotation(m_up, u);
981
982 //set orientation with new up
983 btTransform xform;
984 xform = m_ghostObject->getWorldTransform();
985 btQuaternion orn = rot.inverse() * xform.getRotation();
986 xform.setRotation(orn);
987 m_ghostObject->setWorldTransform(xform);
988}
989
991{
992 if (v0.length2() == 0.0f || v1.length2() == 0.0f)
993 {
994 btQuaternion q;
995 return q;
996 }
997
998 return shortestArcQuatNormalize2(v0, v1);
999}
1000
static btVector3 getNormalizedVector(const btVector3 &v)
btQuaternion shortestArcQuatNormalize2(btVector3 &v0, btVector3 &v1)
btScalar btPow(btScalar x, btScalar y)
Definition btScalar.h:499
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition btScalar.h:292
btScalar btSin(btScalar x)
Definition btScalar.h:477
btScalar btFabs(btScalar x)
Definition btScalar.h:475
btScalar btCos(btScalar x)
Definition btScalar.h:476
btScalar btRadians(btScalar x)
Definition btScalar.h:566
#define SIMD_EPSILON
Definition btScalar.h:521
btScalar btAcos(btScalar x)
Definition btScalar.h:479
#define SIMD_HALF_PI
Definition btScalar.h:506
int size() const
return the number of elements in the array
virtual void setAabb(btBroadphaseProxy *proxy, const btVector3 &aabbMin, const btVector3 &aabbMax, btDispatcher *dispatcher)=0
virtual void getAllContactManifolds(btManifoldArray &manifoldArray)=0
btCollisionObject can be used to manage collision detection objects.
btTransform & getWorldTransform()
btBroadphaseProxy * getBroadphaseHandle()
bool hasContactResponse() const
CollisionWorld is interface and container for the collision detection.
btDispatcher * getDispatcher()
btDispatcherInfo & getDispatchInfo()
void convexSweepTest(const btConvexShape *castShape, const btTransform &from, const btTransform &to, ConvexResultCallback &resultCallback, btScalar allowedCcdPenetration=btScalar(0.)) const
convexTest performs a swept convex cast on all objects in the btCollisionWorld, and calls the resultC...
const btBroadphaseInterface * getBroadphase() const
The btConvexShape is an abstract shape interface, implemented by all convex shapes such as btBoxShape...
virtual void dispatchAllCollisionPairs(btOverlappingPairCache *pairCache, const btDispatcherInfo &dispatchInfo, btDispatcher *dispatcher)=0
Hash-space based Pair Cache, thanks to Erin Catto, Box2D, http://www.box2d.org, and Pierre Terdiman,...
virtual void * removeOverlappingPair(btBroadphaseProxy *proxy0, btBroadphaseProxy *proxy1, btDispatcher *dispatcher)
btBroadphasePairArray & getOverlappingPairArray()
The btIDebugDraw interface class allows hooking up a debug renderer to visually debug simulations.
btVector3 perpindicularComponent(const btVector3 &direction, const btVector3 &normal)
btVector3 m_walkDirection
this is the desired walk direction, set by the user
virtual void setWalkDirection(const btVector3 &walkDirection)
This should probably be called setPositionIncrementPerSimulatorStep.
virtual bool needsCollision(const btCollisionObject *body0, const btCollisionObject *body1)
void reset(btCollisionWorld *collisionWorld)
void jump(const btVector3 &v=btVector3())
void playerStep(btCollisionWorld *collisionWorld, btScalar dt)
void preStep(btCollisionWorld *collisionWorld)
btVector3 computeReflectionDirection(const btVector3 &direction, const btVector3 &normal)
virtual void setLinearVelocity(const btVector3 &velocity)
void stepDown(btCollisionWorld *collisionWorld, btScalar dt)
virtual const btVector3 & getAngularVelocity() const
btManifoldArray m_manifoldArray
keep track of the contact manifolds
btKinematicCharacterController(btPairCachingGhostObject *ghostObject, btConvexShape *convexShape, btScalar stepHeight, const btVector3 &up=btVector3(1.0, 0.0, 0.0))
void stepForwardAndStrafe(btCollisionWorld *collisionWorld, const btVector3 &walkMove)
virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval)
Caller provides a velocity with which the character should move for the given time period.
void setMaxSlope(btScalar slopeRadians)
The max slope determines the maximum angle that the controller can walk up.
void debugDraw(btIDebugDraw *debugDrawer)
btActionInterface interface
void updateTargetPositionBasedOnCollision(const btVector3 &hit_normal, btScalar tangentMag=btScalar(0.0), btScalar normalMag=btScalar(1.0))
void stepUp(btCollisionWorld *collisionWorld)
virtual void setAngularVelocity(const btVector3 &velocity)
btQuaternion getRotation(btVector3 &v0, btVector3 &v1) const
btVector3 parallelComponent(const btVector3 &direction, const btVector3 &normal)
bool recoverFromPenetration(btCollisionWorld *collisionWorld)
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace)
btKinematicClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot)
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace)
ManifoldContactPoint collects and maintains persistent contactpoints.
btScalar getDistance() const
btVector3 m_normalWorldOnB
btPersistentManifold is a contact point cache, it stays persistent as long as objects are overlapping...
const btManifoldPoint & getContactPoint(int index) const
const btCollisionObject * getBody0() const
The btQuaternion implements quaternion to perform linear algebra rotations in combination with btMatr...
btQuaternion inverse() const
Return the inverse of this quaternion.
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition btTransform.h:34
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
void setRotation(const btQuaternion &q)
Set the rotational element by btQuaternion.
void setIdentity()
Set this transformation to the identity.
btQuaternion getRotation() const
Return a quaternion representing the rotation.
void setOrigin(const btVector3 &origin)
Set the translational element.
btVector3 can be used to represent 3D points and vectors.
Definition btVector3.h:84
btScalar length() const
Return the length of the vector.
Definition btVector3.h:263
btScalar dot(const btVector3 &v) const
Return the dot product.
Definition btVector3.h:235
btVector3 normalized() const
Return a normalized version of this vector.
Definition btVector3.h:964
btScalar length2() const
Return the length of the vector squared.
Definition btVector3.h:257
const btScalar & getY() const
Return the y value.
Definition btVector3.h:575
btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:309
The btBroadphasePair class contains a pair of aabb-overlapping objects.
btBroadphaseProxy * m_pProxy1
btBroadphaseProxy * m_pProxy0
btCollisionAlgorithm * m_algorithm
ClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld)
ClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld)
const btCollisionObject * m_hitCollisionObject
const btCollisionObject * m_collisionObject
btScalar m_allowedCcdPenetration