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 
25 
26 
27 // static helper method
28 static btVector3
30 {
31  btVector3 n = v.normalized();
32  if (n.length() < SIMD_EPSILON) {
33  n.setValue(0, 0, 0);
34  }
35  return n;
36 }
37 
38 
46 {
47 public:
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 protected:
62 };
63 
65 {
66 public:
68  : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
69  , m_me(me)
70  , m_up(up)
71  , m_minSlopeDot(minSlopeDot)
72  {
73  }
74 
75  virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
76  {
77  if (convexResult.m_hitCollisionObject == m_me)
78  return btScalar(1.0);
79 
80  btVector3 hitNormalWorld;
81  if (normalInWorldSpace)
82  {
83  hitNormalWorld = convexResult.m_hitNormalLocal;
84  } else
85  {
87  hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
88  }
89 
90  btScalar dotUp = m_up.dot(hitNormalWorld);
91  if (dotUp < m_minSlopeDot) {
92  return btScalar(1.0);
93  }
94 
95  return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
96  }
97 protected:
99  const btVector3 m_up;
101 };
102 
103 /*
104  * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
105  *
106  * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
107  */
109 {
110  return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
111 }
112 
113 /*
114  * Returns the portion of 'direction' that is parallel to 'normal'
115  */
117 {
118  btScalar magnitude = direction.dot(normal);
119  return normal * magnitude;
120 }
121 
122 /*
123  * Returns the portion of 'direction' that is perpindicular to 'normal'
124  */
126 {
127  return direction - parallelComponent(direction, normal);
128 }
129 
131 {
132  m_upAxis = upAxis;
133  m_addedMargin = 0.02;
134  m_walkDirection.setValue(0,0,0);
136  m_ghostObject = ghostObject;
137  m_stepHeight = stepHeight;
138  m_turnAngle = btScalar(0.0);
139  m_convexShape=convexShape;
140  m_useWalkDirection = true; // use walk direction by default, legacy behavior
142  m_verticalVelocity = 0.0;
143  m_verticalOffset = 0.0;
144  m_gravity = 9.8 * 3 ; // 3G acceleration.
145  m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s.
146  m_jumpSpeed = 10.0; // ?
147  m_wasOnGround = false;
148  m_wasJumping = false;
149  setMaxSlope(btRadians(45.0));
150 }
151 
153 {
154 }
155 
157 {
158  return m_ghostObject;
159 }
160 
162 {
163  // Here we must refresh the overlapping paircache as the penetrating movement itself or the
164  // previous recovery iteration might have used setWorldTransform and pushed us into an object
165  // that is not in the previous cache contents from the last timestep, as will happen if we
166  // are pushed into a new AABB overlap. Unhandled this means the next convex sweep gets stuck.
167  //
168  // Do this by calling the broadphase's setAabb with the moved AABB, this will update the broadphase
169  // paircache and the ghostobject's internal paircache at the same time. /BW
170 
171  btVector3 minAabb, maxAabb;
174  minAabb,
175  maxAabb,
176  collisionWorld->getDispatcher());
177 
178  bool penetration = false;
179 
180  collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
181 
183 
184  btScalar maxPen = btScalar(0.0);
185  for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
186  {
188 
190 
191  if (collisionPair->m_algorithm)
193 
194 
195  for (int j=0;j<m_manifoldArray.size();j++)
196  {
197  btPersistentManifold* manifold = m_manifoldArray[j];
198  btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
199  for (int p=0;p<manifold->getNumContacts();p++)
200  {
201  const btManifoldPoint&pt = manifold->getContactPoint(p);
202 
203  btScalar dist = pt.getDistance();
204 
205  if (dist < 0.0)
206  {
207  if (dist < maxPen)
208  {
209  maxPen = dist;
210  m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
211 
212  }
213  m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
214  penetration = true;
215  } else {
216  //printf("touching %f\n", dist);
217  }
218  }
219 
220  //manifold->clearManifold();
221  }
222  }
224  newTrans.setOrigin(m_currentPosition);
225  m_ghostObject->setWorldTransform(newTrans);
226 // printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
227  return penetration;
228 }
229 
231 {
232  // phase 1: up
233  btTransform start, end;
235 
236  start.setIdentity ();
237  end.setIdentity ();
238 
239  /* FIXME: Handle penetration properly */
242 
246 
248  {
250  }
251  else
252  {
253  world->convexSweepTest (m_convexShape, start, end, callback);
254  }
255 
256  if (callback.hasHit())
257  {
258  // Only modify the position if the hit was a slope and not a wall or ceiling.
259  if(callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > 0.0)
260  {
261  // we moved up only a fraction of the step height
264  }
265  m_verticalVelocity = 0.0;
266  m_verticalOffset = 0.0;
267  } else {
270  }
271 }
272 
274 {
275  btVector3 movementDirection = m_targetPosition - m_currentPosition;
276  btScalar movementLength = movementDirection.length();
277  if (movementLength>SIMD_EPSILON)
278  {
279  movementDirection.normalize();
280 
281  btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
282  reflectDir.normalize();
283 
284  btVector3 parallelDir, perpindicularDir;
285 
286  parallelDir = parallelComponent (reflectDir, hitNormal);
287  perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
288 
290  if (0)//tangentMag != 0.0)
291  {
292  btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
293 // printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
294  m_targetPosition += parComponent;
295  }
296 
297  if (normalMag != 0.0)
298  {
299  btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
300 // printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
301  m_targetPosition += perpComponent;
302  }
303  } else
304  {
305 // printf("movementLength don't normalize a zero vector\n");
306  }
307 }
308 
310 {
311  // printf("m_normalizedDirection=%f,%f,%f\n",
312  // m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
313  // phase 2: forward and strafe
314  btTransform start, end;
315  m_targetPosition = m_currentPosition + walkMove;
316 
317  start.setIdentity ();
318  end.setIdentity ();
319 
320  btScalar fraction = 1.0;
321  btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
322 // printf("distance2=%f\n",distance2);
323 
324  if (m_touchingContact)
325  {
327  {
329  }
330  }
331 
332  int maxIter = 10;
333 
334  while (fraction > btScalar(0.01) && maxIter-- > 0)
335  {
338  btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
339 
340  btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0));
343 
344 
345  btScalar margin = m_convexShape->getMargin();
347 
348 
350  {
351  m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
352  } else
353  {
354  collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
355  }
356 
357  m_convexShape->setMargin(margin);
358 
359 
360  fraction -= callback.m_closestHitFraction;
361 
362  if (callback.hasHit())
363  {
364  // we moved only a fraction
365  btScalar hitDistance;
366  hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
367 
368 // m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
369 
372  distance2 = currentDir.length2();
373  if (distance2 > SIMD_EPSILON)
374  {
375  currentDir.normalize();
376  /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
377  if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
378  {
379  break;
380  }
381  } else
382  {
383 // printf("currentDir: don't normalize a zero vector\n");
384  break;
385  }
386 
387  } else {
388  // we moved whole way
390  }
391 
392  // if (callback.m_closestHitFraction == 0.f)
393  // break;
394 
395  }
396 }
397 
399 {
400  btTransform start, end;
401 
402  // phase 3: down
403  /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
404  btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + additionalDownStep);
405  btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt;
406  btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downVelocity;
407  m_targetPosition -= (step_drop + gravity_drop);*/
408 
409  btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
410  if(downVelocity > 0.0 && downVelocity < m_stepHeight
411  && (m_wasOnGround || !m_wasJumping))
412  {
413  downVelocity = m_stepHeight;
414  }
415 
416  btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity);
417  m_targetPosition -= step_drop;
418 
419  start.setIdentity ();
420  end.setIdentity ();
421 
424 
428 
430  {
431  m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
432  } else
433  {
434  collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
435  }
436 
437  if (callback.hasHit())
438  {
439  // we dropped a fraction of the height -> hit floor
441  m_verticalVelocity = 0.0;
442  m_verticalOffset = 0.0;
443  m_wasJumping = false;
444  } else {
445  // we dropped the full height
446 
448  }
449 }
450 
451 
452 
454 (
455 const btVector3& walkDirection
456 )
457 {
458  m_useWalkDirection = true;
459  m_walkDirection = walkDirection;
460  m_normalizedDirection = getNormalizedVector(m_walkDirection);
461 }
462 
463 
464 
466 (
467 const btVector3& velocity,
468 btScalar timeInterval
469 )
470 {
471 // printf("setVelocity!\n");
472 // printf(" interval: %f\n", timeInterval);
473 // printf(" velocity: (%f, %f, %f)\n",
474 // velocity.x(), velocity.y(), velocity.z());
475 
476  m_useWalkDirection = false;
477  m_walkDirection = velocity;
478  m_normalizedDirection = getNormalizedVector(m_walkDirection);
479  m_velocityTimeInterval = timeInterval;
480 }
481 
482 
483 
485 {
486 }
487 
489 {
490  btTransform xform;
491  xform.setIdentity();
492  xform.setOrigin (origin);
494 }
495 
496 
498 {
499 
500  int numPenetrationLoops = 0;
501  m_touchingContact = false;
502  while (recoverFromPenetration (collisionWorld))
503  {
504  numPenetrationLoops++;
505  m_touchingContact = true;
506  if (numPenetrationLoops > 4)
507  {
508  //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
509  break;
510  }
511  }
512 
515 // printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
516 
517 
518 }
519 
520 #include <stdio.h>
521 
523 {
524 // printf("playerStep(): ");
525 // printf(" dt = %f", dt);
526 
527  // quick check...
529 // printf("\n");
530  return; // no motion
531  }
532 
534 
535  // Update fall velocity.
538  {
540  }
542  {
544  }
546 
547 
548  btTransform xform;
549  xform = m_ghostObject->getWorldTransform ();
550 
551 // printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
552 // printf("walkSpeed=%f\n",walkSpeed);
553 
554  stepUp (collisionWorld);
555  if (m_useWalkDirection) {
556  stepForwardAndStrafe (collisionWorld, m_walkDirection);
557  } else {
558  //printf(" time: %f", m_velocityTimeInterval);
559  // still have some time left for moving!
560  btScalar dtMoving =
563 
564  // how far will we move while we are moving?
565  btVector3 move = m_walkDirection * dtMoving;
566 
567  //printf(" dtMoving: %f", dtMoving);
568 
569  // okay, step
570  stepForwardAndStrafe(collisionWorld, move);
571  }
572  stepDown (collisionWorld, dt);
573 
574  // printf("\n");
575 
578 }
579 
581 {
582  m_fallSpeed = fallSpeed;
583 }
584 
586 {
587  m_jumpSpeed = jumpSpeed;
588 }
589 
591 {
592  m_maxJumpHeight = maxJumpHeight;
593 }
594 
596 {
597  return onGround();
598 }
599 
601 {
602  if (!canJump())
603  return;
604 
606  m_wasJumping = true;
607 
608 #if 0
609  currently no jumping.
610  btTransform xform;
611  m_rigidBody->getMotionState()->getWorldTransform (xform);
612  btVector3 up = xform.getBasis()[1];
613  up.normalize ();
614  btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
615  m_rigidBody->applyCentralImpulse (up * magnitude);
616 #endif
617 }
618 
620 {
621  m_gravity = gravity;
622 }
623 
625 {
626  return m_gravity;
627 }
628 
630 {
631  m_maxSlopeRadians = slopeRadians;
632  m_maxSlopeCosine = btCos(slopeRadians);
633 }
634 
636 {
637  return m_maxSlopeRadians;
638 }
639 
641 {
642  return m_verticalVelocity == 0.0 && m_verticalOffset == 0.0;
643 }
644 
645 
647 {
648  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) };
649 
650  return sUpAxisDirection;
651 }
652 
654 {
655 }