Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/SuperOrxoBros_HS18/SuperOrxoBros_HS18/src/external/bullet/BulletDynamics/Character/btKinematicCharacterController.cpp @ 12175

Last change on this file since 12175 was 12175, checked in by siramesh, 5 years ago

Super Orxo Bros (Sidharth Ramesh, Nisa Balta, Jeff Ren)

File size: 19.1 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
17#include "LinearMath/btIDebugDraw.h"
18#include "BulletCollision/CollisionDispatch/btGhostObject.h"
19#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
20#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
21#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
22#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
23#include "LinearMath/btDefaultMotionState.h"
24#include "btKinematicCharacterController.h"
25
26
27// static helper method
28static btVector3
29getNormalizedVector(const btVector3& v)
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
39///@todo Interact with dynamic objects,
40///Ride kinematicly animated platforms properly
41///More realistic (or maybe just a config option) falling
42/// -> Should integrate falling velocity manually and use that in stepDown()
43///Support jumping
44///Support ducking
45class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
46{
47public:
48        btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
49        {
50                m_me = me;
51        }
52
53        virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
54        {
55                if (rayResult.m_collisionObject == m_me)
56                        return 1.0;
57
58                return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
59        }
60protected:
61        btCollisionObject* m_me;
62};
63
64class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
65{
66public:
67        btKinematicClosestNotMeConvexResultCallback (btCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
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                {
86                        ///need to transform normal into worldspace
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        }
97protected:
98        btCollisionObject* m_me;
99        const btVector3 m_up;
100        btScalar m_minSlopeDot;
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 */
108btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal)
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 */
116btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal)
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 */
125btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal)
126{
127        return direction - parallelComponent(direction, normal);
128}
129
130btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis)
131{
132        m_upAxis = upAxis;
133        m_addedMargin = 0.02;
134        m_walkDirection.setValue(0,0,0);
135        m_useGhostObjectSweepTest = true;
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
141        m_velocityTimeInterval = 0.0;
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
152btKinematicCharacterController::~btKinematicCharacterController ()
153{
154}
155
156btPairCachingGhostObject* btKinematicCharacterController::getGhostObject()
157{
158        return m_ghostObject;
159}
160
161bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld)
162{
163
164        bool penetration = false;
165
166        collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher());
167
168        m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
169       
170        btScalar maxPen = btScalar(0.0);
171        for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++)
172        {
173                m_manifoldArray.resize(0);
174
175                btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
176               
177                if (collisionPair->m_algorithm)
178                        collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray);
179
180               
181                for (int j=0;j<m_manifoldArray.size();j++)
182                {
183                        btPersistentManifold* manifold = m_manifoldArray[j];
184                        btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
185                        for (int p=0;p<manifold->getNumContacts();p++)
186                        {
187                                const btManifoldPoint&pt = manifold->getContactPoint(p);
188
189                                btScalar dist = pt.getDistance();
190
191                                if (dist < 0.0)
192                                {
193                                        if (dist < maxPen)
194                                        {
195                                                maxPen = dist;
196                                                m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
197
198                                        }
199                                        m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2);
200                                        penetration = true;
201                                } else {
202                                        //printf("touching %f\n", dist);
203                                }
204                        }
205                       
206                        //manifold->clearManifold();
207                }
208        }
209        btTransform newTrans = m_ghostObject->getWorldTransform();
210        newTrans.setOrigin(m_currentPosition);
211        m_ghostObject->setWorldTransform(newTrans);
212//      printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
213        return penetration;
214}
215
216void btKinematicCharacterController::stepUp ( btCollisionWorld* world)
217{
218        // phase 1: up
219        btTransform start, end;
220        m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.f?m_verticalOffset:0.f));
221
222        start.setIdentity ();
223        end.setIdentity ();
224
225        /* FIXME: Handle penetration properly */
226        start.setOrigin (m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_convexShape->getMargin() + m_addedMargin));
227        end.setOrigin (m_targetPosition);
228
229        btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, -getUpAxisDirections()[m_upAxis], btScalar(0.7071));
230        callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
231        callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
232       
233        if (m_useGhostObjectSweepTest)
234        {
235                m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration);
236        }
237        else
238        {
239                world->convexSweepTest (m_convexShape, start, end, callback);
240        }
241       
242        if (callback.hasHit())
243        {
244                // Only modify the position if the hit was a slope and not a wall or ceiling.
245                if(callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > 0.0)
246                {
247                        // we moved up only a fraction of the step height
248                        m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
249                        m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
250                }
251                m_verticalVelocity = 0.0;
252                m_verticalOffset = 0.0;
253        } else {
254                m_currentStepOffset = m_stepHeight;
255                m_currentPosition = m_targetPosition;
256        }
257}
258
259void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
260{
261        btVector3 movementDirection = m_targetPosition - m_currentPosition;
262        btScalar movementLength = movementDirection.length();
263        if (movementLength>SIMD_EPSILON)
264        {
265                movementDirection.normalize();
266
267                btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
268                reflectDir.normalize();
269
270                btVector3 parallelDir, perpindicularDir;
271
272                parallelDir = parallelComponent (reflectDir, hitNormal);
273                perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
274
275                m_targetPosition = m_currentPosition;
276                if (0)//tangentMag != 0.0)
277                {
278                        btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength);
279//                      printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
280                        m_targetPosition +=  parComponent;
281                }
282
283                if (normalMag != 0.0)
284                {
285                        btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength);
286//                      printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
287                        m_targetPosition += perpComponent;
288                }
289        } else
290        {
291//              printf("movementLength don't normalize a zero vector\n");
292        }
293}
294
295void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove)
296{
297        // printf("m_normalizedDirection=%f,%f,%f\n",
298        //      m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
299        // phase 2: forward and strafe
300        btTransform start, end;
301        m_targetPosition = m_currentPosition + walkMove;
302
303        start.setIdentity ();
304        end.setIdentity ();
305       
306        btScalar fraction = 1.0;
307        btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
308//      printf("distance2=%f\n",distance2);
309
310        if (m_touchingContact)
311        {
312                if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0))
313                {
314                        updateTargetPositionBasedOnCollision (m_touchingNormal);
315                }
316        }
317
318        int maxIter = 10;
319
320        while (fraction > btScalar(0.01) && maxIter-- > 0)
321        {
322                start.setOrigin (m_currentPosition);
323                end.setOrigin (m_targetPosition);
324                btVector3 sweepDirNegative(m_currentPosition - m_targetPosition);
325
326                btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0));
327                callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
328                callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
329
330
331                btScalar margin = m_convexShape->getMargin();
332                m_convexShape->setMargin(margin + m_addedMargin);
333
334
335                if (m_useGhostObjectSweepTest)
336                {
337                        m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
338                } else
339                {
340                        collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
341                }
342               
343                m_convexShape->setMargin(margin);
344
345               
346                fraction -= callback.m_closestHitFraction;
347
348                if (callback.hasHit())
349                {       
350                        // we moved only a fraction
351                        btScalar hitDistance;
352                        hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
353
354//                      m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
355
356                        updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
357                        btVector3 currentDir = m_targetPosition - m_currentPosition;
358                        distance2 = currentDir.length2();
359                        if (distance2 > SIMD_EPSILON)
360                        {
361                                currentDir.normalize();
362                                /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */
363                                if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0))
364                                {
365                                        break;
366                                }
367                        } else
368                        {
369//                              printf("currentDir: don't normalize a zero vector\n");
370                                break;
371                        }
372
373                } else {
374                        // we moved whole way
375                        m_currentPosition = m_targetPosition;
376                }
377
378        //      if (callback.m_closestHitFraction == 0.f)
379        //              break;
380
381        }
382}
383
384void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt)
385{
386        btTransform start, end;
387
388        // phase 3: down
389        /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
390        btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + additionalDownStep);
391        btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt;
392        btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downVelocity;
393        m_targetPosition -= (step_drop + gravity_drop);*/
394
395        btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt;
396        if(downVelocity > 0.0 && downVelocity < m_stepHeight
397                && (m_wasOnGround || !m_wasJumping))
398        {
399                downVelocity = m_stepHeight;
400        }
401
402        btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity);
403        m_targetPosition -= step_drop;
404
405        start.setIdentity ();
406        end.setIdentity ();
407
408        start.setOrigin (m_currentPosition);
409        end.setOrigin (m_targetPosition);
410
411        btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine);
412        callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup;
413        callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask;
414       
415        if (m_useGhostObjectSweepTest)
416        {
417                m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
418        } else
419        {
420                collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
421        }
422
423        if (callback.hasHit())
424        {
425                // we dropped a fraction of the height -> hit floor
426                m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
427                m_verticalVelocity = 0.0;
428                m_verticalOffset = 0.0;
429                m_wasJumping = false;
430        } else {
431                // we dropped the full height
432               
433                m_currentPosition = m_targetPosition;
434        }
435}
436
437
438
439void btKinematicCharacterController::setWalkDirection
440(
441const btVector3& walkDirection
442)
443{
444        m_useWalkDirection = true;
445        m_walkDirection = walkDirection;
446        m_normalizedDirection = getNormalizedVector(m_walkDirection);
447}
448
449
450
451void btKinematicCharacterController::setVelocityForTimeInterval
452(
453const btVector3& velocity,
454btScalar timeInterval
455)
456{
457//      printf("setVelocity!\n");
458//      printf("  interval: %f\n", timeInterval);
459//      printf("  velocity: (%f, %f, %f)\n",
460//               velocity.x(), velocity.y(), velocity.z());
461
462        m_useWalkDirection = false;
463        m_walkDirection = velocity;
464        m_normalizedDirection = getNormalizedVector(m_walkDirection);
465        m_velocityTimeInterval = timeInterval;
466}
467
468
469
470void btKinematicCharacterController::reset ()
471{
472}
473
474void btKinematicCharacterController::warp (const btVector3& origin)
475{
476        btTransform xform;
477        xform.setIdentity();
478        xform.setOrigin (origin);
479        m_ghostObject->setWorldTransform (xform);
480}
481
482
483void btKinematicCharacterController::preStep (  btCollisionWorld* collisionWorld)
484{
485       
486        int numPenetrationLoops = 0;
487        m_touchingContact = false;
488        while (recoverFromPenetration (collisionWorld))
489        {
490                numPenetrationLoops++;
491                m_touchingContact = true;
492                if (numPenetrationLoops > 4)
493                {
494                        //printf("character could not recover from penetration = %d\n", numPenetrationLoops);
495                        break;
496                }
497        }
498
499        m_currentPosition = m_ghostObject->getWorldTransform().getOrigin();
500        m_targetPosition = m_currentPosition;
501//      printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
502
503       
504}
505
506#include <stdio.h>
507
508void btKinematicCharacterController::playerStep (  btCollisionWorld* collisionWorld, btScalar dt)
509{
510//      printf("playerStep(): ");
511//      printf("  dt = %f", dt);
512
513        // quick check...
514        if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) {
515//              printf("\n");
516                return;         // no motion
517        }
518
519        m_wasOnGround = onGround();
520
521        // Update fall velocity.
522        m_verticalVelocity -= m_gravity * dt;
523        if(m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed)
524        {
525                m_verticalVelocity = m_jumpSpeed;
526        }
527        if(m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed))
528        {
529                m_verticalVelocity = -btFabs(m_fallSpeed);
530        }
531        m_verticalOffset = m_verticalVelocity * dt;
532
533
534        btTransform xform;
535        xform = m_ghostObject->getWorldTransform ();
536
537//      printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
538//      printf("walkSpeed=%f\n",walkSpeed);
539
540        stepUp (collisionWorld);
541        if (m_useWalkDirection) {
542                stepForwardAndStrafe (collisionWorld, m_walkDirection);
543        } else {
544                //printf("  time: %f", m_velocityTimeInterval);
545                // still have some time left for moving!
546                btScalar dtMoving =
547                        (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval;
548                m_velocityTimeInterval -= dt;
549
550                // how far will we move while we are moving?
551                btVector3 move = m_walkDirection * dtMoving;
552
553                //printf("  dtMoving: %f", dtMoving);
554
555                // okay, step
556                stepForwardAndStrafe(collisionWorld, move);
557        }
558        stepDown (collisionWorld, dt);
559
560        // printf("\n");
561
562        xform.setOrigin (m_currentPosition);
563        m_ghostObject->setWorldTransform (xform);
564}
565
566void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed)
567{
568        m_fallSpeed = fallSpeed;
569}
570
571void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed)
572{
573        m_jumpSpeed = jumpSpeed;
574}
575
576void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight)
577{
578        m_maxJumpHeight = maxJumpHeight;
579}
580
581bool btKinematicCharacterController::canJump () const
582{
583        return onGround();
584}
585
586void btKinematicCharacterController::jump ()
587{
588        if (!canJump())
589                return;
590
591        m_verticalVelocity = m_jumpSpeed;
592        m_wasJumping = true;
593
594#if 0
595        currently no jumping.
596        btTransform xform;
597        m_rigidBody->getMotionState()->getWorldTransform (xform);
598        btVector3 up = xform.getBasis()[1];
599        up.normalize ();
600        btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
601        m_rigidBody->applyCentralImpulse (up * magnitude);
602#endif
603}
604
605void btKinematicCharacterController::setGravity(btScalar gravity)
606{
607        m_gravity = gravity;
608}
609
610btScalar btKinematicCharacterController::getGravity() const
611{
612        return m_gravity;
613}
614
615void btKinematicCharacterController::setMaxSlope(btScalar slopeRadians)
616{
617        m_maxSlopeRadians = slopeRadians;
618        m_maxSlopeCosine = btCos(slopeRadians);
619}
620
621btScalar btKinematicCharacterController::getMaxSlope() const
622{
623        return m_maxSlopeRadians;
624}
625
626bool btKinematicCharacterController::onGround () const
627{
628        return m_verticalVelocity == 0.0 && m_verticalOffset == 0.0;
629}
630
631
632btVector3* btKinematicCharacterController::getUpAxisDirections()
633{
634        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) };
635       
636        return sUpAxisDirection;
637}
638
639void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer)
640{
641}
Note: See TracBrowser for help on using the repository browser.