Bullet Collision Detection & Physics Library
btKinematicCharacterController.cpp
Go to the documentation of this file.
1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
4 
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
10 
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15 
16 
17 #include <stdio.h>
26 
27 
28 // static helper method
29 static 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 {
49 public:
51  {
52  m_me = me;
53  }
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  }
62 protected:
64 };
65 
67 {
68 public:
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  }
102 protected:
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);
143  m_useGhostObjectSweepTest = true;
144  m_turnAngle = btScalar(0.0);
145  m_convexShape=convexShape;
146  m_useWalkDirection = true; // use walk direction by default, legacy behavior
147  m_velocityTimeInterval = 0.0;
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; // ?
153  m_SetjumpSpeed = m_jumpSpeed;
154  m_wasOnGround = false;
155  m_wasJumping = false;
156  m_interpolateUp = true;
157  m_currentStepOffset = 0.0;
158  m_maxPenetrationDepth = 0.2;
159  full_drop = false;
160  bounce_fix = false;
161  m_linearDamping = btScalar(0.0);
162  m_angularDamping = btScalar(0.0);
163 
164  setUp(up);
165  setStepHeight(stepHeight);
166  setMaxSlope(btRadians(45.0));
167 }
168 
170 {
171 }
172 
174 {
175  return m_ghostObject;
176 }
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)
218  collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
219 
220 
221  for (int j=0;j<m_manifoldArray.size();j++)
222  {
223  btPersistentManifold* manifold = m_manifoldArray[j];
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();
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 
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 
272  m_targetPosition = m_currentPosition + m_up * (stepHeight) + m_jumpAxis * ((m_verticalOffset > 0.f ? m_verticalOffset : 0.f));
273  m_currentPosition = m_targetPosition;
274 
275  end.setOrigin (m_targetPosition);
276 
277  start.setRotation(m_currentOrientation);
278  end.setRotation(m_targetOrientation);
279 
280  btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, -m_up, m_maxSlopeCosine);
281  callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
282  callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
283 
284  if (m_useGhostObjectSweepTest)
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
303  m_currentPosition = m_targetPosition;
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();
324  m_currentPosition = m_targetPosition;
325 
326  if (m_verticalOffset > 0)
327  {
328  m_verticalOffset = 0.0;
329  m_verticalVelocity = 0.0;
330  m_currentStepOffset = m_stepHeight;
331  }
332  } else {
333  m_currentStepOffset = stepHeight;
334  m_currentPosition = m_targetPosition;
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 
361  m_targetPosition = m_currentPosition;
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 
388  m_targetPosition = m_currentPosition + walkMove;
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  {
401  start.setOrigin (m_currentPosition);
402  end.setOrigin (m_targetPosition);
403  btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
404 
405  start.setRotation(m_currentOrientation);
406  end.setRotation(m_targetOrientation);
407 
408  btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0));
409  callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
410  callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
411 
412 
413  btScalar margin = m_convexShape->getMargin();
414  m_convexShape->setMargin(margin + m_addedMargin);
415 
416  if (!(start == end))
417  {
418  if (m_useGhostObjectSweepTest)
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 
440  updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
441  btVector3 currentDir = m_targetPosition - m_currentPosition;
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  {
460  m_currentPosition = m_targetPosition;
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
485  && (m_wasOnGround || !m_wasJumping))
486  downVelocity = m_fallSpeed;
487 
488  btVector3 step_drop = m_up * (m_currentStepOffset + downVelocity);
489  m_targetPosition -= step_drop;
490 
491  btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, m_up, m_maxSlopeCosine);
492  callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
493  callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
494 
495  btKinematicClosestNotMeConvexResultCallback callback2(m_ghostObject, m_up, m_maxSlopeCosine);
496  callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
497  callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
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 
515  if (m_useGhostObjectSweepTest)
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
547  && (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  } 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 
604  m_currentPosition = m_targetPosition;
605  }
606 }
607 
608 
609 
611 (
612 const btVector3& walkDirection
613 )
614 {
615  m_useWalkDirection = true;
616  m_walkDirection = walkDirection;
617  m_normalizedDirection = getNormalizedVector(m_walkDirection);
618 }
619 
620 
621 
623 (
624 const btVector3& velocity,
625 btScalar 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;
635  m_normalizedDirection = getNormalizedVector(m_walkDirection);
636  m_velocityTimeInterval += timeInterval;
637 }
638 
640 {
641  m_AngVel = velocity;
642 }
643 
645 {
646  return m_AngVel;
647 }
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 
677 {
678  return m_walkDirection + (m_verticalVelocity * m_up);
679 }
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);
688  m_velocityTimeInterval = 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();
710  m_targetPosition = m_currentPosition;
711 
712  m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
713  m_targetOrientation = m_currentOrientation;
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  {
724  m_AngVel *= btPow(btScalar(1) - m_angularDamping, dt);
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();
741  m_targetPosition = m_currentPosition;
742  m_currentOrientation = m_ghostObject->getWorldTransform().getRotation();
743  m_targetOrientation = m_currentOrientation;
744  }
745 
746  // quick check...
747  if (!m_useWalkDirection && (m_velocityTimeInterval <= 0.0)) {
748 // printf("\n");
749  return; // no motion
750  }
751 
752  m_wasOnGround = onGround();
753 
754  //btVector3 lvel = m_walkDirection;
755  //btScalar c = 0.0f;
756 
757  if (m_walkDirection.length2() > 0)
758  {
759  // apply damping
760  m_walkDirection *= btPow(btScalar(1) - m_linearDamping, dt);
761  }
762 
763  m_verticalVelocity *= btPow(btScalar(1) - m_linearDamping, dt);
764 
765  // Update fall velocity.
766  m_verticalVelocity -= m_gravity * dt;
767  if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
768  {
769  m_verticalVelocity = m_jumpSpeed;
770  }
771  if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
772  {
773  m_verticalVelocity = -btFabs(m_fallSpeed);
774  }
775  m_verticalOffset = m_verticalVelocity * dt;
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 =
808  (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval;
809  m_velocityTimeInterval -= dt;
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 
838  xform.setOrigin (m_currentPosition);
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 
861 {
862  m_jumpSpeed = jumpSpeed;
863  m_SetjumpSpeed = m_jumpSpeed;
864 }
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();
879  m_verticalVelocity = m_jumpSpeed;
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 
905 {
906  return -m_gravity * m_up;
907 }
908 
910 {
911  m_maxSlopeRadians = slopeRadians;
912  m_maxSlopeCosine = btCos(slopeRadians);
913 }
914 
916 {
917  return m_maxSlopeRadians;
918 }
919 
921 {
922  m_maxPenetrationDepth = d;
923 }
924 
926 {
927  return m_maxPenetrationDepth;
928 }
929 
931 {
932  return (fabs(m_verticalVelocity) < SIMD_EPSILON) && (fabs(m_verticalOffset) < SIMD_EPSILON);
933 }
934 
936 {
937  m_stepHeight = h;
938 }
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 
948 {
949 }
950 
952 {
953  m_interpolateUp = value;
954 }
955 
957 {
958  if (up.length2() > 0 && m_gravity > 0.0f)
959  {
960  setGravity(-m_gravity * up.normalized());
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 
void setOrigin(const btVector3 &origin)
Set the translational element.
Definition: btTransform.h:150
#define SIMD_EPSILON
Definition: btScalar.h:521
btPersistentManifold is a contact point cache, it stays persistent as long as objects are overlapping...
void playerStep(btCollisionWorld *collisionWorld, btScalar dt)
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace)
void stepDown(btCollisionWorld *collisionWorld, btScalar dt)
btScalar btRadians(btScalar x)
Definition: btScalar.h:566
btQuaternion getRotation() const
Return a quaternion representing the rotation.
Definition: btTransform.h:122
btQuaternion getRotation(btVector3 &v0, btVector3 &v1) const
btScalar btSin(btScalar x)
Definition: btScalar.h:477
virtual void dispatchAllCollisionPairs(btOverlappingPairCache *pairCache, const btDispatcherInfo &dispatchInfo, btDispatcher *dispatcher)=0
btVector3 computeReflectionDirection(const btVector3 &direction, const btVector3 &normal)
void setIdentity()
Set this transformation to the identity.
Definition: btTransform.h:172
btBroadphasePairArray & getOverlappingPairArray()
ManifoldContactPoint collects and maintains persistent contactpoints.
const btCollisionObject * getBody0() const
virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval)
Caller provides a velocity with which the character should move for the given time period...
#define SIMD_HALF_PI
Definition: btScalar.h:506
void debugDraw(btIDebugDraw *debugDrawer)
btActionInterface interface
btScalar dot(const btVector3 &v) const
Return the dot product.
Definition: btVector3.h:235
bool hasContactResponse() const
btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition: btVector3.h:309
The btConvexShape is an abstract shape interface, implemented by all convex shapes such as btBoxShape...
Definition: btConvexShape.h:31
const btCollisionObject * m_hitCollisionObject
btTransform & getWorldTransform()
btVector3 m_normalWorldOnB
int size() const
return the number of elements in the array
btBroadphaseProxy * getBroadphaseHandle()
static btVector3 getNormalizedVector(const btVector3 &v)
virtual void setLinearVelocity(const btVector3 &velocity)
btQuaternion shortestArcQuatNormalize2(btVector3 &v0, btVector3 &v1)
Definition: btQuaternion.h:970
void setMaxSlope(btScalar slopeRadians)
The max slope determines the maximum angle that the controller can walk up.
const btScalar & getY() const
Return the y value.
Definition: btVector3.h:575
btCollisionObject can be used to manage collision detection objects.
void setRotation(const btQuaternion &q)
Set the rotational element by btQuaternion.
Definition: btTransform.h:165
bool recoverFromPenetration(btCollisionWorld *collisionWorld)
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
Definition: btTransform.h:112
The btIDebugDraw interface class allows hooking up a debug renderer to visually debug simulations...
Definition: btIDebugDraw.h:29
btQuaternion inverse() const
Return the inverse of this quaternion.
Definition: btQuaternion.h:500
btScalar length() const
Return the length of the vector.
Definition: btVector3.h:263
const btCollisionObject * m_collisionObject
const btManifoldPoint & getContactPoint(int index) const
btDispatcher * getDispatcher()
btVector3 parallelComponent(const btVector3 &direction, const btVector3 &normal)
btBroadphaseProxy * m_pProxy1
btCollisionAlgorithm * m_algorithm
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace)
btVector3 can be used to represent 3D points and vectors.
Definition: btVector3.h:83
virtual void * removeOverlappingPair(btBroadphaseProxy *proxy0, btBroadphaseProxy *proxy1, btDispatcher *dispatcher)
btScalar btPow(btScalar x, btScalar y)
Definition: btScalar.h:499
virtual void getAllContactManifolds(btManifoldArray &manifoldArray)=0
btScalar length2() const
Return the length of the vector squared.
Definition: btVector3.h:257
btScalar btAcos(btScalar x)
Definition: btScalar.h:479
virtual bool needsCollision(btBroadphaseProxy *proxy0) const
btBroadphaseProxy * m_pProxy0
const btBroadphaseInterface * getBroadphase() const
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition: btTransform.h:34
void updateTargetPositionBasedOnCollision(const btVector3 &hit_normal, btScalar tangentMag=btScalar(0.0), btScalar normalMag=btScalar(1.0))
CollisionWorld is interface and container for the collision detection.
btDispatcherInfo & getDispatchInfo()
void stepUp(btCollisionWorld *collisionWorld)
btVector3 normalized() const
Return a normalized version of this vector.
Definition: btVector3.h:966
btScalar m_allowedCcdPenetration
Definition: btDispatcher.h:63
virtual void setAngularVelocity(const btVector3 &velocity)
btVector3 perpindicularComponent(const btVector3 &direction, const btVector3 &normal)
void reset(btCollisionWorld *collisionWorld)
btKinematicClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot)
virtual void setAabb(btBroadphaseProxy *proxy, const btVector3 &aabbMin, const btVector3 &aabbMax, btDispatcher *dispatcher)=0
void stepForwardAndStrafe(btCollisionWorld *collisionWorld, const btVector3 &walkMove)
void preStep(btCollisionWorld *collisionWorld)
The btQuaternion implements quaternion to perform linear algebra rotations in combination with btMatr...
Definition: btQuaternion.h:55
virtual void setWalkDirection(const btVector3 &walkDirection)
This should probably be called setPositionIncrementPerSimulatorStep.
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...
void jump(const btVector3 &v=btVector3(0, 0, 0))
btScalar getDistance() const
virtual bool needsCollision(const btCollisionObject *body0, const btCollisionObject *body1)
btKinematicCharacterController(btPairCachingGhostObject *ghostObject, btConvexShape *convexShape, btScalar stepHeight, const btVector3 &up=btVector3(1.0, 0.0, 0.0))
void setInterpolate3(const btVector3 &v0, const btVector3 &v1, btScalar rt)
Definition: btVector3.h:503
virtual const btVector3 & getAngularVelocity() const
Hash-space based Pair Cache, thanks to Erin Catto, Box2D, http://www.box2d.org, and Pierre Terdiman...
ClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld)
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition: btScalar.h:292
btScalar btCos(btScalar x)
Definition: btScalar.h:476
btScalar btFabs(btScalar x)
Definition: btScalar.h:475
The btBroadphasePair class contains a pair of aabb-overlapping objects.