/// <summary> /// cast a convex against another convex object /// </summary> /// <param name="fromA"></param> /// <param name="toA"></param> /// <param name="fromB"></param> /// <param name="toB"></param> /// <param name="result"></param> /// <returns></returns> public bool CalcTimeOfImpact(Matrix fromA, Matrix toA, Matrix fromB, Matrix toB, CastResult result) { MinkowskiSumShape combined = new MinkowskiSumShape(_convexA, _convexB); Matrix rayFromLocalA = MathHelper.InvertMatrix(fromA) * fromB; Matrix rayToLocalA = MathHelper.InvertMatrix(toA) * toB; Matrix transformA = fromA; Matrix transformB = fromB; transformA.Translation = new Vector3(0, 0, 0); transformB.Translation = new Vector3(0, 0, 0); combined.TransformA = transformA; combined.TransformB = transformB; float radius = 0.01f; float lambda = 0; Vector3 s = rayFromLocalA.Translation; Vector3 r = rayToLocalA.Translation - rayFromLocalA.Translation; Vector3 x = s; Vector3 n = new Vector3(); Vector3 c = new Vector3(); bool hasResult = false; float lastLambda = lambda; IConvexPenetrationDepthSolver penSolver = null; Matrix identityTransform = Matrix.Identity; SphereShape raySphere = new SphereShape(0.0f); raySphere.Margin = 0.0f; Matrix sphereTransform = Matrix.Identity; sphereTransform.Translation = rayFromLocalA.Translation; result.DrawCoordSystem(sphereTransform); { PointCollector pointCollector = new PointCollector(); GjkPairDetector gjk = new GjkPairDetector(raySphere, combined, _simplexSolver, penSolver); GjkPairDetector.ClosestPointInput input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); input.TransformA = sphereTransform; input.TransformB = identityTransform; gjk.GetClosestPoints(input, pointCollector, null); hasResult = pointCollector.HasResult; c = pointCollector.PointInWorld; n = pointCollector.NormalOnBInWorld; } if (hasResult) { float dist = (c - x).Length(); if (dist < radius) { lastLambda = 1.0f; } while (dist > radius) { n = x - c; float dot = Vector3.Dot(n, r); if (dot >= -(MathHelper.Epsilon * MathHelper.Epsilon)) { return(false); } lambda = lambda - Vector3.Distance(n, n) / dot; if (lambda <= lastLambda) { break; } lastLambda = lambda; x = s + lambda * r; sphereTransform.Translation = x; result.DrawCoordSystem(sphereTransform); PointCollector pointCollector = new PointCollector(); GjkPairDetector gjk = new GjkPairDetector(raySphere, combined, _simplexSolver, penSolver); GjkPairDetector.ClosestPointInput input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); input.TransformA = sphereTransform; input.TransformB = identityTransform; gjk.GetClosestPoints(input, pointCollector, null); if (pointCollector.HasResult) { if (pointCollector.Distance < 0.0f) { result.Fraction = lastLambda; result.Normal = n; return(true); } c = pointCollector.PointInWorld; dist = (c - x).Length(); } else { return(false); } } if (lastLambda < 1.0f) { result.Fraction = lastLambda; result.Normal = n; return(true); } } return(false); }
public override float CalculateTimeOfImpact(CollisionObject colA, CollisionObject colB, DispatcherInfo dispatchInfo, ManifoldResult resultOut) { //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, float resultFraction = 1f; float squareMotA = (colA.InterpolationWorldTransform.Translation - colA.WorldTransform.Translation).LengthSquared(); float squareMotB = (colB.InterpolationWorldTransform.Translation - colB.WorldTransform.Translation).LengthSquared(); if (squareMotA < colA.CcdSquareMotionThreshold && squareMotB < colB.CcdSquareMotionThreshold) return resultFraction; if (DisableCcd) return 1f; //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 { ConvexShape convexA = colA.CollisionShape as ConvexShape; SphereShape sphereB = new SphereShape(colB.CcdSweptSphereRadius); //todo: allow non-zero sphere sizes, for better approximation CastResult result = new CastResult(); VoronoiSimplexSolver voronoiSimplex = new VoronoiSimplexSolver(); //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); //Simplification, one object is simplified as a sphere GjkConvexCast ccdB = new GjkConvexCast(convexA, sphereB, voronoiSimplex); //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); if (ccdB.CalcTimeOfImpact(colA.WorldTransform, colA.InterpolationWorldTransform, colB.WorldTransform, colB.InterpolationWorldTransform, result)) { //store result.m_fraction in both bodies if (colA.HitFraction > result.Fraction) colA.HitFraction = result.Fraction; if (colB.HitFraction > result.Fraction) colB.HitFraction = result.Fraction; if (resultFraction > result.Fraction) resultFraction = result.Fraction; } } // Sphere (for convex0) against Convex1 { ConvexShape convexB = colB.CollisionShape as ConvexShape; SphereShape sphereA = new SphereShape(colA.CcdSweptSphereRadius); //todo: allow non-zero sphere sizes, for better approximation CastResult result = new CastResult(); VoronoiSimplexSolver voronoiSimplex = new VoronoiSimplexSolver(); //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); ///Simplification, one object is simplified as a sphere GjkConvexCast ccdB = new GjkConvexCast(sphereA, convexB, voronoiSimplex); //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); if (ccdB.CalcTimeOfImpact(colA.WorldTransform, colA.InterpolationWorldTransform, colB.WorldTransform, colB.InterpolationWorldTransform, result)) { //store result.m_fraction in both bodies if (colA.HitFraction > result.Fraction) colA.HitFraction = result.Fraction; if (colB.HitFraction > result.Fraction) colB.HitFraction = result.Fraction; if (resultFraction > result.Fraction) resultFraction = result.Fraction; } } return resultFraction; }
public bool CalcTimeOfImpact(Matrix fromA, Matrix toA, Matrix fromB, Matrix toB, CastResult result) { _simplexSolver.Reset(); // compute linear and angular velocity for this interval, to interpolate Vector3 linVelA = new Vector3(), angVelA = new Vector3(), linVelB = new Vector3(), angVelB = new Vector3(); TransformUtil.CalculateVelocity(fromA, toA, 1f, ref linVelA, ref angVelA); TransformUtil.CalculateVelocity(fromB, toB, 1f, ref linVelB, ref angVelB); float boundingRadiusA = _convexA.GetAngularMotionDisc(); float boundingRadiusB = _convexB.GetAngularMotionDisc(); float maxAngularProjectedVelocity = angVelA.Length() * boundingRadiusA + angVelB.Length() * boundingRadiusB; float radius = 0.001f; float lambda = 0f; Vector3 v = new Vector3(1f, 0f, 0f); int maxIter = MaxIterations; Vector3 n = new Vector3(); bool hasResult = false; Vector3 c; float lastLambda = lambda; //float epsilon = 0.001f; int numIter = 0; //first solution, using GJK Matrix identityTrans = Matrix.Identity; SphereShape raySphere = new SphereShape(0f); raySphere.Margin=0f; //result.drawCoordSystem(sphereTr); PointCollector pointCollector1 = new PointCollector(); GjkPairDetector gjk = new GjkPairDetector(_convexA, _convexB, (VoronoiSimplexSolver)_simplexSolver, _penetrationDepthSolver); GjkPairDetector.ClosestPointInput input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); //we don't use margins during CCD gjk.setIgnoreMargin(true); input.TransformA = fromA; input.TransformB = fromB; DiscreteCollisionDetectorInterface.Result r = (DiscreteCollisionDetectorInterface.Result)pointCollector1; gjk.GetClosestPoints(input, r, null); hasResult = pointCollector1.HasResult; c = pointCollector1.PointInWorld; if (hasResult) { float dist; dist = pointCollector1.Distance; n = pointCollector1.NormalOnBInWorld; //not close enough while (dist > radius) { numIter++; if (numIter > maxIter) return false; //todo: report a failure float dLambda = 0f; //calculate safe moving fraction from distance / (linear+rotational velocity) //float clippedDist = GEN_min(angularConservativeRadius,dist); //float clippedDist = dist; float projectedLinearVelocity = Vector3.Dot(linVelB - linVelA, n); dLambda = dist / (projectedLinearVelocity + maxAngularProjectedVelocity); lambda = lambda + dLambda; if (lambda > 1f) return false; if (lambda < 0f) return false; //todo: next check with relative epsilon if (lambda <= lastLambda) break; lastLambda = lambda; //interpolate to next lambda Matrix interpolatedTransA = new Matrix(), interpolatedTransB = new Matrix(), relativeTrans; TransformUtil.IntegrateTransform(fromA, linVelA, angVelA, lambda, ref interpolatedTransA); TransformUtil.IntegrateTransform(fromB, linVelB, angVelB, lambda, ref interpolatedTransB); relativeTrans = MathHelper.InverseTimes(interpolatedTransB, interpolatedTransA); result.DebugDraw(lambda); PointCollector pointCollector = new PointCollector(); gjk = new GjkPairDetector(_convexA, _convexB, (VoronoiSimplexSolver)_simplexSolver, _penetrationDepthSolver); input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); input.TransformA = interpolatedTransA; input.TransformB = interpolatedTransB; // !!!!!!!!!! r = (DiscreteCollisionDetectorInterface.Result)pointCollector1; gjk.GetClosestPoints(input, r, null); if (pointCollector.HasResult) { if (pointCollector.Distance < 0f) { //degenerate ?! result.Fraction = lastLambda; result.Normal = n; return true; } c = pointCollector.PointInWorld; dist = pointCollector.Distance; } else { //?? return false; } } result.Fraction = lambda; result.Normal = n; return true; } return false; }
// rayTestSingle performs a raycast call and calls the resultCallback. It is used internally by rayTest. // In a future implementation, we consider moving the ray test as a virtual method in CollisionShape. // This allows more customization. public static void RayTestSingle(Matrix rayFromTrans, Matrix rayToTrans, CollisionObject collisionObject, CollisionShape collisionShape, Matrix colObjWorldTransform, RayResultCallback resultCallback) { SphereShape pointShape=new SphereShape(0.0f); if (collisionShape.IsConvex) { CastResult castResult = new CastResult(); castResult.Fraction = 1f;//?? ConvexShape convexShape = collisionShape as ConvexShape; VoronoiSimplexSolver simplexSolver = new VoronoiSimplexSolver(); SubsimplexConvexCast convexCaster = new SubsimplexConvexCast(pointShape, convexShape, simplexSolver); //GjkConvexCast convexCaster(&pointShape,convexShape,&simplexSolver); //ContinuousConvexCollision convexCaster(&pointShape,convexShape,&simplexSolver,0); if (convexCaster.CalcTimeOfImpact(rayFromTrans, rayToTrans, colObjWorldTransform, colObjWorldTransform, castResult)) { //add hit if (castResult.Normal.LengthSquared() > 0.0001f) { castResult.Normal.Normalize(); if (castResult.Fraction < resultCallback.ClosestHitFraction) { CollisionWorld.LocalRayResult localRayResult = new LocalRayResult ( collisionObject, new LocalShapeInfo(), castResult.Normal, castResult.Fraction ); resultCallback.AddSingleResult(localRayResult); } } } else { if (collisionShape.IsConcave) { TriangleMeshShape triangleMesh = collisionShape as TriangleMeshShape; Matrix worldTocollisionObject = MathHelper.InvertMatrix(colObjWorldTransform); Vector3 rayFromLocal = Vector3.TransformNormal(rayFromTrans.Translation, worldTocollisionObject); Vector3 rayToLocal = Vector3.TransformNormal(rayToTrans.Translation, worldTocollisionObject); BridgeTriangleRaycastCallback rcb = new BridgeTriangleRaycastCallback(rayFromLocal, rayToLocal, resultCallback, collisionObject, triangleMesh); rcb.HitFraction = resultCallback.ClosestHitFraction; Vector3 rayAabbMinLocal = rayFromLocal; MathHelper.SetMin(ref rayAabbMinLocal, rayToLocal); Vector3 rayAabbMaxLocal = rayFromLocal; MathHelper.SetMax(ref rayAabbMaxLocal, rayToLocal); triangleMesh.ProcessAllTriangles(rcb, rayAabbMinLocal, rayAabbMaxLocal); } else { //todo: use AABB tree or other BVH acceleration structure! if (collisionShape.IsCompound) { CompoundShape compoundShape = collisionShape as CompoundShape; for (int i = 0; i < compoundShape.ChildShapeCount; i++) { Matrix childTrans = compoundShape.GetChildTransform(i); CollisionShape childCollisionShape = compoundShape.GetChildShape(i); Matrix childWorldTrans = colObjWorldTransform * childTrans; RayTestSingle(rayFromTrans, rayToTrans, collisionObject, childCollisionShape, childWorldTrans, resultCallback); } } } } } }
/// <summary> /// cast a convex against another convex object /// </summary> /// <param name="fromA"></param> /// <param name="toA"></param> /// <param name="fromB"></param> /// <param name="toB"></param> /// <param name="result"></param> /// <returns></returns> public bool CalcTimeOfImpact(Matrix fromA, Matrix toA, Matrix fromB, Matrix toB, CastResult result) { MinkowskiSumShape combined = new MinkowskiSumShape(_convexA, _convexB); Matrix rayFromLocalA = MathHelper.InvertMatrix(fromA) * fromB; Matrix rayToLocalA = MathHelper.InvertMatrix(toA) * toB; Matrix transformA = fromA; Matrix transformB = fromB; transformA.Translation = new Vector3(0, 0, 0); transformB.Translation = new Vector3(0, 0, 0); combined.TransformA = transformA; combined.TransformB = transformB; float radius = 0.01f; float lambda = 0; Vector3 s = rayFromLocalA.Translation; Vector3 r = rayToLocalA.Translation - rayFromLocalA.Translation; Vector3 x = s; Vector3 n = new Vector3(); Vector3 c = new Vector3(); bool hasResult = false; float lastLambda = lambda; IConvexPenetrationDepthSolver penSolver = null; Matrix identityTransform = Matrix.Identity; SphereShape raySphere = new SphereShape(0.0f); raySphere.Margin=0.0f; Matrix sphereTransform = Matrix.Identity; sphereTransform.Translation = rayFromLocalA.Translation; result.DrawCoordSystem(sphereTransform); { PointCollector pointCollector = new PointCollector(); GjkPairDetector gjk = new GjkPairDetector(raySphere, combined, _simplexSolver, penSolver); GjkPairDetector.ClosestPointInput input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); input.TransformA = sphereTransform; input.TransformB = identityTransform; gjk.GetClosestPoints(input, pointCollector, null); hasResult = pointCollector.HasResult; c = pointCollector.PointInWorld; n = pointCollector.NormalOnBInWorld; } if (hasResult) { float dist = (c - x).Length(); if (dist < radius) { lastLambda = 1.0f; } while (dist > radius) { n = x - c; float dot = Vector3.Dot(n, r); if (dot >= -(MathHelper.Epsilon * MathHelper.Epsilon)) return false; lambda = lambda - Vector3.Distance(n, n) / dot; if (lambda <= lastLambda) break; lastLambda = lambda; x = s + lambda * r; sphereTransform.Translation = x; result.DrawCoordSystem(sphereTransform); PointCollector pointCollector = new PointCollector(); GjkPairDetector gjk = new GjkPairDetector(raySphere, combined, _simplexSolver, penSolver); GjkPairDetector.ClosestPointInput input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); input.TransformA = sphereTransform; input.TransformB = identityTransform; gjk.GetClosestPoints(input, pointCollector, null); if (pointCollector.HasResult) { if (pointCollector.Distance < 0.0f) { result.Fraction = lastLambda; result.Normal = n; return true; } c = pointCollector.PointInWorld; dist = (c - x).Length(); } else { return false; } } if (lastLambda < 1.0f) { result.Fraction = lastLambda; result.Normal = n; return true; } } return false; }
public void ProcessTriangle(Vector3[] triangle, int partId, int triangleIndex) { //do a swept sphere for now Matrix ident = Matrix.Identity; CastResult castResult = new CastResult(); castResult.Fraction = _hitFraction; SphereShape pointShape = new SphereShape(_ccdSphereRadius); TriangleShape triShape = new TriangleShape(triangle[0], triangle[1], triangle[2]); VoronoiSimplexSolver simplexSolver = new VoronoiSimplexSolver(); SubsimplexConvexCast convexCaster = new SubsimplexConvexCast(pointShape, triShape, simplexSolver); //GjkConvexCast convexCaster(&pointShape,convexShape,&simplexSolver); //ContinuousConvexCollision convexCaster(&pointShape,convexShape,&simplexSolver,0); //local space? if (convexCaster.CalcTimeOfImpact(_ccdSphereFromTrans, _ccdSphereToTrans, ident, ident, castResult)) { if (_hitFraction > castResult.Fraction) _hitFraction = castResult.Fraction; } }
/// <summary> /// SimsimplexConvexCast calculateTimeOfImpact calculates the time of impact+normal for the linear cast (sweep) between two moving objects. /// Precondition is that objects should not penetration/overlap at the start from the interval. Overlap can be tested using GjkPairDetector. /// </summary> /// <param name="fromA"></param> /// <param name="toA"></param> /// <param name="fromB"></param> /// <param name="toB"></param> /// <param name="result"></param> /// <returns></returns> public bool CalcTimeOfImpact(Matrix fromA, Matrix toA, Matrix fromB, Matrix toB, CastResult result) { MinkowskiSumShape convex = new MinkowskiSumShape(_convexA, _convexB); Matrix rayFromLocalA; Matrix rayToLocalA; rayFromLocalA = MathHelper.InvertMatrix(fromA) * fromB; rayToLocalA = MathHelper.InvertMatrix(toA) * toB; _simplexSolver.Reset(); convex.TransformB = rayFromLocalA; float lambda = 0; //todo: need to verify this: //because of minkowski difference, we need the inverse direction Vector3 s = -rayFromLocalA.Translation; Vector3 r = -(rayToLocalA.Translation - rayFromLocalA.Translation); Vector3 x = s; Vector3 v; Vector3 arbitraryPoint = convex.LocalGetSupportingVertex(r); v = x - arbitraryPoint; int maxIter = MaxIterations; Vector3 n = new Vector3(); float lastLambda = lambda; float dist2 = v.LengthSquared(); float epsilon = 0.0001f; Vector3 w, p; float VdotR; while ((dist2 > epsilon) && (maxIter-- != 0)) { p = convex.LocalGetSupportingVertex(v); w = x - p; float VdotW = Vector3.Dot(v, w); if (VdotW > 0) { VdotR = Vector3.Dot(v, r); if (VdotR >= -(MathHelper.Epsilon * MathHelper.Epsilon)) return false; else { lambda = lambda - VdotW / VdotR; x = s + lambda * r; _simplexSolver.Reset(); //check next line w = x - p; lastLambda = lambda; n = v; } } _simplexSolver.AddVertex(w, x, p); if (_simplexSolver.Closest(out v)) { dist2 = v.LengthSquared(); } else { dist2 = 0f; } } result.Fraction = lambda; result.Normal = n; return true; }
/// <summary> /// SimsimplexConvexCast calculateTimeOfImpact calculates the time of impact+normal for the linear cast (sweep) between two moving objects. /// Precondition is that objects should not penetration/overlap at the start from the interval. Overlap can be tested using GjkPairDetector. /// </summary> /// <param name="fromA"></param> /// <param name="toA"></param> /// <param name="fromB"></param> /// <param name="toB"></param> /// <param name="result"></param> /// <returns></returns> public bool CalcTimeOfImpact(Matrix fromA, Matrix toA, Matrix fromB, Matrix toB, CastResult result) { MinkowskiSumShape convex = new MinkowskiSumShape(_convexA, _convexB); Matrix rayFromLocalA; Matrix rayToLocalA; rayFromLocalA = MathHelper.InvertMatrix(fromA) * fromB; rayToLocalA = MathHelper.InvertMatrix(toA) * toB; _simplexSolver.Reset(); convex.TransformB = rayFromLocalA; float lambda = 0; //todo: need to verify this: //because of minkowski difference, we need the inverse direction Vector3 s = -rayFromLocalA.Translation; Vector3 r = -(rayToLocalA.Translation - rayFromLocalA.Translation); Vector3 x = s; Vector3 v; Vector3 arbitraryPoint = convex.LocalGetSupportingVertex(r); v = x - arbitraryPoint; int maxIter = MaxIterations; Vector3 n = new Vector3(); float lastLambda = lambda; float dist2 = v.LengthSquared(); float epsilon = 0.0001f; Vector3 w, p; float VdotR; while ((dist2 > epsilon) && (maxIter-- != 0)) { p = convex.LocalGetSupportingVertex(v); w = x - p; float VdotW = Vector3.Dot(v, w); if (VdotW > 0) { VdotR = Vector3.Dot(v, r); if (VdotR >= -(MathHelper.Epsilon * MathHelper.Epsilon)) { return(false); } else { lambda = lambda - VdotW / VdotR; x = s + lambda * r; _simplexSolver.Reset(); //check next line w = x - p; lastLambda = lambda; n = v; } } _simplexSolver.AddVertex(w, x, p); if (_simplexSolver.Closest(out v)) { dist2 = v.LengthSquared(); } else { dist2 = 0f; } } result.Fraction = lambda; result.Normal = n; return(true); }
public bool CalcTimeOfImpact(Matrix fromA, Matrix toA, Matrix fromB, Matrix toB, CastResult result) { _simplexSolver.Reset(); // compute linear and angular velocity for this interval, to interpolate Vector3 linVelA = new Vector3(), angVelA = new Vector3(), linVelB = new Vector3(), angVelB = new Vector3(); TransformUtil.CalculateVelocity(fromA, toA, 1f, ref linVelA, ref angVelA); TransformUtil.CalculateVelocity(fromB, toB, 1f, ref linVelB, ref angVelB); float boundingRadiusA = _convexA.GetAngularMotionDisc(); float boundingRadiusB = _convexB.GetAngularMotionDisc(); float maxAngularProjectedVelocity = angVelA.Length() * boundingRadiusA + angVelB.Length() * boundingRadiusB; float radius = 0.001f; float lambda = 0f; Vector3 v = new Vector3(1f, 0f, 0f); int maxIter = MaxIterations; Vector3 n = new Vector3(); bool hasResult = false; Vector3 c; float lastLambda = lambda; //float epsilon = 0.001f; int numIter = 0; //first solution, using GJK Matrix identityTrans = Matrix.Identity; SphereShape raySphere = new SphereShape(0f); raySphere.Margin = 0f; //result.drawCoordSystem(sphereTr); PointCollector pointCollector1 = new PointCollector(); GjkPairDetector gjk = new GjkPairDetector(_convexA, _convexB, (VoronoiSimplexSolver)_simplexSolver, _penetrationDepthSolver); GjkPairDetector.ClosestPointInput input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); //we don't use margins during CCD gjk.setIgnoreMargin(true); input.TransformA = fromA; input.TransformB = fromB; DiscreteCollisionDetectorInterface.Result r = (DiscreteCollisionDetectorInterface.Result)pointCollector1; gjk.GetClosestPoints(input, r, null); hasResult = pointCollector1.HasResult; c = pointCollector1.PointInWorld; if (hasResult) { float dist; dist = pointCollector1.Distance; n = pointCollector1.NormalOnBInWorld; //not close enough while (dist > radius) { numIter++; if (numIter > maxIter) { return(false); //todo: report a failure } float dLambda = 0f; //calculate safe moving fraction from distance / (linear+rotational velocity) //float clippedDist = GEN_min(angularConservativeRadius,dist); //float clippedDist = dist; float projectedLinearVelocity = Vector3.Dot(linVelB - linVelA, n); dLambda = dist / (projectedLinearVelocity + maxAngularProjectedVelocity); lambda = lambda + dLambda; if (lambda > 1f) { return(false); } if (lambda < 0f) { return(false); } //todo: next check with relative epsilon if (lambda <= lastLambda) { break; } lastLambda = lambda; //interpolate to next lambda Matrix interpolatedTransA = new Matrix(), interpolatedTransB = new Matrix(), relativeTrans; TransformUtil.IntegrateTransform(fromA, linVelA, angVelA, lambda, ref interpolatedTransA); TransformUtil.IntegrateTransform(fromB, linVelB, angVelB, lambda, ref interpolatedTransB); relativeTrans = MathHelper.InverseTimes(interpolatedTransB, interpolatedTransA); result.DebugDraw(lambda); PointCollector pointCollector = new PointCollector(); gjk = new GjkPairDetector(_convexA, _convexB, (VoronoiSimplexSolver)_simplexSolver, _penetrationDepthSolver); input = new DiscreteCollisionDetectorInterface.ClosestPointInput(); input.TransformA = interpolatedTransA; input.TransformB = interpolatedTransB; // !!!!!!!!!! r = (DiscreteCollisionDetectorInterface.Result)pointCollector1; gjk.GetClosestPoints(input, r, null); if (pointCollector.HasResult) { if (pointCollector.Distance < 0f) { //degenerate ?! result.Fraction = lastLambda; result.Normal = n; return(true); } c = pointCollector.PointInWorld; dist = pointCollector.Distance; } else { //?? return(false); } } result.Fraction = lambda; result.Normal = n; return(true); } return(false); }
// rayTestSingle performs a raycast call and calls the resultCallback. It is used internally by rayTest. // In a future implementation, we consider moving the ray test as a virtual method in CollisionShape. // This allows more customization. public static void RayTestSingle(Matrix rayFromTrans, Matrix rayToTrans, CollisionObject collisionObject, CollisionShape collisionShape, Matrix colObjWorldTransform, RayResultCallback resultCallback) { SphereShape pointShape = new SphereShape(0.0f); if (collisionShape.IsConvex) { CastResult castResult = new CastResult(); castResult.Fraction = 1f; //?? ConvexShape convexShape = collisionShape as ConvexShape; VoronoiSimplexSolver simplexSolver = new VoronoiSimplexSolver(); SubsimplexConvexCast convexCaster = new SubsimplexConvexCast(pointShape, convexShape, simplexSolver); //GjkConvexCast convexCaster(&pointShape,convexShape,&simplexSolver); //ContinuousConvexCollision convexCaster(&pointShape,convexShape,&simplexSolver,0); if (convexCaster.CalcTimeOfImpact(rayFromTrans, rayToTrans, colObjWorldTransform, colObjWorldTransform, castResult)) { //add hit if (castResult.Normal.LengthSquared() > 0.0001f) { castResult.Normal.Normalize(); if (castResult.Fraction < resultCallback.ClosestHitFraction) { CollisionWorld.LocalRayResult localRayResult = new LocalRayResult ( collisionObject, new LocalShapeInfo(), castResult.Normal, castResult.Fraction ); resultCallback.AddSingleResult(localRayResult); } } } else { if (collisionShape.IsConcave) { TriangleMeshShape triangleMesh = collisionShape as TriangleMeshShape; Matrix worldTocollisionObject = MathHelper.InvertMatrix(colObjWorldTransform); Vector3 rayFromLocal = Vector3.TransformNormal(rayFromTrans.Translation, worldTocollisionObject); Vector3 rayToLocal = Vector3.TransformNormal(rayToTrans.Translation, worldTocollisionObject); BridgeTriangleRaycastCallback rcb = new BridgeTriangleRaycastCallback(rayFromLocal, rayToLocal, resultCallback, collisionObject, triangleMesh); rcb.HitFraction = resultCallback.ClosestHitFraction; Vector3 rayAabbMinLocal = rayFromLocal; MathHelper.SetMin(ref rayAabbMinLocal, rayToLocal); Vector3 rayAabbMaxLocal = rayFromLocal; MathHelper.SetMax(ref rayAabbMaxLocal, rayToLocal); triangleMesh.ProcessAllTriangles(rcb, rayAabbMinLocal, rayAabbMaxLocal); } else { //todo: use AABB tree or other BVH acceleration structure! if (collisionShape.IsCompound) { CompoundShape compoundShape = collisionShape as CompoundShape; for (int i = 0; i < compoundShape.ChildShapeCount; i++) { Matrix childTrans = compoundShape.GetChildTransform(i); CollisionShape childCollisionShape = compoundShape.GetChildShape(i); Matrix childWorldTrans = colObjWorldTransform * childTrans; RayTestSingle(rayFromTrans, rayToTrans, collisionObject, childCollisionShape, childWorldTrans, resultCallback); } } } } } }
public override float CalculateTimeOfImpact(CollisionObject colA, CollisionObject colB, DispatcherInfo dispatchInfo, ManifoldResult resultOut) { //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, float resultFraction = 1f; float squareMotA = (colA.InterpolationWorldTransform.Translation - colA.WorldTransform.Translation).LengthSquared(); float squareMotB = (colB.InterpolationWorldTransform.Translation - colB.WorldTransform.Translation).LengthSquared(); if (squareMotA < colA.CcdSquareMotionThreshold && squareMotB < colB.CcdSquareMotionThreshold) { return(resultFraction); } if (DisableCcd) { return(1f); } //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 { ConvexShape convexA = colA.CollisionShape as ConvexShape; SphereShape sphereB = new SphereShape(colB.CcdSweptSphereRadius); //todo: allow non-zero sphere sizes, for better approximation CastResult result = new CastResult(); VoronoiSimplexSolver voronoiSimplex = new VoronoiSimplexSolver(); //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); //Simplification, one object is simplified as a sphere GjkConvexCast ccdB = new GjkConvexCast(convexA, sphereB, voronoiSimplex); //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); if (ccdB.CalcTimeOfImpact(colA.WorldTransform, colA.InterpolationWorldTransform, colB.WorldTransform, colB.InterpolationWorldTransform, result)) { //store result.m_fraction in both bodies if (colA.HitFraction > result.Fraction) { colA.HitFraction = result.Fraction; } if (colB.HitFraction > result.Fraction) { colB.HitFraction = result.Fraction; } if (resultFraction > result.Fraction) { resultFraction = result.Fraction; } } } // Sphere (for convex0) against Convex1 { ConvexShape convexB = colB.CollisionShape as ConvexShape; SphereShape sphereA = new SphereShape(colA.CcdSweptSphereRadius); //todo: allow non-zero sphere sizes, for better approximation CastResult result = new CastResult(); VoronoiSimplexSolver voronoiSimplex = new VoronoiSimplexSolver(); //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); ///Simplification, one object is simplified as a sphere GjkConvexCast ccdB = new GjkConvexCast(sphereA, convexB, voronoiSimplex); //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); if (ccdB.CalcTimeOfImpact(colA.WorldTransform, colA.InterpolationWorldTransform, colB.WorldTransform, colB.InterpolationWorldTransform, result)) { //store result.m_fraction in both bodies if (colA.HitFraction > result.Fraction) { colA.HitFraction = result.Fraction; } if (colB.HitFraction > result.Fraction) { colB.HitFraction = result.Fraction; } if (resultFraction > result.Fraction) { resultFraction = result.Fraction; } } } return(resultFraction); }