Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/bullet/BulletDynamics/Character/btKinematicCharacterController.cpp @ 2974

Last change on this file since 2974 was 2662, checked in by rgrieder, 16 years ago

Merged presentation branch back to trunk.

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