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