internal override double calculateTimeOfImpact( btCollisionObject col0, btCollisionObject col1, btDispatcherInfo dispatchInfo, btManifoldResult resultOut ) { //(void)resultOut; //(void)dispatchInfo; ///Rather then checking ALL pairs, only calculate TOI when motion exceeds threshold ///Linear motion for one of objects needs to exceed m_ccdSquareMotionThreshold ///col0.m_worldTransform, double resultFraction = btScalar.BT_ONE; btVector3 tmp; col0.m_interpolationWorldTransform.m_origin.Sub( ref col0.m_worldTransform.m_origin, out tmp ); double squareMot0 = ( tmp ).length2(); col1.m_interpolationWorldTransform.m_origin.Sub( ref col1.m_worldTransform.m_origin, out tmp ); double squareMot1 = ( tmp ).length2(); if( squareMot0 < col0.getCcdSquareMotionThreshold() && squareMot1 < col1.getCcdSquareMotionThreshold() ) return resultFraction; if( disableCcd ) return btScalar.BT_ONE; //An adhoc way of testing the Continuous Collision Detection algorithms //One object is approximated as a sphere, to simplify things //Starting in penetration should report no time of impact //For proper CCD, better accuracy and handling of 'allowed' penetration should be added //also the mainloop of the physics should have a kind of toi queue (something like Brian Mirtich's application of Timewarp for Rigidbodies) /// Convex0 against sphere for Convex1 { btConvexShape convex0 = (btConvexShape)col0.getCollisionShape(); using( btSphereShape sphere1 = BulletGlobals.SphereShapePool.Get() ) { sphere1.Initialize( col1.getCcdSweptSphereRadius() ); //todo: allow non-zero sphere sizes, for better approximation btConvexCast.CastResult result = BulletGlobals.CastResultPool.Get(); btVoronoiSimplexSolver voronoiSimplex = BulletGlobals.VoronoiSimplexSolverPool.Get(); //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); ///Simplification, one object is simplified as a sphere btGjkConvexCast ccd1 = BulletGlobals.GjkConvexCastPool.Get(); ccd1.Initialize( convex0, sphere1, voronoiSimplex ); //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); if( ccd1.calcTimeOfImpact( ref col0.m_worldTransform, ref col0.m_interpolationWorldTransform, ref col1.m_worldTransform, ref col1.m_interpolationWorldTransform, result ) ) { //store result.m_fraction in both bodies if( col0.getHitFraction() > result.m_fraction ) col0.setHitFraction( result.m_fraction ); if( col1.getHitFraction() > result.m_fraction ) col1.setHitFraction( result.m_fraction ); if( resultFraction > result.m_fraction ) resultFraction = result.m_fraction; } BulletGlobals.GjkConvexCastPool.Free( ccd1 ); } } /// Sphere (for convex0) against Convex1 { btConvexShape convex1 = (btConvexShape)( col1.getCollisionShape() ); using( btSphereShape sphere0 = BulletGlobals.SphereShapePool.Get() ) { sphere0.Initialize( col0.getCcdSweptSphereRadius() ); //todo: allow non-zero sphere sizes, for better approximation btConvexCast.CastResult result = BulletGlobals.CastResultPool.Get(); btVoronoiSimplexSolver voronoiSimplex = BulletGlobals.VoronoiSimplexSolverPool.Get(); //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); ///Simplification, one object is simplified as a sphere btGjkConvexCast ccd1 = BulletGlobals.GjkConvexCastPool.Get(); ccd1.Initialize( sphere0, convex1, voronoiSimplex ); //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); if( ccd1.calcTimeOfImpact( ref col0.m_worldTransform, ref col0.m_interpolationWorldTransform, ref col1.m_worldTransform, ref col1.m_interpolationWorldTransform, result ) ) { //store result.m_fraction in both bodies if( col0.getHitFraction() > result.m_fraction ) col0.setHitFraction( result.m_fraction ); if( col1.getHitFraction() > result.m_fraction ) col1.setHitFraction( result.m_fraction ); if( resultFraction > result.m_fraction ) resultFraction = result.m_fraction; } BulletGlobals.GjkConvexCastPool.Free( ccd1 ); } } return resultFraction; }