a4c9df51466368142758dcaa0a5693cb6c8ca249
[blender.git] / source / gameengine / Physics / Bullet / CcdPhysicsEnvironment.cpp
1 #include "CcdPhysicsEnvironment.h"
2 #include "CcdPhysicsController.h"
3
4 #include <algorithm>
5 #include "SimdTransform.h"
6 #include "Dynamics/RigidBody.h"
7 #include "BroadphaseCollision/BroadPhaseInterface.h"
8 #include "BroadphaseCollision/SimpleBroadphase.h"
9
10 #include "CollisionShapes/ConvexShape.h"
11 #include "BroadphaseCollision/CollisionDispatcher.h"
12 #include "NarrowPhaseCollision/PersistentManifold.h"
13 #include "CollisionShapes/TriangleMeshShape.h"
14 #include "ConstraintSolver/OdeConstraintSolver.h"
15 #include "ConstraintSolver/SimpleConstraintSolver.h"
16
17
18
19 #include "CollisionDispatch/ToiContactDispatcher.h"
20
21
22 #include "CollisionDispatch/EmptyCollisionAlgorithm.h"
23 #include "CollisionDispatch/UnionFind.h"
24
25 #include "NarrowPhaseCollision/RaycastCallback.h"
26
27 bool useIslands = true;
28
29 #include "ConstraintSolver/ConstraintSolver.h"
30 #include "ConstraintSolver/Point2PointConstraint.h"
31 //#include "BroadphaseCollision/QueryDispatcher.h"
32 //#include "BroadphaseCollision/QueryBox.h"
33 //todo: change this to allow dynamic registration of types!
34
35 unsigned long gNumIterations = 10;
36
37 #ifdef WIN32
38 void DrawRasterizerLine(const float* from,const float* to,int color);
39 #endif
40
41
42 #include "ConstraintSolver/ContactConstraint.h"
43
44
45
46 #include <stdio.h>
47
48
49
50
51 static void DrawAabb(PHY_IPhysicsDebugDraw* debugDrawer,const SimdVector3& from,const SimdVector3& to,const SimdVector3& color)
52 {
53         SimdVector3 halfExtents = (to-from)* 0.5f;
54         SimdVector3 center = (to+from) *0.5f;
55         int i,j;
56
57         SimdVector3 edgecoord(1.f,1.f,1.f),pa,pb;
58         for (i=0;i<4;i++)
59         {
60                 for (j=0;j<3;j++)
61                 {
62                         pa = SimdVector3(edgecoord[0]*halfExtents[0], edgecoord[1]*halfExtents[1],              
63                                 edgecoord[2]*halfExtents[2]);
64                         pa+=center;
65                         
66                         int othercoord = j%3;
67                         edgecoord[othercoord]*=-1.f;
68                         pb = SimdVector3(edgecoord[0]*halfExtents[0], edgecoord[1]*halfExtents[1],      
69                                 edgecoord[2]*halfExtents[2]);
70                         pb+=center;
71                         
72                         debugDrawer->DrawLine(pa,pb,color);
73                 }
74                 edgecoord = SimdVector3(-1.f,-1.f,-1.f);
75                 if (i<3)
76                         edgecoord[i]*=-1.f;
77         }
78
79
80 }
81
82
83
84
85
86
87 CcdPhysicsEnvironment::CcdPhysicsEnvironment(ToiContactDispatcher* dispatcher,BroadphaseInterface* bp)
88 :m_dispatcher(dispatcher),
89 m_broadphase(bp),
90 m_scalingPropagated(false)
91 {
92         if (!m_dispatcher)
93         {
94                 OdeConstraintSolver* solver = new OdeConstraintSolver();
95                 //SimpleConstraintSolver* solver= new SimpleConstraintSolver();
96                 m_dispatcher = new ToiContactDispatcher(solver);
97         }
98         if (!m_broadphase)
99         {
100                 m_broadphase = new SimpleBroadphase();
101         }
102         
103         m_debugDrawer = 0;
104         m_gravity = SimdVector3(0.f,-10.f,0.f);
105         
106
107 }
108
109 void    CcdPhysicsEnvironment::addCcdPhysicsController(CcdPhysicsController* ctrl)
110 {
111         ctrl->GetRigidBody()->setGravity( m_gravity );
112         m_controllers.push_back(ctrl);
113         
114         BroadphaseInterface* scene =  m_broadphase;
115         
116         CollisionShape* shapeinterface = ctrl->GetCollisionShape();
117         
118         assert(shapeinterface);
119         
120         const SimdTransform& t = ctrl->GetRigidBody()->getCenterOfMassTransform();
121         
122         RigidBody* body = ctrl->GetRigidBody();
123         
124         SimdPoint3 minAabb,maxAabb;
125         
126         shapeinterface->GetAabb(t,minAabb,maxAabb);
127         
128         float timeStep = 0.02f;
129         
130         
131         //extent it with the motion
132         
133         SimdVector3 linMotion = body->getLinearVelocity()*timeStep;
134         
135         float maxAabbx = maxAabb.getX();
136         float maxAabby = maxAabb.getY();
137         float maxAabbz = maxAabb.getZ();
138         float minAabbx = minAabb.getX();
139         float minAabby = minAabb.getY();
140         float minAabbz = minAabb.getZ();
141
142         if (linMotion.x() > 0.f)
143                 maxAabbx += linMotion.x(); 
144         else
145                 minAabbx += linMotion.x();
146         if (linMotion.y() > 0.f)
147                 maxAabby += linMotion.y(); 
148         else
149                 minAabby += linMotion.y();
150         if (linMotion.z() > 0.f)
151                 maxAabbz += linMotion.z(); 
152         else
153                 minAabbz += linMotion.z();
154         
155
156         minAabb = SimdVector3(minAabbx,minAabby,minAabbz);
157         maxAabb = SimdVector3(maxAabbx,maxAabby,maxAabbz);
158         
159         if (!ctrl->m_broadphaseHandle)
160         {
161                 int type = shapeinterface->GetShapeType();
162                 ctrl->m_broadphaseHandle = scene->CreateProxy(
163                         ctrl->GetRigidBody(),
164                         type,
165                         minAabb, 
166                         maxAabb);
167         }
168         
169         body->SetCollisionShape( shapeinterface );
170         
171         
172         
173 }
174
175 void    CcdPhysicsEnvironment::removeCcdPhysicsController(CcdPhysicsController* ctrl)
176 {
177         
178         //also remove constraint
179         
180         {
181                 std::vector<Point2PointConstraint*>::iterator i;
182                 
183                 for (i=m_p2pConstraints.begin();
184                 !(i==m_p2pConstraints.end()); i++)
185                 {
186                         Point2PointConstraint* p2p = (*i);
187                         if  ((&p2p->GetRigidBodyA() == ctrl->GetRigidBody() ||
188                                 (&p2p->GetRigidBodyB() == ctrl->GetRigidBody())))
189                         {
190                                 removeConstraint(int(p2p));
191                                 //only 1 constraint per constroller
192                                 break;
193                         }
194                 }
195         }
196         
197         {
198                 std::vector<Point2PointConstraint*>::iterator i;
199                 
200                 for (i=m_p2pConstraints.begin();
201                 !(i==m_p2pConstraints.end()); i++)
202                 {
203                         Point2PointConstraint* p2p = (*i);
204                         if  ((&p2p->GetRigidBodyA() == ctrl->GetRigidBody() ||
205                                 (&p2p->GetRigidBodyB() == ctrl->GetRigidBody())))
206                         {
207                                 removeConstraint(int(p2p));
208                                 //only 1 constraint per constroller
209                                 break;
210                         }
211                 }
212         }
213         
214         
215         
216         bool removeFromBroadphase = false;
217         
218         {
219                 BroadphaseInterface* scene = m_broadphase;
220                 BroadphaseProxy* bp = (BroadphaseProxy*)ctrl->m_broadphaseHandle;
221                 
222                 if (removeFromBroadphase)
223                 {
224                 }
225                 //
226                 // only clear the cached algorithms
227                 //
228                 scene->CleanProxyFromPairs(bp);
229         }
230         {
231                 std::vector<CcdPhysicsController*>::iterator i =
232                         std::find(m_controllers.begin(), m_controllers.end(), ctrl);
233                 if (!(i == m_controllers.end()))
234                 {
235                         std::swap(*i, m_controllers.back());
236                         m_controllers.pop_back();
237                 }
238         }
239 }
240
241 void    CcdPhysicsEnvironment::UpdateActivationState()
242 {
243         m_dispatcher->InitUnionFind();
244         
245         // put the index into m_controllers into m_tag  
246         {
247                 std::vector<CcdPhysicsController*>::iterator i;
248                 
249                 int index = 0;
250                 for (i=m_controllers.begin();
251                 !(i==m_controllers.end()); i++)
252                 {
253                         CcdPhysicsController* ctrl = (*i);
254                         RigidBody* body = ctrl->GetRigidBody();
255                         body->m_islandTag1 = index;
256                         body->m_hitFraction = 1.f;
257                         index++;
258                         
259                 }
260         }
261         // do the union find
262         
263         m_dispatcher->FindUnions();
264         
265         // put the islandId ('find' value) into m_tag   
266         {
267                 UnionFind& unionFind = m_dispatcher->GetUnionFind();
268                 
269                 std::vector<CcdPhysicsController*>::iterator i;
270                 
271                 int index = 0;
272                 for (i=m_controllers.begin();
273                 !(i==m_controllers.end()); i++)
274                 {
275                         CcdPhysicsController* ctrl = (*i);
276                         RigidBody* body = ctrl->GetRigidBody();
277                         
278                         
279                         if (body->mergesSimulationIslands())
280                         {
281                                 body->m_islandTag1 = unionFind.find(index);
282                         } else
283                         {
284                                 body->m_islandTag1 = -1;
285                         }
286                         index++;
287                 }
288         }
289         
290 }
291
292 bool gPredictCollision = false;//true;//false;
293
294
295 /// Perform an integration step of duration 'timeStep'.
296 bool    CcdPhysicsEnvironment::proceedDeltaTime(double curTime,float timeStep)
297 {
298         
299         
300 //      printf("CcdPhysicsEnvironment::proceedDeltaTime\n");
301         
302         if (timeStep == 0.f)
303                 return true;
304
305         //clamp hardcoded for now
306         if (timeStep > 0.02)
307                 timeStep = 0.02;
308         
309         //this is needed because scaling is not known in advance, and scaling has to propagate to the shape
310         if (!m_scalingPropagated)
311         {
312                 //SyncMotionStates(timeStep);
313                 //m_scalingPropagated = true;
314         }
315
316 #ifdef EXTRA_PHYSICS_PROFILE
317         cpuProfile.begin("integrate force");
318 #endif //EXTRA_PHYSICS_PROFILE
319
320
321
322         {
323 //              std::vector<CcdPhysicsController*>::iterator i;
324                 
325                 
326                 
327                 int k;
328                 for (k=0;k<GetNumControllers();k++)
329                 {
330                         CcdPhysicsController* ctrl = m_controllers[k];
331                         //              SimdTransform predictedTrans;
332                         RigidBody* body = ctrl->GetRigidBody();
333                         if (body->GetActivationState() != ISLAND_SLEEPING)
334                         {
335                                 body->applyForces( timeStep);
336                                 body->integrateVelocities( timeStep);
337                         }
338                         
339                 }
340         }
341 #ifdef EXTRA_PHYSICS_PROFILE
342         cpuProfile.end("integrate force");
343 #endif //EXTRA_PHYSICS_PROFILE
344         BroadphaseInterface*    scene = m_broadphase;
345         
346         
347         //
348         // collision detection (?)
349         //
350         
351         
352         
353         
354         
355         int numsubstep = gNumIterations;
356         
357         
358         DispatcherInfo dispatchInfo;
359         dispatchInfo.m_timeStep = timeStep;
360         dispatchInfo.m_stepCount = 0;
361 #ifdef EXTRA_PHYSICS_PROFILE
362         cpuProfile.begin("cd");
363 #endif //EXTRA_PHYSICS_PROFILE
364
365         scene->DispatchAllCollisionPairs(*m_dispatcher,dispatchInfo);///numsubstep,g);
366
367 #ifdef EXTRA_PHYSICS_PROFILE
368         cpuProfile.end("cd");
369 #endif //EXTRA_PHYSICS_PROFILE
370
371
372         
373                 
374 #ifdef EXTRA_PHYSICS_PROFILE
375         cpuProfile.begin("solver");
376 #endif //EXTRA_PHYSICS_PROFILE
377         
378         int numRigidBodies = m_controllers.size();
379         
380         UpdateActivationState();
381
382         //contacts
383         m_dispatcher->SolveConstraints(timeStep, gNumIterations ,numRigidBodies);
384         
385 #ifdef EXTRA_PHYSICS_PROFILE
386         cpuProfile.end("solver");
387 #endif //EXTRA_PHYSICS_PROFILE
388
389         for (int g=0;g<numsubstep;g++)
390         {
391                 //
392                 // constraint solving
393                 //
394                 
395                 
396                 int i;
397                 int numPoint2Point = m_p2pConstraints.size();
398                 
399                 //point to point constraints
400                 for (i=0;i< numPoint2Point ; i++ )
401                 {
402                         Point2PointConstraint* p2p = m_p2pConstraints[i];
403                         
404                         p2p->BuildJacobian();
405                         p2p->SolveConstraint( timeStep );
406                         
407                 }
408                 /*
409                 //vehicles
410                 int numVehicles = m_vehicles.size();
411                 for (i=0;i<numVehicles;i++)
412                 {
413                         Vehicle* vehicle = m_vehicles[i];
414                         vehicle->UpdateVehicle( timeStep );
415                 }
416                 */
417                 
418                 
419                 
420         }
421         
422         {
423                 
424                 
425                 
426                 {
427                         
428                         std::vector<CcdPhysicsController*>::iterator i;
429                         
430                         //
431                         // update aabbs, only for moving objects (!)
432                         //
433                         for (i=m_controllers.begin();
434                         !(i==m_controllers.end()); i++)
435                         {
436                                 CcdPhysicsController* ctrl = (*i);
437                                 RigidBody* body = ctrl->GetRigidBody();
438                                 
439                                 
440                                 SimdPoint3 minAabb,maxAabb;
441                                 CollisionShape* shapeinterface = ctrl->GetCollisionShape();
442
443
444
445                                 shapeinterface->CalculateTemporalAabb(body->getCenterOfMassTransform(),
446                                         body->getLinearVelocity(),body->getAngularVelocity(),
447                                         timeStep,minAabb,maxAabb);
448
449                                 shapeinterface->GetAabb(body->getCenterOfMassTransform(),
450                                         minAabb,maxAabb);
451
452                                 
453                                 BroadphaseProxy* bp = (BroadphaseProxy*) ctrl->m_broadphaseHandle;
454                                 if (bp)
455                                 {
456                                         
457 #ifdef WIN32
458                                         SimdVector3 color (1,0,0);
459                                         if (m_debugDrawer)
460                                         {       
461                                                 //draw aabb
462
463                                                 DrawAabb(m_debugDrawer,minAabb,maxAabb,color);
464                                         }
465 #endif
466                                         scene->SetAabb(bp,minAabb,maxAabb);
467                                 }
468                         }
469                         
470                         float toi = 1.f;
471
472
473                         
474                         if (gPredictCollision)
475                         {
476                                 DispatcherInfo dispatchInfo;
477                                 dispatchInfo.m_timeStep = timeStep;
478                                 dispatchInfo.m_stepCount = 0;
479                                 dispatchInfo.m_dispatchFunc = DispatcherInfo::DISPATCH_CONTINUOUS;
480                                 
481                                 scene->DispatchAllCollisionPairs( *m_dispatcher,dispatchInfo);///numsubstep,g);
482                                 toi = dispatchInfo.m_timeOfImpact;
483                         }
484                         
485                         //
486                         // integrating solution
487                         //
488                         
489                         {
490                                 std::vector<CcdPhysicsController*>::iterator i;
491                                 
492                                 for (i=m_controllers.begin();
493                                 !(i==m_controllers.end()); i++)
494                                 {
495                                         
496                                         CcdPhysicsController* ctrl = *i;
497                                         
498                                         SimdTransform predictedTrans;
499                                         RigidBody* body = ctrl->GetRigidBody();
500                                         if (body->GetActivationState() != ISLAND_SLEEPING)
501                                         {
502                                                 body->predictIntegratedTransform(timeStep*      toi, predictedTrans);
503                                                 body->proceedToTransform( predictedTrans);
504
505                                         }
506                                 }
507                                 
508                         }
509                         
510                         
511                         
512                         
513                         
514                         //
515                         // disable sleeping physics objects
516                         //
517                         
518                         std::vector<CcdPhysicsController*> m_sleepingControllers;
519                         
520                         for (i=m_controllers.begin();
521                         !(i==m_controllers.end()); i++)
522                         {
523                                 CcdPhysicsController* ctrl = (*i);
524                                 RigidBody* body = ctrl->GetRigidBody();
525                                 
526                                 if (ctrl->wantsSleeping())
527                                 {
528                                         if (body->GetActivationState() == ACTIVE_TAG)
529                                                 body->SetActivationState( WANTS_DEACTIVATION );
530                                 } else
531                                 {
532                                         body->SetActivationState( ACTIVE_TAG );
533                                 }
534
535                                 if (useIslands)
536                                 {
537                                         if (body->GetActivationState() == ISLAND_SLEEPING)
538                                         {
539                                                 m_sleepingControllers.push_back(ctrl);
540                                         }
541                                 } else
542                                 {
543                                         if (ctrl->wantsSleeping())
544                                         {
545                                                 m_sleepingControllers.push_back(ctrl);
546                                         }
547                                 }
548                         }
549                         
550         
551                         
552                         
553         }
554         
555         SyncMotionStates(timeStep);
556
557         }
558         return true;
559 }
560
561 void    CcdPhysicsEnvironment::SyncMotionStates(float timeStep)
562 {
563         std::vector<CcdPhysicsController*>::iterator i;
564
565         //
566         // synchronize the physics and graphics transformations
567         //
568         for (i=m_controllers.begin();
569         !(i==m_controllers.end()); i++)
570         {
571                 CcdPhysicsController* ctrl = (*i);
572                 ctrl->SynchronizeMotionStates(timeStep);
573                 
574         }
575
576 }
577 void            CcdPhysicsEnvironment::setGravity(float x,float y,float z)
578 {
579         m_gravity = SimdVector3(x,y,z);
580
581         std::vector<CcdPhysicsController*>::iterator i;
582
583         //todo: review this gravity stuff
584         for (i=m_controllers.begin();
585         !(i==m_controllers.end()); i++)
586         {
587
588                 CcdPhysicsController* ctrl = (*i);
589                 ctrl->GetRigidBody()->setGravity(m_gravity);
590
591         }
592 }
593
594 #ifdef DASHDASJKHASDJK
595 class RaycastingQueryBox : public QueryBox
596 {
597         
598         SimdVector3 m_aabbMin;
599         
600         SimdVector3 m_aabbMax;
601         
602         
603         
604 public:
605         
606         RaycastCallback m_raycastCallback;
607         
608         
609         RaycastingQueryBox(QueryBoxConstructionInfo& ci,const SimdVector3& from,const SimdVector3& to)
610                 : QueryBox(ci),
611                 m_raycastCallback(from,to)
612         {
613                 for (int i=0;i<3;i++)
614                 {
615                         float fromI = from[i];
616                         float toI = to[i];
617                         if (fromI < toI)
618                         {
619                                 m_aabbMin[i] = fromI;
620                                 m_aabbMax[i] = toI;
621                         } else
622                         {
623                                 m_aabbMin[i] = toI;
624                                 m_aabbMax[i] = fromI;
625                         }
626                 }
627                 
628         }
629         virtual void AddCollider( BroadphaseProxy* proxy)
630         {
631                 //perform raycast if wanted, and update the m_hitFraction
632                 
633                 if (proxy->GetClientObjectType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)
634                 {
635                         //do it
636                         RigidBody* body = (RigidBody*)proxy->m_clientObject;
637                         TriangleMeshInterface* meshInterface = (TriangleMeshInterface*)
638                                 body->m_minkowski1;
639                         
640                         //if the hit is closer, record the proxy!
641                         float curFraction = m_raycastCallback.m_hitFraction;
642                         
643                         meshInterface->ProcessAllTriangles(&m_raycastCallback,m_aabbMin,m_aabbMax);
644                         
645                         if (m_raycastCallback.m_hitFraction < curFraction)
646                         {
647                                 m_raycastCallback.m_hitProxy = proxy;
648                         }
649                         
650                 }
651                 
652         }
653 };
654
655 struct InternalVehicleRaycaster : public VehicleRaycaster
656 {
657         
658         CcdPhysicsEnvironment* m_env;
659         
660 public:
661         
662         InternalVehicleRaycaster(CcdPhysicsEnvironment* env)
663                 :       m_env(env)
664         {
665                 
666         }
667         
668         virtual void* CastRay(const SimdVector3& from,const SimdVector3& to, VehicleRaycasterResult& result)
669         {
670
671                 return 0;
672         }
673         
674 };
675
676 #endif 
677 int                     CcdPhysicsEnvironment::createConstraint(class PHY_IPhysicsController* ctrl0,class PHY_IPhysicsController* ctrl1,PHY_ConstraintType type,
678                                                                                                                 float pivotX,float pivotY,float pivotZ,
679                                                                                                                 float axisX,float axisY,float axisZ)
680 {
681         
682         
683         CcdPhysicsController* c0 = (CcdPhysicsController*)ctrl0;
684         CcdPhysicsController* c1 = (CcdPhysicsController*)ctrl1;
685         
686         RigidBody* rb0 = c0 ? c0->GetRigidBody() : 0;
687         RigidBody* rb1 = c1 ? c1->GetRigidBody() : 0;
688         
689         ASSERT(rb0);
690         
691         SimdVector3 pivotInA(pivotX,pivotY,pivotZ);
692         SimdVector3 pivotInB = rb1 ? rb1->getCenterOfMassTransform().inverse()(rb0->getCenterOfMassTransform()(pivotInA)) : pivotInA;
693         
694         switch (type)
695         {
696         case PHY_POINT2POINT_CONSTRAINT:
697                 {
698                         
699                         Point2PointConstraint* p2p = 0;
700                         
701                         if (rb1)
702                         {
703                                 p2p = new Point2PointConstraint(*rb0,
704                                         *rb1,pivotInA,pivotInB);
705                         } else
706                         {
707                                 p2p = new Point2PointConstraint(*rb0,
708                                         pivotInA);
709                         }
710                         
711                         m_p2pConstraints.push_back(p2p);
712                         return 0;
713                         
714                         break;
715                 }
716         default:
717                 {
718                 }
719         };
720         
721         //RigidBody& rbA,RigidBody& rbB, const SimdVector3& pivotInA,const SimdVector3& pivotInB
722         
723         return 0;
724         
725 }
726
727 void            CcdPhysicsEnvironment::removeConstraint(int constraintid)
728 {
729         
730         Point2PointConstraint* p2p = (Point2PointConstraint*) constraintid;
731         
732         std::vector<Point2PointConstraint*>::iterator i =
733                 std::find(m_p2pConstraints.begin(), m_p2pConstraints.end(), p2p);
734         
735         if (!(i == m_p2pConstraints.end()) )
736         {
737                 std::swap(*i, m_p2pConstraints.back());
738                 m_p2pConstraints.pop_back();
739         }
740         
741 }
742 PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IPhysicsController* ignoreClient, float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
743                                                                 float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ)
744 {
745
746
747 //      m_broadphase->cast(
748         return 0;
749 }
750
751
752
753 int     CcdPhysicsEnvironment::getNumContactPoints()
754 {
755         return 0;
756 }
757
758 void CcdPhysicsEnvironment::getContactPoint(int i,float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ)
759 {
760         
761 }
762
763
764
765
766
767 Dispatcher* CcdPhysicsEnvironment::GetDispatcher()
768 {
769         return m_dispatcher;
770 }
771
772 CcdPhysicsEnvironment::~CcdPhysicsEnvironment()
773 {
774         
775         
776         m_vehicles.clear();
777         
778         //m_broadphase->DestroyScene();
779         //delete broadphase ? release reference on broadphase ?
780         
781         //first delete scene, then dispatcher, because pairs have to release manifolds on the dispatcher
782         delete m_dispatcher;
783         
784 }
785
786
787 int     CcdPhysicsEnvironment::GetNumControllers()
788 {
789         return m_controllers.size();
790 }
791
792
793 CcdPhysicsController* CcdPhysicsEnvironment::GetPhysicsController( int index)
794 {
795         return m_controllers[index];
796 }
797
798
799 int     CcdPhysicsEnvironment::GetNumManifolds() const
800 {
801         return m_dispatcher->GetNumManifolds();
802 }
803
804 const PersistentManifold*       CcdPhysicsEnvironment::GetManifold(int index) const
805 {
806         return m_dispatcher->GetManifoldByIndexInternal(index);
807 }