Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core4/src/bullet/BulletDynamics/Character/btKinematicCharacterController.cpp @ 5681

Last change on this file since 5681 was 2882, checked in by rgrieder, 16 years ago

Update from Bullet 2.73 to 2.74.

  • Property svn:eol-style set to native
File size: 14.9 KB
Line 
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 "LinearMath/btIDebugDraw.h"
17#include "BulletCollision/CollisionDispatch/btGhostObject.h"
18#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
19#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
20#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
21#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
22#include "LinearMath/btDefaultMotionState.h"
23#include "btKinematicCharacterController.h"
24
25static btVector3 upAxisDirection[3] = { btVector3(1.0f, 0.0f, 0.0f), btVector3(0.0f, 1.0f, 0.0f), btVector3(0.0f, 0.0f, 1.0f) };
26
27///@todo Interact with dynamic objects,
28///Ride kinematicly animated platforms properly
29///More realistic (or maybe just a config option) falling
30/// -> Should integrate falling velocity manually and use that in stepDown()
31///Support jumping
32///Support ducking
33class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
34{
35public:
36        btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
37        {
38                m_me = me;
39        }
40
41        virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
42        {
43                if (rayResult.m_collisionObject == m_me)
44                        return 1.0;
45
46                return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
47        }
48protected:
49        btCollisionObject* m_me;
50};
51
52class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
53{
54public:
55        btKinematicClosestNotMeConvexResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
56        {
57                m_me = me;
58        }
59
60        virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
61        {
62                if (convexResult.m_hitCollisionObject == m_me)
63                        return 1.0;
64
65                return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
66        }
67protected:
68        btCollisionObject* m_me;
69};
70
71/*
72 * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
73 *
74 * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
75 */
76btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal)
77{
78        return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
79}
80
81/*
82 * Returns the portion of 'direction' that is parallel to 'normal'
83 */
84btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal)
85{
86        btScalar magnitude = direction.dot(normal);
87        return normal * magnitude;
88}
89
90/*
91 * Returns the portion of 'direction' that is perpindicular to 'normal'
92 */
93btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal)
94{
95        return direction - parallelComponent(direction, normal);
96}
97
98btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis)
99{
100        m_upAxis = upAxis;
101        m_addedMargin = 0.02f;
102        m_walkDirection.setValue(0,0,0);
103        m_useGhostObjectSweepTest = true;
104        m_ghostObject = ghostObject;
105        m_stepHeight = stepHeight;
106        m_turnAngle = btScalar(0.0);
107        m_convexShape=convexShape;     
108}
109
110btKinematicCharacterController::~btKinematicCharacterController ()
111{
112}
113
114btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
115{
116        return m_ghostObject;
117}
118
119bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld)
120{
121
122        bool penetration = false;
123
124        collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
125
126        m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
127       
128        btScalar maxPen = btScalar(0.0);
129        for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
130        {
131                m_manifoldArray.resize(0);
132
133                btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
134               
135                if (collisionPair->m_algorithm)
136                        collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
137
138               
139                for (int j=0;j<m_manifoldArray.size();j++)
140                {
141                        btPersistentManifold* manifold = m_manifoldArray[j];
142                        btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
143                        for (int p=0;p<manifold->getNumContacts();p++)
144                        {
145                                const btManifoldPoint&pt = manifold->getContactPoint(p);
146
147                                if (pt.getDistance() < 0.0)
148                                {
149                                        if (pt.getDistance() < maxPen)
150                                        {
151                                                maxPen = pt.getDistance();
152                                                m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
153
154                                        }
155                                        m_currentPosition += pt.m_normalWorldOnB * directionSign * pt.getDistance() * btScalar(0.2);
156                                        penetration = true;
157                                } else {
158                                        //printf("touching %f\n", pt.getDistance());
159                                }
160                        }
161                       
162                        //manifold->clearManifold();
163                }
164        }
165        btTransform newTrans = m_ghostObject->getWorldTransform();
166        newTrans.setOrigin(m_currentPosition);
167        m_ghostObject->setWorldTransform(newTrans);
168//      printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
169        return penetration;
170}
171
172void btKinematicCharacterController::stepUp ( btCollisionWorld* world)
173{
174        // phase 1: up
175        btTransform start, end;
176        m_targetPosition = m_currentPosition + upAxisDirection[m_upAxis] * m_stepHeight;
177
178        start.setIdentity ();
179        end.setIdentity ();
180
181        /* FIXME: Handle penetration properly */
182        start.setOrigin (m_currentPosition + upAxisDirection[m_upAxis] * btScalar(0.1f));
183        end.setOrigin (m_targetPosition);
184
185        btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject);
186        callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
187        callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
188       
189        if (m_useGhostObjectSweepTest)
190        {
191                m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
192        }
193        else
194        {
195                world->convexSweepTest (m_convexShape, start, end, callback);
196        }
197       
198        if (callback.hasHit())
199        {
200                // we moved up only a fraction of the step height
201                m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
202                m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
203        } else {
204                m_currentStepOffset = m_stepHeight;
205                m_currentPosition = m_targetPosition;
206        }
207}
208
209void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
210{
211        btVector3 movementDirection = m_targetPosition - m_currentPosition;
212        btScalar movementLength = movementDirection.length();
213        if (movementLength>SIMD_EPSILON)
214        {
215                movementDirection.normalize();
216
217                btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
218                reflectDir.normalize();
219
220                btVector3 parallelDir, perpindicularDir;
221
222                parallelDir = parallelComponent (reflectDir, hitNormal);
223                perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
224
225                m_targetPosition = m_currentPosition;
226                if (0)//tangentMag != 0.0)
227                {
228                        btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
229//                      printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
230                        m_targetPosition +=  parComponent;
231                }
232
233                if (normalMag != 0.0)
234                {
235                        btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
236//                      printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
237                        m_targetPosition += perpComponent;
238                }
239        } else
240        {
241//              printf("movementLength don't normalize a zero vector\n");
242        }
243}
244
245void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove)
246{
247
248        btVector3 originalDir = walkMove.normalized();
249        if (walkMove.length() < SIMD_EPSILON)
250        {
251                originalDir.setValue(0.f,0.f,0.f);
252        }
253//      printf("originalDir=%f,%f,%f\n",originalDir[0],originalDir[1],originalDir[2]);
254        // phase 2: forward and strafe
255        btTransform start, end;
256        m_targetPosition = m_currentPosition + walkMove;
257        start.setIdentity ();
258        end.setIdentity ();
259       
260        btScalar fraction = 1.0;
261        btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
262//      printf("distance2=%f\n",distance2);
263
264        if (m_touchingContact)
265        {
266                if (originalDir.dot(m_touchingNormal) > btScalar(0.0))
267                        updateTargetPositionBasedOnCollision (m_touchingNormal);
268        }
269
270        int maxIter = 10;
271
272        while (fraction > btScalar(0.01) && maxIter-- > 0)
273        {
274                start.setOrigin (m_currentPosition);
275                end.setOrigin (m_targetPosition);
276
277                btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject);
278                callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
279                callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
280
281
282                btScalar margin = m_convexShape->getMargin();
283                m_convexShape->setMargin(margin + m_addedMargin);
284
285
286                if (m_useGhostObjectSweepTest)
287                {
288                        m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
289                } else
290                {
291                        collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
292                }
293               
294                m_convexShape->setMargin(margin);
295
296               
297                fraction -= callback.m_closestHitFraction;
298
299                if (callback.hasHit())
300                {       
301                        // we moved only a fraction
302                        btScalar hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
303                        if (hitDistance<0.f)
304                        {
305//                              printf("neg dist?\n");
306                        }
307
308                        /* If the distance is farther than the collision margin, move */
309                        if (hitDistance > m_addedMargin)
310                        {
311//                              printf("callback.m_closestHitFraction=%f\n",callback.m_closestHitFraction);
312                                m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
313                        }
314
315                        updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
316                        btVector3 currentDir = m_targetPosition - m_currentPosition;
317                        distance2 = currentDir.length2();
318                        if (distance2 > SIMD_EPSILON)
319                        {
320                                currentDir.normalize();
321                                /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
322                                if (currentDir.dot(originalDir) <= btScalar(0.0))
323                                {
324                                        break;
325                                }
326                        } else
327                        {
328//                              printf("currentDir: don't normalize a zero vector\n");
329                                break;
330                        }
331                } else {
332                        // we moved whole way
333                        m_currentPosition = m_targetPosition;
334                }
335
336        //      if (callback.m_closestHitFraction == 0.f)
337        //              break;
338
339        }
340}
341
342void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt)
343{
344        btTransform start, end;
345
346        // phase 3: down
347        btVector3 step_drop = upAxisDirection[m_upAxis] * m_currentStepOffset;
348        btVector3 gravity_drop = upAxisDirection[m_upAxis] * m_stepHeight; 
349        m_targetPosition -= (step_drop + gravity_drop);
350
351        start.setIdentity ();
352        end.setIdentity ();
353
354        start.setOrigin (m_currentPosition);
355        end.setOrigin (m_targetPosition);
356
357        btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject);
358        callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
359        callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
360       
361        if (m_useGhostObjectSweepTest)
362        {
363                m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
364        } else
365        {
366                collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
367        }
368
369        if (callback.hasHit())
370        {
371                // we dropped a fraction of the height -> hit floor
372                m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
373        } else {
374                // we dropped the full height
375               
376                m_currentPosition = m_targetPosition;
377        }
378}
379
380void btKinematicCharacterController::reset ()
381{
382}
383
384void btKinematicCharacterController::warp (const btVector3& origin)
385{
386        btTransform xform;
387        xform.setIdentity();
388        xform.setOrigin (origin);
389        m_ghostObject->setWorldTransform (xform);
390}
391
392
393void btKinematicCharacterController::preStep (  btCollisionWorld* collisionWorld)
394{
395       
396        int numPenetrationLoops = 0;
397        m_touchingContact = false;
398        while (recoverFromPenetration (collisionWorld))
399        {
400                numPenetrationLoops++;
401                m_touchingContact = true;
402                if (numPenetrationLoops > 4)
403                {
404//                      printf("character could not recover from penetration = %d\n", numPenetrationLoops);
405                        break;
406                }
407        }
408
409        m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
410        m_targetPosition = m_currentPosition;
411//      printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
412
413       
414}
415
416void btKinematicCharacterController::playerStep (  btCollisionWorld* collisionWorld, btScalar dt)
417{
418        btTransform xform;
419        xform = m_ghostObject->getWorldTransform ();
420
421//      printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
422//      printf("walkSpeed=%f\n",walkSpeed);
423
424        stepUp (collisionWorld);
425        stepForwardAndStrafe (collisionWorld, m_walkDirection);
426        stepDown (collisionWorld, dt);
427
428        xform.setOrigin (m_currentPosition);
429        m_ghostObject->setWorldTransform (xform);
430}
431
432void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed)
433{
434        m_fallSpeed = fallSpeed;
435}
436
437void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed)
438{
439        m_jumpSpeed = jumpSpeed;
440}
441
442void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight)
443{
444        m_maxJumpHeight = maxJumpHeight;
445}
446
447bool btKinematicCharacterController::canJump () const
448{
449        return onGround();
450}
451
452void btKinematicCharacterController::jump ()
453{
454        if (!canJump())
455                return;
456
457#if 0
458        currently no jumping.
459        btTransform xform;
460        m_rigidBody->getMotionState()->getWorldTransform (xform);
461        btVector3 up = xform.getBasis()[1];
462        up.normalize ();
463        btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
464        m_rigidBody->applyCentralImpulse (up * magnitude);
465#endif
466}
467
468bool btKinematicCharacterController::onGround () const
469{
470        return true;
471}
472
473
474void    btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)
475{
476}
Note: See TracBrowser for help on using the repository browser.