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#include <stdio.h>
25
26// static helper method
27static btVector3
29{
30 btVector3 n(0, 0, 0);
31
32 if (v.length() > SIMD_EPSILON)
33 {
34 n = v.normalized();
35 }
36 return n;
37}
38
46{
47public:
49 {
50 m_me = me;
51 }
52
53 virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
54 {
55 if (rayResult.m_collisionObject == m_me)
56 return 1.0;
57
58 return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
59 }
60
61protected:
63};
64
66{
67public:
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)
70 {
71 }
72
73 virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
74 {
75 if (convexResult.m_hitCollisionObject == m_me)
76 return btScalar(1.0);
77
78 if (!convexResult.m_hitCollisionObject->hasContactResponse())
79 return btScalar(1.0);
80
81 btVector3 hitNormalWorld;
82 if (normalInWorldSpace)
83 {
84 hitNormalWorld = convexResult.m_hitNormalLocal;
85 }
86 else
87 {
89 hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal;
90 }
91
92 btScalar dotUp = m_up.dot(hitNormalWorld);
93 if (dotUp < m_minSlopeDot)
94 {
95 return btScalar(1.0);
96 }
97
98 return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
99 }
100
101protected:
105};
106
107/*
108 * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
109 *
110 * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
111 */
113{
114 return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
115}
116
117/*
118 * Returns the portion of 'direction' that is parallel to 'normal'
119 */
121{
122 btScalar magnitude = direction.dot(normal);
123 return normal * magnitude;
124}
125
126/*
127 * Returns the portion of 'direction' that is perpindicular to 'normal'
128 */
130{
131 return direction - parallelComponent(direction, normal);
132}
133
135{
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);
143 m_turnAngle = btScalar(0.0);
144 m_convexShape = convexShape;
145 m_useWalkDirection = true; // use walk direction by default, legacy behavior
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; // ?
153 m_wasOnGround = false;
154 m_wasJumping = false;
155 m_interpolateUp = true;
158 full_drop = false;
159 bounce_fix = false;
162
163 setUp(up);
164 setStepHeight(stepHeight);
165 setMaxSlope(btRadians(45.0));
166}
167
169{
170}
171
173{
174 return m_ghostObject;
175}
176
178{
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.
183 //
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
186
187 btVector3 minAabb, maxAabb;
190 minAabb,
191 maxAabb,
192 collisionWorld->getDispatcher());
193
194 bool penetration = false;
195
196 collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
197
199
200 // btScalar maxPen = btScalar(0.0);
201 for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
202 {
204
206
207 btCollisionObject* obj0 = static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject);
208 btCollisionObject* obj1 = static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject);
209
210 if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse()))
211 continue;
212
213 if (!needsCollision(obj0, obj1))
214 continue;
215
216 if (collisionPair->m_algorithm)
218
219 for (int j = 0; j < m_manifoldArray.size(); j++)
220 {
222 btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
223 for (int p = 0; p < manifold->getNumContacts(); p++)
224 {
225 const btManifoldPoint& pt = manifold->getContactPoint(p);
226
227 btScalar dist = pt.getDistance();
228
229 if (dist < -m_maxPenetrationDepth)
230 {
231 // TODO: cause problems on slopes, not sure if it is needed
232 //if (dist < maxPen)
233 //{
234 // maxPen = dist;
235 // m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
236
237 //}
238 m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
239 penetration = true;
240 }
241 else
242 {
243 //printf("touching %f\n", dist);
244 }
245 }
246
247 //manifold->clearManifold();
248 }
249 }
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 {
287 }
288 else
289 {
290 world->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
291 }
292
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
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 }
325
326 if (m_verticalOffset > 0)
327 {
328 m_verticalOffset = 0.0;
329 m_verticalVelocity = 0.0;
331 }
332 }
333 else
334 {
335 m_currentStepOffset = stepHeight;
337 }
338}
339
341{
342 bool collides = (body0->getBroadphaseHandle()->m_collisionFilterGroup & body1->getBroadphaseHandle()->m_collisionFilterMask) != 0;
343 collides = collides && (body1->getBroadphaseHandle()->m_collisionFilterGroup & body0->getBroadphaseHandle()->m_collisionFilterMask);
344 return collides;
345}
346
348{
349 btVector3 movementDirection = m_targetPosition - m_currentPosition;
350 btScalar movementLength = movementDirection.length();
351 if (movementLength > SIMD_EPSILON)
352 {
353 movementDirection.normalize();
354
355 btVector3 reflectDir = computeReflectionDirection(movementDirection, hitNormal);
356 reflectDir.normalize();
357
358 btVector3 parallelDir, perpindicularDir;
359
360 parallelDir = parallelComponent(reflectDir, hitNormal);
361 perpindicularDir = perpindicularComponent(reflectDir, hitNormal);
362
364 if (0) //tangentMag != 0.0)
365 {
366 btVector3 parComponent = parallelDir * btScalar(tangentMag * movementLength);
367 // printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
368 m_targetPosition += parComponent;
369 }
370
371 if (normalMag != 0.0)
372 {
373 btVector3 perpComponent = perpindicularDir * btScalar(normalMag * movementLength);
374 // printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
375 m_targetPosition += perpComponent;
376 }
377 }
378 else
379 {
380 // printf("movementLength don't normalize a zero vector\n");
381 }
382}
383
385{
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;
390
392
393 start.setIdentity();
394 end.setIdentity();
395
396 btScalar fraction = 1.0;
397 btScalar distance2 = (m_currentPosition - m_targetPosition).length2();
398 // printf("distance2=%f\n",distance2);
399
400 int maxIter = 10;
401
402 while (fraction > btScalar(0.01) && maxIter-- > 0)
403 {
406 btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
407
410
414
415 btScalar margin = m_convexShape->getMargin();
417
418 if (!(start == end))
419 {
421 {
422 m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
423 }
424 else
425 {
426 collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
427 }
428 }
429 m_convexShape->setMargin(margin);
430
431 fraction -= callback.m_closestHitFraction;
432
434 {
435 // we moved only a fraction
436 //btScalar hitDistance;
437 //hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
438
439 // m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
440
443 distance2 = currentDir.length2();
444 if (distance2 > SIMD_EPSILON)
445 {
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))
449 {
450 break;
451 }
452 }
453 else
454 {
455 // printf("currentDir: don't normalize a zero vector\n");
456 break;
457 }
458 }
459 else
460 {
462 }
463 }
464}
465
467{
468 btTransform start, end, end_double;
469 bool runonce = false;
470
471 // phase 3: down
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);*/
477
478 btVector3 orig_position = m_targetPosition;
479
480 btScalar downVelocity = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f) * dt;
481
482 if (m_verticalVelocity > 0.0)
483 return;
484
485 if (downVelocity > 0.0 && downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping))
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 }
525 else
526 {
527 collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
528
529 if (!callback.hasHit() && m_ghostObject->hasContactResponse())
530 {
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);
533 }
534 }
535
536 btScalar downVelocity2 = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f) * dt;
537 bool has_hit;
538 if (bounce_fix == true)
539 has_hit = (callback.hasHit() || callback2.hasHit()) && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject);
540 else
542
543 btScalar stepHeight = 0.0f;
544 if (m_verticalVelocity < 0.0)
545 stepHeight = m_stepHeight;
546
547 if (downVelocity2 > 0.0 && downVelocity2 < stepHeight && has_hit == true && runonce == false && (m_wasOnGround || !m_wasJumping))
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
577 }
578 else
580
581 full_drop = false;
582
583 m_verticalVelocity = 0.0;
584 m_verticalOffset = 0.0;
585 m_wasJumping = false;
586 }
587 else
588 {
589 // we dropped the full height
590
591 full_drop = true;
592
593 if (bounce_fix == true)
594 {
595 downVelocity = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f) * dt;
596 if (downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping))
597 {
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;
602 }
603 }
604 //printf("full drop - %g, %g\n", m_currentPosition.getY(), m_targetPosition.getY());
605
607 }
608}
609
611 const btVector3& walkDirection)
612{
613 m_useWalkDirection = true;
614 m_walkDirection = walkDirection;
616}
617
619 const btVector3& velocity,
620 btScalar timeInterval)
621{
622 // printf("setVelocity!\n");
623 // printf(" interval: %f\n", timeInterval);
624 // printf(" velocity: (%f, %f, %f)\n",
625 // velocity.x(), velocity.y(), velocity.z());
626
627 m_useWalkDirection = false;
628 m_walkDirection = velocity;
630 m_velocityTimeInterval += timeInterval;
631}
632
634{
635 m_AngVel = velocity;
636}
637
639{
640 return m_AngVel;
641}
642
644{
645 m_walkDirection = velocity;
646
647 // HACK: if we are moving in the direction of the up, treat it as a jump :(
648 if (m_walkDirection.length2() > 0)
649 {
650 btVector3 w = velocity.normalized();
651 btScalar c = w.dot(m_up);
652 if (c != 0)
653 {
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();
658
659 if (c > 0.0f)
660 {
661 m_wasJumping = true;
663 }
664 }
665 }
666 else
667 m_verticalVelocity = 0.0f;
668}
669
671{
673}
674
676{
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);
683
684 //clear pair cache
686 while (cache->getOverlappingPairArray().size() > 0)
687 {
688 cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher());
689 }
690}
691
693{
694 btTransform xform;
695 xform.setIdentity();
696 xform.setOrigin(origin);
698}
699
701{
704
707 // printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
708}
709
711{
712 // printf("playerStep(): ");
713 // printf(" dt = %f", dt);
714
715 if (m_AngVel.length2() > 0.0f)
716 {
718 }
719
720 // integrate for angular velocity
721 if (m_AngVel.length2() > 0.0f)
722 {
723 btTransform xform;
725
727
728 btQuaternion orn = rot * xform.getRotation();
729
730 xform.setRotation(orn);
732
737 }
738
739 // quick check...
741 {
742 // printf("\n");
743 return; // no motion
744 }
745
747
748 //btVector3 lvel = m_walkDirection;
749 //btScalar c = 0.0f;
750
751 if (m_walkDirection.length2() > 0)
752 {
753 // apply damping
755 }
756
758
759 // Update fall velocity.
762 {
764 }
766 {
768 }
770
771 btTransform xform;
773
774 // printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
775 // printf("walkSpeed=%f\n",walkSpeed);
776
777 stepUp(collisionWorld);
778 //todo: Experimenting with behavior of controller when it hits a ceiling..
779 //bool hitUp = stepUp (collisionWorld);
780 //if (hitUp)
781 //{
782 // m_verticalVelocity -= m_gravity * dt;
783 // if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
784 // {
785 // m_verticalVelocity = m_jumpSpeed;
786 // }
787 // if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
788 // {
789 // m_verticalVelocity = -btFabs(m_fallSpeed);
790 // }
791 // m_verticalOffset = m_verticalVelocity * dt;
792
793 // xform = m_ghostObject->getWorldTransform();
794 //}
795
797 {
798 stepForwardAndStrafe(collisionWorld, m_walkDirection);
799 }
800 else
801 {
802 //printf(" time: %f", m_velocityTimeInterval);
803 // still have some time left for moving!
804 btScalar dtMoving =
807
808 // how far will we move while we are moving?
809 btVector3 move = m_walkDirection * dtMoving;
810
811 //printf(" dtMoving: %f", dtMoving);
812
813 // okay, step
814 stepForwardAndStrafe(collisionWorld, move);
815 }
816 stepDown(collisionWorld, dt);
817
818 //todo: Experimenting with max jump height
819 //if (m_wasJumping)
820 //{
821 // btScalar ds = m_currentPosition[m_upAxis] - m_jumpPosition[m_upAxis];
822 // if (ds > m_maxJumpHeight)
823 // {
824 // // substract the overshoot
825 // m_currentPosition[m_upAxis] -= ds - m_maxJumpHeight;
826
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;
831 // }
832 //}
833 // printf("\n");
834
837
838 int numPenetrationLoops = 0;
839 m_touchingContact = false;
840 while (recoverFromPenetration(collisionWorld))
841 {
842 numPenetrationLoops++;
843 m_touchingContact = true;
844 if (numPenetrationLoops > 4)
845 {
846 //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
847 break;
848 }
849 }
850}
851
853{
854 m_fallSpeed = fallSpeed;
855}
856
858{
859 m_jumpSpeed = jumpSpeed;
861}
862
864{
865 m_maxJumpHeight = maxJumpHeight;
866}
867
869{
870 return onGround();
871}
872
874{
875 m_jumpSpeed = v.length2() == 0 ? m_SetjumpSpeed : v.length();
877 m_wasJumping = true;
878
879 m_jumpAxis = v.length2() == 0 ? m_up : v.normalized();
880
882
883#if 0
884 currently no jumping.
885 btTransform xform;
886 m_rigidBody->getMotionState()->getWorldTransform (xform);
887 btVector3 up = xform.getBasis()[1];
888 up.normalize ();
889 btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
890 m_rigidBody->applyCentralImpulse (up * magnitude);
891#endif
892}
893
895{
896 if (gravity.length2() > 0) setUpVector(-gravity);
897
898 m_gravity = gravity.length();
899}
900
902{
903 return -m_gravity * m_up;
904}
905
907{
908 m_maxSlopeRadians = slopeRadians;
909 m_maxSlopeCosine = btCos(slopeRadians);
910}
911
913{
914 return m_maxSlopeRadians;
915}
916
918{
920}
921
923{
925}
926
928{
929 return (fabs(m_verticalVelocity) < SIMD_EPSILON) && (fabs(m_verticalOffset) < SIMD_EPSILON);
930}
931
933{
934 m_stepHeight = h;
935}
936
938{
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)};
940
941 return sUpAxisDirection;
942}
943
945{
946}
947
949{
950 m_interpolateUp = value;
951}
952
954{
955 if (up.length2() > 0 && m_gravity > 0.0f)
956 {
958 return;
959 }
960
961 setUpVector(up);
962}
963
965{
966 if (m_up == up)
967 return;
968
969 btVector3 u = m_up;
970
971 if (up.length2() > 0)
972 m_up = up.normalized();
973 else
974 m_up = btVector3(0.0, 0.0, 0.0);
975
976 if (!m_ghostObject) return;
977 btQuaternion rot = getRotation(m_up, u);
978
979 //set orientation with new up
980 btTransform xform;
982 btQuaternion orn = rot.inverse() * xform.getRotation();
983 xform.setRotation(orn);
985}
986
988{
989 if (v0.length2() == 0.0f || v1.length2() == 0.0f)
990 {
991 btQuaternion q;
992 return q;
993 }
994
995 return shortestArcQuatNormalize2(v0, v1);
996}
static btVector3 getNormalizedVector(const btVector3 &v)
btQuaternion shortestArcQuatNormalize2(btVector3 &v0, btVector3 &v1)
Definition: btQuaternion.h:959
btScalar btPow(btScalar x, btScalar y)
Definition: btScalar.h:521
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition: btScalar.h:314
btScalar btSin(btScalar x)
Definition: btScalar.h:499
btScalar btFabs(btScalar x)
Definition: btScalar.h:497
btScalar btCos(btScalar x)
Definition: btScalar.h:498
btScalar btRadians(btScalar x)
Definition: btScalar.h:588
#define SIMD_EPSILON
Definition: btScalar.h:543
btScalar btAcos(btScalar x)
Definition: btScalar.h:501
#define SIMD_HALF_PI
Definition: btScalar.h:528
int size() const
return the number of elements in the array
void resize(int newsize, const T &fillData=T())
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
void setWorldTransform(const btTransform &worldTrans)
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...
Definition: btConvexShape.h:33
virtual void setMargin(btScalar margin)=0
virtual btScalar getMargin() const =0
void getAabb(const btTransform &t, btVector3 &aabbMin, btVector3 &aabbMax) const =0
getAabb's default implementation is brute force, expected derived classes to implement a fast dedicat...
virtual void dispatchAllCollisionPairs(btOverlappingPairCache *pairCache, const btDispatcherInfo &dispatchInfo, btDispatcher *dispatcher)=0
void convexSweepTest(const class btConvexShape *castShape, const btTransform &convexFromWorld, const btTransform &convexToWorld, btCollisionWorld::ConvexResultCallback &resultCallback, btScalar allowedCcdPenetration=0.f) const
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.
Definition: btIDebugDraw.h:27
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 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 jump(const btVector3 &v=btVector3(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
btHashedOverlappingPairCache * getOverlappingPairCache()
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...
Definition: btQuaternion.h:50
btQuaternion inverse() const
Return the inverse of this quaternion.
Definition: btQuaternion.h:497
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition: btTransform.h:30
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
Definition: btTransform.h:109
void setRotation(const btQuaternion &q)
Set the rotational element by btQuaternion.
Definition: btTransform.h:161
void setIdentity()
Set this transformation to the identity.
Definition: btTransform.h:167
btQuaternion getRotation() const
Return a quaternion representing the rotation.
Definition: btTransform.h:119
btVector3 & getOrigin()
Return the origin vector translation.
Definition: btTransform.h:114
void setOrigin(const btVector3 &origin)
Set the translational element.
Definition: btTransform.h:147
btVector3 can be used to represent 3D points and vectors.
Definition: btVector3.h:82
void setInterpolate3(const btVector3 &v0, const btVector3 &v1, btScalar rt)
Definition: btVector3.h:492
btScalar length() const
Return the length of the vector.
Definition: btVector3.h:257
btScalar dot(const btVector3 &v) const
Return the dot product.
Definition: btVector3.h:229
void setValue(const btScalar &_x, const btScalar &_y, const btScalar &_z)
Definition: btVector3.h:640
btVector3 normalized() const
Return a normalized version of this vector.
Definition: btVector3.h:949
btScalar length2() const
Return the length of the vector squared.
Definition: btVector3.h:251
const btScalar & getY() const
Return the y value.
Definition: btVector3.h:563
bool fuzzyZero() const
Definition: btVector3.h:688
btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition: btVector3.h:303
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
Definition: btDispatcher.h:62