Blender V4.3
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
28getNormalizedVector(const btVector3& v)
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:
48 btKinematicClosestNotMeRayResultCallback(btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
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:
62 btCollisionObject* m_me;
63};
64
66{
67public:
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)
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:
102 btCollisionObject* m_me;
103 const btVector3 m_up;
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 */
112btVector3 btKinematicCharacterController::computeReflectionDirection(const btVector3& direction, const btVector3& normal)
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 */
120btVector3 btKinematicCharacterController::parallelComponent(const btVector3& direction, const btVector3& normal)
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 */
129btVector3 btKinematicCharacterController::perpindicularComponent(const btVector3& direction, const btVector3& normal)
130{
131 return direction - parallelComponent(direction, normal);
132}
133
134btKinematicCharacterController::btKinematicCharacterController(btPairCachingGhostObject* ghostObject, btConvexShape* convexShape, btScalar stepHeight, const btVector3& up)
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
168btKinematicCharacterController::~btKinematicCharacterController()
169{
170}
171
172btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
173{
174 return m_ghostObject;
175}
176
177bool btKinematicCharacterController::recoverFromPenetration(btCollisionWorld* collisionWorld)
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;
188 m_convexShape->getAabb(m_ghostObject->getWorldTransform(), minAabb, maxAabb);
189 collisionWorld->getBroadphase()->setAabb(m_ghostObject->getBroadphaseHandle(),
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
198 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
199
200 // btScalar maxPen = btScalar(0.0);
201 for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
202 {
204
205 btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
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)
217 collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
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 }
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]);
254 return penetration;
255}
256
257void btKinematicCharacterController::stepUp(btCollisionWorld* world)
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 */
270 start.setOrigin(m_currentPosition);
271
274
275 end.setOrigin(m_targetPosition);
276
277 start.setRotation(m_currentOrientation);
278 end.setRotation(m_targetOrientation);
279
281 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
282 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
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)
301 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
302 else
304 }
305
306 btTransform& xform = m_ghostObject->getWorldTransform();
307 xform.setOrigin(m_currentPosition);
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 }
333 else
334 {
335 m_currentStepOffset = stepHeight;
337 }
338}
339
340bool btKinematicCharacterController::needsCollision(const btCollisionObject* body0, const btCollisionObject* body1)
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
347void btKinematicCharacterController::updateTargetPositionBasedOnCollision(const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
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
384void btKinematicCharacterController::stepForwardAndStrafe(btCollisionWorld* collisionWorld, const btVector3& walkMove)
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;
398 // printf("distance2=%f\n",distance2);
399
400 int maxIter = 10;
401
402 while (fraction > btScalar(0.01) && maxIter-- > 0)
403 {
404 start.setOrigin(m_currentPosition);
405 end.setOrigin(m_targetPosition);
406 btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
407
408 start.setRotation(m_currentOrientation);
409 end.setRotation(m_targetOrientation);
410
412 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
413 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
414
415 btScalar margin = m_convexShape->getMargin();
416 m_convexShape->setMargin(margin + m_addedMargin);
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
433 if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))
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
442 btVector3 currentDir = m_targetPosition - m_currentPosition;
443 distance2 = currentDir.length2();
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
466void btKinematicCharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt)
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
492 callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
493 callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
494
496 callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
497 callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
498
499 while (1)
500 {
501 start.setIdentity();
502 end.setIdentity();
503
504 end_double.setIdentity();
505
506 start.setOrigin(m_currentPosition);
507 end.setOrigin(m_targetPosition);
508
509 start.setRotation(m_currentOrientation);
510 end.setRotation(m_targetOrientation);
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
541 has_hit = callback2.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback2.m_hitCollisionObject);
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)
573 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
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
579 m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
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
610void btKinematicCharacterController::setWalkDirection(
611 const btVector3& walkDirection)
612{
613 m_useWalkDirection = true;
614 m_walkDirection = walkDirection;
616}
617
618void btKinematicCharacterController::setVelocityForTimeInterval(
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
633void btKinematicCharacterController::setAngularVelocity(const btVector3& velocity)
634{
635 m_AngVel = velocity;
636}
637
638const btVector3& btKinematicCharacterController::getAngularVelocity() const
639{
640 return m_AngVel;
641}
642
643void btKinematicCharacterController::setLinearVelocity(const btVector3& velocity)
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;
662 m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin();
663 }
664 }
665 }
666 else
667 m_verticalVelocity = 0.0f;
668}
669
670btVector3 btKinematicCharacterController::getLinearVelocity() const
671{
673}
674
675void btKinematicCharacterController::reset(btCollisionWorld* collisionWorld)
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
692void btKinematicCharacterController::warp(const btVector3& origin)
693{
694 btTransform xform;
695 xform.setIdentity();
696 xform.setOrigin(origin);
697 m_ghostObject->setWorldTransform(xform);
698}
699
700void btKinematicCharacterController::preStep(btCollisionWorld* collisionWorld)
701{
702 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
704
705 m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
707 // printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
708}
709
710void btKinematicCharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt)
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;
724 xform = m_ghostObject->getWorldTransform();
725
726 btQuaternion rot(m_AngVel.normalized(), m_AngVel.length() * dt);
727
728 btQuaternion orn = rot * xform.getRotation();
729
730 xform.setRotation(orn);
731 m_ghostObject->setWorldTransform(xform);
732
733 m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
735 m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
737 }
738
739 // quick check...
740 if (!m_useWalkDirection && (m_velocityTimeInterval <= 0.0 || m_walkDirection.fuzzyZero()))
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;
772 xform = m_ghostObject->getWorldTransform();
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
835 xform.setOrigin(m_currentPosition);
836 m_ghostObject->setWorldTransform(xform);
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
852void btKinematicCharacterController::setFallSpeed(btScalar fallSpeed)
853{
854 m_fallSpeed = fallSpeed;
855}
856
857void btKinematicCharacterController::setJumpSpeed(btScalar jumpSpeed)
858{
859 m_jumpSpeed = jumpSpeed;
861}
862
863void btKinematicCharacterController::setMaxJumpHeight(btScalar maxJumpHeight)
864{
865 m_maxJumpHeight = maxJumpHeight;
866}
867
868bool btKinematicCharacterController::canJump() const
869{
870 return onGround();
871}
872
873void btKinematicCharacterController::jump(const btVector3& v)
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
881 m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin();
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
894void btKinematicCharacterController::setGravity(const btVector3& gravity)
895{
896 if (gravity.length2() > 0) setUpVector(-gravity);
897
898 m_gravity = gravity.length();
899}
900
901btVector3 btKinematicCharacterController::getGravity() const
902{
903 return -m_gravity * m_up;
904}
905
906void btKinematicCharacterController::setMaxSlope(btScalar slopeRadians)
907{
908 m_maxSlopeRadians = slopeRadians;
909 m_maxSlopeCosine = btCos(slopeRadians);
910}
911
912btScalar btKinematicCharacterController::getMaxSlope() const
913{
914 return m_maxSlopeRadians;
915}
916
917void btKinematicCharacterController::setMaxPenetrationDepth(btScalar d)
918{
920}
921
922btScalar btKinematicCharacterController::getMaxPenetrationDepth() const
923{
925}
926
927bool btKinematicCharacterController::onGround() const
928{
930}
931
932void btKinematicCharacterController::setStepHeight(btScalar h)
933{
934 m_stepHeight = h;
935}
936
937btVector3* btKinematicCharacterController::getUpAxisDirections()
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
944void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)
945{
946}
947
948void btKinematicCharacterController::setUpInterpolate(bool value)
949{
950 m_interpolateUp = value;
951}
952
953void btKinematicCharacterController::setUp(const btVector3& up)
954{
955 if (up.length2() > 0 && m_gravity > 0.0f)
956 {
957 setGravity(-m_gravity * up.normalized());
958 return;
959 }
960
961 setUpVector(up);
962}
963
964void btKinematicCharacterController::setUpVector(const btVector3& up)
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;
978
979 //set orientation with new up
980 btTransform xform;
981 xform = m_ghostObject->getWorldTransform();
982 btQuaternion orn = rot.inverse() * xform.getRotation();
983 xform.setRotation(orn);
984 m_ghostObject->setWorldTransform(xform);
985}
986
987btQuaternion btKinematicCharacterController::getRotation(btVector3& v0, btVector3& v1) const
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}
ATTR_WARN_UNUSED_RESULT const BMVert * v
btBroadphasePair
btConvexShape()
not supported on IBM SDK, until we fix the alignment of btVector3
static btVector3 getNormalizedVector(const btVector3 &v)
btVector3 m_jumpAxis
btScalar m_maxSlopeRadians
btConvexShape * m_convexShape
void setUp(const btVector3 &up)
void setUpVector(const btVector3 &up)
btVector3 perpindicularComponent(const btVector3 &direction, const btVector3 &normal)
btScalar m_fallSpeed
btVector3 m_targetPosition
btScalar m_jumpSpeed
btScalar m_angularDamping
btScalar m_linearDamping
btScalar m_turnAngle
btVector3 computeReflectionDirection(const btVector3 &direction, const btVector3 &normal)
btScalar m_velocityTimeInterval
btScalar m_addedMargin
btScalar m_maxPenetrationDepth
void stepDown(btCollisionWorld *collisionWorld, btScalar dt)
bool onGround() const
btScalar m_stepHeight
btQuaternion m_targetOrientation
btManifoldArray m_manifoldArray
keep track of the contact manifolds
btVector3 m_jumpPosition
btVector3 m_walkDirection
this is the desired walk direction, set by the user
void stepForwardAndStrafe(btCollisionWorld *collisionWorld, const btVector3 &walkMove)
btScalar m_maxSlopeCosine
void setMaxSlope(btScalar slopeRadians)
void setStepHeight(btScalar h)
void setGravity(const btVector3 &gravity)
btScalar m_currentStepOffset
void updateTargetPositionBasedOnCollision(const btVector3 &hit_normal, btScalar tangentMag=btScalar(0.0), btScalar normalMag=btScalar(1.0))
void stepUp(btCollisionWorld *collisionWorld)
btVector3 m_AngVel
btPairCachingGhostObject * getGhostObject()
virtual bool needsCollision(const btCollisionObject *body0, const btCollisionObject *body1)
bool m_useGhostObjectSweepTest
btScalar m_SetjumpSpeed
btQuaternion m_currentOrientation
btScalar m_verticalVelocity
btVector3 m_normalizedDirection
btVector3 m_currentPosition
btVector3 parallelComponent(const btVector3 &direction, const btVector3 &normal)
bool recoverFromPenetration(btCollisionWorld *collisionWorld)
btScalar m_verticalOffset
btScalar m_maxJumpHeight
btPairCachingGhostObject * m_ghostObject
btHashedOverlappingPairCache()
int getNumOverlappingPairs() const
btPersistentManifold()
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
SIMD_FORCE_INLINE btQuaternion shortestArcQuatNormalize2(btVector3 &v0, btVector3 &v1)
SIMD_FORCE_INLINE btScalar btAcos(btScalar x)
Definition btScalar.h:501
SIMD_FORCE_INLINE btScalar btCos(btScalar x)
Definition btScalar.h:498
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition btScalar.h:314
SIMD_FORCE_INLINE btScalar btSin(btScalar x)
Definition btScalar.h:499
SIMD_FORCE_INLINE btScalar btFabs(btScalar x)
Definition btScalar.h:497
SIMD_FORCE_INLINE btScalar btRadians(btScalar x)
Definition btScalar.h:588
#define SIMD_EPSILON
Definition btScalar.h:543
#define SIMD_HALF_PI
Definition btScalar.h:528
SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y)
Definition btScalar.h:521
btQuaternion getRotation() const
Return a quaternion representing the rotation.
btTransform
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition btTransform.h:30
SIMD_FORCE_INLINE btScalar length2() const
Return the length of the vector squared.
Definition btVector3.h:251
btVector3
btVector3 can be used to represent 3D points and vectors. It has an un-used w component to suit 16-by...
Definition btVector3.h:82
SIMD_FORCE_INLINE btScalar distance2(const btVector3 &v) const
Return the distance squared between the ends of this and another vector This is symantically treating...
Definition btVector3.h:939
SIMD_FORCE_INLINE int size() const
return the number of elements in the array
SIMD_FORCE_INLINE void resize(int newsize, const T &fillData=T())
virtual void setAabb(btBroadphaseProxy *proxy, const btVector3 &aabbMin, const btVector3 &aabbMax, btDispatcher *dispatcher)=0
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
const btBroadphaseInterface * getBroadphase() const
virtual void dispatchAllCollisionPairs(btOverlappingPairCache *pairCache, const btDispatcherInfo &dispatchInfo, btDispatcher *dispatcher)=0
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace)
btKinematicClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot)
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace)
btScalar getDistance() const
btVector3 m_normalWorldOnB
btHashedOverlappingPairCache * getOverlappingPairCache()
The btQuaternion implements quaternion to perform linear algebra rotations in combination with btMatr...
btQuaternion inverse() const
Return the inverse of this quaternion.
void setRotation(const btVector3 &axis, const btScalar &_angle)
Set the rotation using axis angle notation.
DEGForeachIDComponentCallback callback
#define rot(x, k)
ccl_device_inline float2 fabs(const float2 a)
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