///<summary>
        /// Notifies the position updater that an updateable has changed state.
        ///</summary>
        ///<param name="updateable">Updateable with changed state.</param>
        ///<param name="previousMode">Previous state the updateable was in.</param>
        public void UpdateableModeChanged(ICCDPositionUpdateable updateable, PositionUpdateMode previousMode)
        {
            switch (previousMode)
            {
            case PositionUpdateMode.Discrete:
                discreteUpdateables.Remove(updateable);
                break;

            case PositionUpdateMode.Passive:
                passiveUpdateables.Remove(updateable);
                break;

            case PositionUpdateMode.Continuous:
                continuousUpdateables.Remove(updateable);
                break;
            }

            switch (updateable.PositionUpdateMode)
            {
            case PositionUpdateMode.Discrete:
                discreteUpdateables.Add(updateable);
                break;

            case PositionUpdateMode.Passive:
                passiveUpdateables.Add(updateable);
                break;

            case PositionUpdateMode.Continuous:
                continuousUpdateables.Add(updateable);
                break;
            }
        }
Exemple #2
0
        ///<summary>
        /// Updates the time of impact for the pair.
        ///</summary>
        ///<param name="requester">Collidable requesting the update.</param>
        ///<param name="dt">Timestep duration.</param>
        public override void UpdateTimeOfImpact(Collidable requester, float dt)
        {
            BroadPhaseOverlap  overlap      = BroadPhaseOverlap;
            PositionUpdateMode triangleMode = triangle.entity == null
                ? PositionUpdateMode.Discrete
                : triangle.entity.PositionUpdateMode;
            PositionUpdateMode convexMode =
                convex.entity == null ? PositionUpdateMode.Discrete : convex.entity.PositionUpdateMode;

            if (
                (overlap.entryA.IsActive || overlap.entryB.IsActive) && //At least one has to be active.
                (
                    convexMode == PositionUpdateMode.Continuous &&      //If both are continuous, only do the process for A.
                    triangleMode == PositionUpdateMode.Continuous &&
                    overlap.entryA == requester ||
                    (convexMode == PositionUpdateMode.Continuous) ^ //If only one is continuous, then we must do it.
                    (triangleMode == PositionUpdateMode.Continuous)
                )
                )
            {
                //Only perform the test if the minimum radii are small enough relative to the size of the velocity.
                Vector3 velocity;
                if (convexMode == PositionUpdateMode.Discrete)
                //Triangle is static for the purposes of this continuous test.
                {
                    velocity = triangle.entity.linearVelocity;
                }
                else if (triangleMode == PositionUpdateMode.Discrete)
                //Convex is static for the purposes of this continuous test.
                {
                    Vector3.Negate(ref convex.entity.linearVelocity, out velocity);
                }
                else
                //Both objects are moving.
                {
                    Vector3.Subtract(ref triangle.entity.linearVelocity, ref convex.entity.linearVelocity,
                                     out velocity);
                }

                Vector3.Multiply(ref velocity, dt, out velocity);
                float velocitySquared = velocity.LengthSquared();

                float minimumRadiusA = convex.Shape.MinimumRadius * MotionSettings.CoreShapeScaling;
                timeOfImpact = 1;
                if (minimumRadiusA * minimumRadiusA < velocitySquared)
                {
                    //Spherecast A against B.
                    RayHit rayHit;
                    if (GJKToolbox.CCDSphereCast(new Ray(convex.worldTransform.Position, -velocity), minimumRadiusA,
                                                 triangle.Shape, ref triangle.worldTransform, timeOfImpact, out rayHit))
                    {
                        if (triangle.Shape.sidedness != TriangleSidedness.DoubleSided)
                        {
                            //Only perform sweep if the object is in danger of hitting the object.
                            //Triangles can be one sided, so check the impact normal against the triangle normal.
                            Vector3 AB, AC;
                            Vector3.Subtract(ref triangle.Shape.vB, ref triangle.Shape.vA, out AB);
                            Vector3.Subtract(ref triangle.Shape.vC, ref triangle.Shape.vA, out AC);
                            Vector3 normal;
                            Vector3.Cross(ref AB, ref AC, out normal);

                            float dot;
                            Vector3.Dot(ref rayHit.Normal, ref normal, out dot);
                            if (triangle.Shape.sidedness == TriangleSidedness.Counterclockwise && dot < 0 ||
                                triangle.Shape.sidedness == TriangleSidedness.Clockwise && dot > 0)
                            {
                                timeOfImpact = rayHit.T;
                            }
                        }
                        else
                        {
                            timeOfImpact = rayHit.T;
                        }
                    }
                }

                //TECHNICALLY, the triangle should be casted too.  But, given the way triangles are usually used and their tiny minimum radius, ignoring it is usually just fine.
                //var minimumRadiusB = triangle.minimumRadius * MotionSettings.CoreShapeScaling;
                //if (minimumRadiusB * minimumRadiusB < velocitySquared)
                //{
                //    //Spherecast B against A.
                //    RayHit rayHit;
                //    if (GJKToolbox.SphereCast(new Ray(triangle.entity.position, velocity), minimumRadiusB, convex.Shape, ref convex.worldTransform, 1, out rayHit) &&
                //        rayHit.T < timeOfImpact)
                //    {
                //        if (triangle.Shape.sidedness != TriangleSidedness.DoubleSided)
                //        {
                //            float dot;
                //            Vector3.Dot(ref rayHit.Normal, ref normal, out dot);
                //            if (dot > 0)
                //            {
                //                timeOfImpact = rayHit.T;
                //            }
                //        }
                //        else
                //        {
                //            timeOfImpact = rayHit.T;
                //        }
                //    }
                //}

                //If it's intersecting, throw our hands into the air and give up.
                //This is generally a perfectly acceptable thing to do, since it's either sitting
                //inside another object (no ccd makes sense) or we're still in an intersecting case
                //from a previous frame where CCD took place and a contact should have been created
                //to deal with interpenetrating velocity.  Sometimes that contact isn't sufficient,
                //but it's good enough.
                if (timeOfImpact == 0)
                {
                    timeOfImpact = 1;
                }
            }
        }
        ///<summary>
        /// Updates the time of impact for the pair.
        ///</summary>
        ///<param name="requester">Collidable requesting the update.</param>
        ///<param name="dt">Timestep duration.</param>
        public override void UpdateTimeOfImpact(Collidable requester, float dt)
        {
            BroadPhaseOverlap  overlap  = BroadPhaseOverlap;
            PositionUpdateMode meshMode = mobileMesh.entity == null
                ? PositionUpdateMode.Discrete
                : mobileMesh.entity.PositionUpdateMode;
            PositionUpdateMode convexMode =
                convex.entity == null ? PositionUpdateMode.Discrete : convex.entity.PositionUpdateMode;

            if (
                (mobileMesh.IsActive || convex.IsActive) &&        //At least one has to be active.
                (
                    convexMode == PositionUpdateMode.Continuous && //If both are continuous, only do the process for A.
                    meshMode == PositionUpdateMode.Continuous &&
                    overlap.entryA == requester ||
                    (convexMode == PositionUpdateMode.Continuous) ^ //If only one is continuous, then we must do it.
                    (meshMode == PositionUpdateMode.Continuous)
                )
                )
            {
                //TODO: This system could be made more robust by using a similar region-based rejection of edges.
                //CCD events are awfully rare under normal circumstances, so this isn't usually an issue.

                //Only perform the test if the minimum radii are small enough relative to the size of the velocity.
                Vector3 velocity;
                if (convexMode == PositionUpdateMode.Discrete)
                //Convex is static for the purposes of CCD.
                {
                    Vector3.Negate(ref mobileMesh.entity.linearVelocity, out velocity);
                }
                else if (meshMode == PositionUpdateMode.Discrete)
                //Mesh is static for the purposes of CCD.
                {
                    velocity = convex.entity.linearVelocity;
                }
                else
                //Both objects can move.
                {
                    Vector3.Subtract(ref convex.entity.linearVelocity, ref mobileMesh.entity.linearVelocity,
                                     out velocity);
                }

                Vector3.Multiply(ref velocity, dt, out velocity);
                float velocitySquared = velocity.LengthSquared();

                float minimumRadius = convex.Shape.MinimumRadius * MotionSettings.CoreShapeScaling;
                timeOfImpact = 1;
                if (minimumRadius * minimumRadius < velocitySquared)
                {
                    TriangleSidedness sidedness = mobileMesh.Shape.Sidedness;
                    Matrix3x3         orientation;
                    Matrix3x3.CreateFromQuaternion(ref mobileMesh.worldTransform.Orientation, out orientation);
                    TriangleShape triangle = PhysicsThreadResources.GetTriangle();
                    triangle.collisionMargin = 0;
                    //Spherecast against all triangles to find the earliest time.
                    for (int i = 0; i < MeshManifold.overlappedTriangles.Count; i++)
                    {
                        MeshBoundingBoxTreeData data = mobileMesh.Shape.TriangleMesh.Data;
                        int triangleIndex            = MeshManifold.overlappedTriangles.Elements[i];
                        data.GetTriangle(triangleIndex, out triangle.vA, out triangle.vB, out triangle.vC);
                        Matrix3x3.Transform(ref triangle.vA, ref orientation, out triangle.vA);
                        Matrix3x3.Transform(ref triangle.vB, ref orientation, out triangle.vB);
                        Matrix3x3.Transform(ref triangle.vC, ref orientation, out triangle.vC);
                        Vector3.Add(ref triangle.vA, ref mobileMesh.worldTransform.Position, out triangle.vA);
                        Vector3.Add(ref triangle.vB, ref mobileMesh.worldTransform.Position, out triangle.vB);
                        Vector3.Add(ref triangle.vC, ref mobileMesh.worldTransform.Position, out triangle.vC);
                        //Put the triangle into 'localish' space of the convex.
                        Vector3.Subtract(ref triangle.vA, ref convex.worldTransform.Position, out triangle.vA);
                        Vector3.Subtract(ref triangle.vB, ref convex.worldTransform.Position, out triangle.vB);
                        Vector3.Subtract(ref triangle.vC, ref convex.worldTransform.Position, out triangle.vC);

                        RayHit rayHit;
                        if (GJKToolbox.CCDSphereCast(new Ray(Toolbox.ZeroVector, velocity), minimumRadius, triangle,
                                                     ref Toolbox.RigidIdentity, timeOfImpact, out rayHit) &&
                            rayHit.T > Toolbox.BigEpsilon)
                        {
                            if (sidedness != TriangleSidedness.DoubleSided)
                            {
                                Vector3 AB, AC;
                                Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB);
                                Vector3.Subtract(ref triangle.vC, ref triangle.vA, out AC);
                                Vector3 normal;
                                Vector3.Cross(ref AB, ref AC, out normal);
                                float dot;
                                Vector3.Dot(ref normal, ref rayHit.Normal, out dot);
                                //Only perform sweep if the object is in danger of hitting the object.
                                //Triangles can be one sided, so check the impact normal against the triangle normal.
                                if (sidedness == TriangleSidedness.Counterclockwise && dot < 0 ||
                                    sidedness == TriangleSidedness.Clockwise && dot > 0)
                                {
                                    timeOfImpact = rayHit.T;
                                }
                            }
                            else
                            {
                                timeOfImpact = rayHit.T;
                            }
                        }
                    }

                    PhysicsThreadResources.GiveBack(triangle);
                }
            }
        }
Exemple #4
0
        ///<summary>
        /// Updates the time of impact for the pair.
        ///</summary>
        ///<param name="requester">Collidable requesting the update.</param>
        ///<param name="dt">Timestep duration.</param>
        public override void UpdateTimeOfImpact(Collidable requester, float dt)
        {
            ConvexCollidable   collidableA = CollidableA as ConvexCollidable;
            ConvexCollidable   collidableB = CollidableB as ConvexCollidable;
            PositionUpdateMode modeA       = collidableA.entity == null
                ? PositionUpdateMode.Discrete
                : collidableA.entity.PositionUpdateMode;
            PositionUpdateMode modeB = collidableB.entity == null
                ? PositionUpdateMode.Discrete
                : collidableB.entity.PositionUpdateMode;

            BroadPhaseOverlap overlap = BroadPhaseOverlap;

            if (
                (overlap.entryA.IsActive || overlap.entryB.IsActive) && //At least one has to be active.
                (
                    modeA == PositionUpdateMode.Continuous &&           //If both are continuous, only do the process for A.
                    modeB == PositionUpdateMode.Continuous &&
                    overlap.entryA == requester ||
                    (modeA == PositionUpdateMode.Continuous) ^ //If only one is continuous, then we must do it.
                    (modeB == PositionUpdateMode.Continuous)
                )
                )
            {
                //Only perform the test if the minimum radii are small enough relative to the size of the velocity.
                //Discrete objects have already had their linear motion integrated, so don't use their velocity.
                Vector3 velocity;
                if (modeA == PositionUpdateMode.Discrete)
                //CollidableA is static for the purposes of this continuous test.
                {
                    velocity = collidableB.entity.linearVelocity;
                }
                else if (modeB == PositionUpdateMode.Discrete)
                //CollidableB is static for the purposes of this continuous test.
                {
                    Vector3.Negate(ref collidableA.entity.linearVelocity, out velocity);
                }
                else
                //Both objects are moving.
                {
                    Vector3.Subtract(ref collidableB.entity.linearVelocity, ref collidableA.entity.linearVelocity,
                                     out velocity);
                }

                Vector3.Multiply(ref velocity, dt, out velocity);
                float velocitySquared = velocity.LengthSquared();

                float minimumRadiusA = collidableA.Shape.MinimumRadius * MotionSettings.CoreShapeScaling;
                timeOfImpact = 1;
                if (minimumRadiusA * minimumRadiusA < velocitySquared)
                {
                    //Spherecast A against B.
                    RayHit rayHit;
                    if (GJKToolbox.CCDSphereCast(new Ray(collidableA.worldTransform.Position, -velocity),
                                                 minimumRadiusA, collidableB.Shape, ref collidableB.worldTransform, timeOfImpact, out rayHit))
                    {
                        timeOfImpact = rayHit.T;
                    }
                }

                float minimumRadiusB = collidableB.Shape.MinimumRadius * MotionSettings.CoreShapeScaling;
                if (minimumRadiusB * minimumRadiusB < velocitySquared)
                {
                    //Spherecast B against A.
                    RayHit rayHit;
                    if (GJKToolbox.CCDSphereCast(new Ray(collidableB.worldTransform.Position, velocity), minimumRadiusB,
                                                 collidableA.Shape, ref collidableA.worldTransform, timeOfImpact, out rayHit))
                    {
                        timeOfImpact = rayHit.T;
                    }
                }

                //If it's intersecting, throw our hands into the air and give up.
                //This is generally a perfectly acceptable thing to do, since it's either sitting
                //inside another object (no ccd makes sense) or we're still in an intersecting case
                //from a previous frame where CCD took place and a contact should have been created
                //to deal with interpenetrating velocity.  Sometimes that contact isn't sufficient,
                //but it's good enough.
                if (timeOfImpact == 0)
                {
                    timeOfImpact = 1;
                }
            }
        }
        ///<summary>
        /// Notifies the position updater that an updateable has changed state.
        ///</summary>
        ///<param name="updateable">Updateable with changed state.</param>
        ///<param name="previousMode">Previous state the updateable was in.</param>
        public void UpdateableModeChanged(ICCDPositionUpdateable updateable, PositionUpdateMode previousMode)
        {
            switch (previousMode)
            {
                case PositionUpdateMode.Discrete:
                    discreteUpdateables.Remove(updateable);
                    break;
                case PositionUpdateMode.Passive:
                    passiveUpdateables.Remove(updateable);
                    break;
                case PositionUpdateMode.Continuous:
                    continuousUpdateables.Remove(updateable);
                    break;
            }

            switch (updateable.PositionUpdateMode)
            {
                case PositionUpdateMode.Discrete:
                    discreteUpdateables.Add(updateable);
                    break;
                case PositionUpdateMode.Passive:
                    passiveUpdateables.Add(updateable);
                    break;
                case PositionUpdateMode.Continuous:
                    continuousUpdateables.Add(updateable);
                    break;
            }
        }