Exemple #1
0
        /// <summary>
        /// Gets the intersection between the convex shape and the ray.
        /// </summary>
        /// <param name="ray">Ray to test.</param>
        /// <param name="transform">Transform of the convex shape.</param>
        /// <param name="maximumLength">Maximum distance to travel in units of the ray direction's length.</param>
        /// <param name="hit">Ray hit data, if any.</param>
        /// <returns>Whether or not the ray hit the target.</returns>
        public virtual bool RayTest(ref Ray ray, ref RigidTransform transform, float maximumLength, out RayHit hit)
        {
            //RayHit newHit;
            //bool newBool = GJKToolbox.RayCast(ray, this, ref transform, maximumLength, out newHit);
            //RayHit oldHit;
            //bool oldBool = OldGJKVerifier.RayCastGJK(ray.Position, ray.Direction, maximumLength, this, transform, out oldHit.Location, out oldHit.Normal, out oldHit.T);
            //bool mprBool = MPRToolbox.RayCast(ray, maximumLength, this, ref transform, out hit);
            ////if (newBool != oldBool || ((newBool && oldBool) && Vector3.DistanceSquared(newHit.Location, hit.Location) > .01f))
            ////    Debug.WriteLine("break.");
            //return mprBool;

            //if (GJKToolbox.RayCast(ray, this, ref transform, maximumLength, out hit))
            //{
            //    //GJK toolbox doesn't normalize the hit normal; it's unnecessary for some other systems so it just saves on time.
            //    //It would be nice if ray tests always normalized it though.
            //    float length = hit.Normal.LengthSquared();
            //    if (length > Toolbox.Epsilon)
            //        Vector3.Divide(ref hit.Normal, (float) Math.Sqrt(length), out hit.Normal);
            //    else
            //        hit.Normal = new Vector3();
            //    return true;
            //}

            //return false;

            return(MPRToolbox.RayCast(ray, maximumLength, this, ref transform, out hit));
        }
Exemple #2
0
        /// <summary>
        /// Casts a convex shape against the collidable.
        /// </summary>
        /// <param name="castShape">Shape to cast.</param>
        /// <param name="startingTransform">Initial transform of the shape.</param>
        /// <param name="sweep">Sweep to apply to the shape.</param>
        /// <param name="hit">Hit data, if any.</param>
        /// <returns>Whether or not the cast hit anything.</returns>
        public override bool ConvexCast(CollisionShapes.ConvexShapes.ConvexShape castShape, ref RigidTransform startingTransform, ref Vector3 sweep, out RayHit hit)
        {
            hit = new RayHit();
            BoundingBox boundingBox;

            castShape.GetSweptLocalBoundingBox(ref startingTransform, ref worldTransform, ref sweep, out boundingBox);
            var tri         = PhysicsThreadResources.GetTriangle();
            var hitElements = CommonResources.GetIntList();

            if (this.Shape.TriangleMesh.Tree.GetOverlaps(boundingBox, hitElements))
            {
                hit.T = float.MaxValue;
                for (int i = 0; i < hitElements.Count; i++)
                {
                    Shape.TriangleMesh.Data.GetTriangle(hitElements[i], out tri.vA, out tri.vB, out tri.vC);
                    AffineTransform.Transform(ref tri.vA, ref worldTransform, out tri.vA);
                    AffineTransform.Transform(ref tri.vB, ref worldTransform, out tri.vB);
                    AffineTransform.Transform(ref tri.vC, ref worldTransform, out tri.vC);
                    Vector3 center;
                    Vector3.Add(ref tri.vA, ref tri.vB, out center);
                    Vector3.Add(ref center, ref tri.vC, out center);
                    Vector3.Multiply(ref center, 1f / 3f, out center);
                    Vector3.Subtract(ref tri.vA, ref center, out tri.vA);
                    Vector3.Subtract(ref tri.vB, ref center, out tri.vB);
                    Vector3.Subtract(ref tri.vC, ref center, out tri.vC);
                    tri.MaximumRadius = tri.vA.LengthSquared();
                    float radius = tri.vB.LengthSquared();
                    if (tri.MaximumRadius < radius)
                    {
                        tri.MaximumRadius = radius;
                    }
                    radius = tri.vC.LengthSquared();
                    if (tri.MaximumRadius < radius)
                    {
                        tri.MaximumRadius = radius;
                    }
                    tri.MaximumRadius   = (float)Math.Sqrt(tri.MaximumRadius);
                    tri.collisionMargin = 0;
                    var triangleTransform = new RigidTransform {
                        Orientation = Quaternion.Identity, Position = center
                    };
                    RayHit tempHit;
                    if (MPRToolbox.Sweep(castShape, tri, ref sweep, ref Toolbox.ZeroVector, ref startingTransform, ref triangleTransform, out tempHit) && tempHit.T < hit.T)
                    {
                        hit = tempHit;
                    }
                }
                tri.MaximumRadius = 0;
                PhysicsThreadResources.GiveBack(tri);
                CommonResources.GiveBack(hitElements);
                return(hit.T != float.MaxValue);
            }
            PhysicsThreadResources.GiveBack(tri);
            CommonResources.GiveBack(hitElements);
            return(false);
        }
        public override void UpdateCollision(float dt)
        {
            WasContaining = Containing;
            WasTouching   = Touching;


            RigidTransform transform = new RigidTransform {
                Orientation = Quaternion.Identity
            };

            DetectorVolume.TriangleMesh.Tree.GetOverlaps(convex.boundingBox, overlaps);
            for (int i = 0; i < overlaps.Count; i++)
            {
                DetectorVolume.TriangleMesh.Data.GetTriangle(overlaps.Elements[i], out triangle.vA, out triangle.vB,
                                                             out triangle.vC);
                Vector3.Add(ref triangle.vA, ref triangle.vB, out transform.Position);
                Vector3.Add(ref triangle.vC, ref transform.Position, out transform.Position);
                Vector3.Multiply(ref transform.Position, 1 / 3f, out transform.Position);
                Vector3.Subtract(ref triangle.vA, ref transform.Position, out triangle.vA);
                Vector3.Subtract(ref triangle.vB, ref transform.Position, out triangle.vB);
                Vector3.Subtract(ref triangle.vC, ref transform.Position, out triangle.vC);

                //If this triangle collides with the convex, we can stop immediately since we know we're touching and not containing.)))
                //[MPR is used here in lieu of GJK because the MPR implementation tends to finish quicker when objects are overlapping than GJK.  The GJK implementation does better on separated objects.]
                if (MPRToolbox.AreShapesOverlapping(convex.Shape, triangle, ref convex.worldTransform, ref transform))
                {
                    Touching = true;
                    //The convex can't be fully contained if it's still touching the surface.
                    Containing = false;

                    overlaps.Clear();
                    goto events;
                }
            }

            overlaps.Clear();
            //If we get here, then there was no shell intersection.
            //If the convex's center point is contained by the mesh, then the convex is fully contained.
            //If this is a child pair, the CheckContainment flag may be set to false.  This is because the parent has
            //already determined that it is not contained (another child performed the check and found that it was not contained)
            //and that it is already touching somehow (either by intersection or by containment).
            //so further containment tests are unnecessary.
            if (CheckContainment && DetectorVolume.IsPointContained(ref convex.worldTransform.Position, overlaps))
            {
                Touching   = true;
                Containing = true;
                goto events;
            }

            //If we get here, then there was no surface intersection and the convex's center is not contained- the volume and convex are separate!
            Touching   = false;
            Containing = false;

events:
            NotifyDetectorVolumeOfChanges();
        }
Exemple #4
0
        /// <summary>
        /// Casts a convex shape against the collidable.
        /// </summary>
        /// <param name="castShape">Shape to cast.</param>
        /// <param name="startingTransform">Initial transform of the shape.</param>
        /// <param name="sweep">Sweep to apply to the shape.</param>
        /// <param name="hit">Hit data, if any.</param>
        /// <returns>Whether or not the cast hit anything.</returns>
        public override bool ConvexCast(CollisionShapes.ConvexShapes.ConvexShape castShape, ref RigidTransform startingTransform, ref Vector3f sweep, out RayHit hit)
        {
            hit = new RayHit();
            BoundingBox localSpaceBoundingBox;

            castShape.GetSweptLocalBoundingBox(ref startingTransform, ref worldTransform, ref sweep, out localSpaceBoundingBox);
            var tri         = PhysicsThreadResources.GetTriangle();
            var hitElements = new QuickList <int>(BufferPools <int> .Thread);

            if (Shape.GetOverlaps(localSpaceBoundingBox, ref hitElements))
            {
                hit.T = float.MaxValue;
                for (int i = 0; i < hitElements.Count; i++)
                {
                    Shape.GetTriangle(hitElements.Elements[i], ref worldTransform, out tri.vA, out tri.vB, out tri.vC);
                    Vector3f center;
                    Vector3f.Add(ref tri.vA, ref tri.vB, out center);
                    Vector3f.Add(ref center, ref tri.vC, out center);
                    Vector3f.Multiply(ref center, 1f / 3f, out center);
                    Vector3f.Subtract(ref tri.vA, ref center, out tri.vA);
                    Vector3f.Subtract(ref tri.vB, ref center, out tri.vB);
                    Vector3f.Subtract(ref tri.vC, ref center, out tri.vC);
                    tri.MaximumRadius = tri.vA.LengthSquared;
                    float radius = tri.vB.LengthSquared;
                    if (tri.MaximumRadius < radius)
                    {
                        tri.MaximumRadius = radius;
                    }
                    radius = tri.vC.LengthSquared;
                    if (tri.MaximumRadius < radius)
                    {
                        tri.MaximumRadius = radius;
                    }
                    tri.MaximumRadius   = (float)Math.Sqrt(tri.MaximumRadius);
                    tri.collisionMargin = 0;
                    var triangleTransform = new RigidTransform {
                        Orientation = Quaternion.Identity, Position = center
                    };
                    RayHit tempHit;
                    if (MPRToolbox.Sweep(castShape, tri, ref sweep, ref Toolbox.ZeroVector, ref startingTransform, ref triangleTransform, out tempHit) && tempHit.T < hit.T)
                    {
                        hit = tempHit;
                    }
                }
                tri.MaximumRadius = 0;
                PhysicsThreadResources.GiveBack(tri);
                hitElements.Dispose();
                return(hit.T != float.MaxValue);
            }
            PhysicsThreadResources.GiveBack(tri);
            hitElements.Dispose();
            return(false);
        }
        public override void Update(float dt)
        {
            if (Game.KeyboardInput.IsKeyDown(Keys.NumPad6))
            {
                aTransform.Position += Vector3.Right * dt;
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.NumPad4))
            {
                aTransform.Position += Vector3.Left * dt;
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.NumPad1))
            {
                aTransform.Position += Vector3.Up * dt;
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.NumPad0))
            {
                aTransform.Position += Vector3.Down * dt;
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.NumPad8))
            {
                aTransform.Position += Vector3.Forward * dt;
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.NumPad5))
            {
                aTransform.Position += Vector3.Backward * dt;
            }

            Vector3 sweepA = new Vector3(0, 10, 0);
            Vector3 sweepB = new Vector3(0, -10, 0);

            if (Game.KeyboardInput.IsKeyDown(Keys.P))
            {
                Debug.WriteLine("Breka.");
            }

            if (hit = MPRToolbox.Sweep(aShape, bShape, ref sweepA, ref sweepB, ref aTransform, ref bTransform, out hitData))
            //if (hit = OldGJKVerifier.ConvexCast(a.CollisionInformation.Shape, b.CollisionInformation.Shape, ref sweepA, ref sweepB, ref aTransform, ref bTransform, out hitData))
            {
                a.Position = aTransform.Position + sweepA * hitData.T;
                b.Position = bTransform.Position + sweepB * hitData.T;
            }
            else
            {
                a.Position = aTransform.Position;
                b.Position = bTransform.Position;
            }
            base.Update(dt);
        }
Exemple #6
0
        private bool DoDeepContact(out ContactData contact)
        {
            if (previousState == CollisionState.Separated
                ) //If it was shallow before, then its closest points will be used to find the normal.
            {
                //It's overlapping! Find the relative velocity at the point relative to the two objects.  The point is still in local space!

                //The above takes into account angular velocity, but linear velocity alone is a lot more stable and does the job just fine.
                if (collidableA.entity != null && collidableB.entity != null)
                {
                    Vector3.Subtract(ref collidableA.entity.linearVelocity, ref collidableB.entity.linearVelocity,
                                     out localDirection);
                }
                else
                {
                    localDirection = localSeparatingAxis;
                }

                if (localDirection.LengthSquared() < Toolbox.Epsilon)
                {
                    localDirection = Vector3.Up;
                }
            }

            if (MPRToolbox.GetContact(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform,
                                      ref collidableB.worldTransform, ref localDirection, out contact))
            {
                if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin)
                {
                    state = CollisionState.ShallowContact;
                }

                return(true);
            }

            //This is rare, but could happen.
            state = CollisionState.Separated;
            return(false);
        }
Exemple #7
0
 /// <summary>
 /// Gets the intersection between the convex shape and the ray.
 /// </summary>
 /// <param name="ray">Ray to test.</param>
 /// <param name="transform">Transform of the convex shape.</param>
 /// <param name="maximumLength">Maximum distance to travel in units of the ray direction's length.</param>
 /// <param name="hit">Ray hit data, if any.</param>
 /// <returns>Whether or not the ray hit the target.</returns>
 public virtual bool RayTest(ref Ray ray, ref RigidTransform transform, float maximumLength, out RayHit hit)
 {
     return(MPRToolbox.RayCast(ray, maximumLength, this, ref transform, out hit));
 }
Exemple #8
0
        private bool DoDeepContact(TriangleShape triangle, out TinyStructList <ContactData> contactList)
        {
            //Find the origin to triangle center offset.
            Vector3 center;

            Vector3.Add(ref triangle.vA, ref triangle.vB, out center);
            Vector3.Add(ref center, ref triangle.vC, out center);
            Vector3.Multiply(ref center, 1f / 3f, out center);

            ContactData contact;

            contactList = new TinyStructList <ContactData>();

            if (MPRToolbox.AreLocalShapesOverlapping(convex, triangle, ref center, ref Toolbox.RigidIdentity))
            {
                float dot;


                Vector3 triangleNormal, ab, ac;
                Vector3.Subtract(ref triangle.vB, ref triangle.vA, out ab);
                Vector3.Subtract(ref triangle.vC, ref triangle.vA, out ac);
                Vector3.Cross(ref ab, ref ac, out triangleNormal);
                float lengthSquared = triangleNormal.LengthSquared();
                if (lengthSquared < Toolbox.Epsilon * .01f)
                {
                    //Degenerate triangle! That's no good.
                    //Just use the direction pointing from A to B, "B" being the triangle.  That direction is center - origin, or just center.
                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref center,
                                                out contact.PenetrationDepth, out contact.Normal, out contact.Position);
                }
                else
                {
                    //Normalize the normal.
                    Vector3.Divide(ref triangleNormal, (float)Math.Sqrt(lengthSquared), out triangleNormal);


                    //TODO: This tests all three edge axes with a full MPR raycast.  That's not really necessary; the correct edge normal should be discoverable, resulting in a single MPR raycast.

                    //Find the edge directions that will be tested with MPR.
                    Vector3 AO, BO, CO;
                    Vector3 AB, BC, CA;
                    Vector3.Subtract(ref center, ref triangle.vA, out AO);
                    Vector3.Subtract(ref center, ref triangle.vB, out BO);
                    Vector3.Subtract(ref center, ref triangle.vC, out CO);
                    Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB);
                    Vector3.Subtract(ref triangle.vC, ref triangle.vB, out BC);
                    Vector3.Subtract(ref triangle.vA, ref triangle.vC, out CA);


                    //We don't have to worry about degenerate triangles here because we've already handled that possibility above.
                    Vector3 ABnormal, BCnormal, CAnormal;

                    //Project the center onto the edge to find the direction from the center to the edge AB.
                    Vector3.Dot(ref AO, ref AB, out dot);
                    Vector3.Multiply(ref AB, dot / AB.LengthSquared(), out ABnormal);
                    Vector3.Subtract(ref AO, ref ABnormal, out ABnormal);
                    ABnormal.Normalize();

                    //Project the center onto the edge to find the direction from the center to the edge BC.
                    Vector3.Dot(ref BO, ref BC, out dot);
                    Vector3.Multiply(ref BC, dot / BC.LengthSquared(), out BCnormal);
                    Vector3.Subtract(ref BO, ref BCnormal, out BCnormal);
                    BCnormal.Normalize();

                    //Project the center onto the edge to find the direction from the center to the edge BC.
                    Vector3.Dot(ref CO, ref CA, out dot);
                    Vector3.Multiply(ref CA, dot / CA.LengthSquared(), out CAnormal);
                    Vector3.Subtract(ref CO, ref CAnormal, out CAnormal);
                    CAnormal.Normalize();


                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref ABnormal,
                                                out contact.PenetrationDepth, out contact.Normal);
                    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot);
                    if (triangle.sidedness == TriangleSidedness.Clockwise && dot > 0 ||
                        triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)
                    {
                        //Normal was facing the wrong way.
                        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                        Vector3 previousNormal = contact.Normal;
                        Vector3.Dot(ref contact.Normal, ref triangleNormal, out dot);

                        Vector3 p;
                        Vector3.Multiply(ref contact.Normal, dot, out p);
                        Vector3.Subtract(ref contact.Normal, ref p, out contact.Normal);
                        float length = contact.Normal.LengthSquared();
                        if (length > Toolbox.Epsilon)
                        {
                            //Renormalize the corrected normal.
                            Vector3.Divide(ref contact.Normal, (float)Math.Sqrt(length), out contact.Normal);
                            Vector3.Dot(ref contact.Normal, ref previousNormal, out dot);
                            contact.PenetrationDepth *= dot;
                        }
                        else
                        {
                            contact.PenetrationDepth = float.MaxValue;
                            contact.Normal           = new Vector3();
                        }
                    }


                    Vector3 candidateNormal;
                    float   candidateDepth;

                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref BCnormal,
                                                out candidateDepth, out candidateNormal);
                    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    Vector3.Dot(ref triangleNormal, ref candidateNormal, out dot);
                    if (triangle.sidedness == TriangleSidedness.Clockwise && dot > 0 ||
                        triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)
                    {
                        //Normal was facing the wrong way.
                        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                        Vector3 previousNormal = candidateNormal;
                        Vector3.Dot(ref candidateNormal, ref triangleNormal, out dot);

                        Vector3 p;
                        Vector3.Multiply(ref candidateNormal, dot, out p);
                        Vector3.Subtract(ref candidateNormal, ref p, out candidateNormal);
                        float length = candidateNormal.LengthSquared();
                        if (length > Toolbox.Epsilon)
                        {
                            //Renormalize the corrected normal.
                            Vector3.Divide(ref candidateNormal, (float)Math.Sqrt(length), out candidateNormal);
                            Vector3.Dot(ref candidateNormal, ref previousNormal, out dot);
                            candidateDepth *= dot;
                        }
                        else
                        {
                            contact.PenetrationDepth = float.MaxValue;
                            contact.Normal           = new Vector3();
                        }
                    }

                    if (candidateDepth < contact.PenetrationDepth)
                    {
                        contact.Normal           = candidateNormal;
                        contact.PenetrationDepth = candidateDepth;
                    }


                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref CAnormal,
                                                out candidateDepth, out candidateNormal);
                    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    Vector3.Dot(ref triangleNormal, ref candidateNormal, out dot);
                    if (triangle.sidedness == TriangleSidedness.Clockwise && dot > 0 ||
                        triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0)
                    {
                        //Normal was facing the wrong way.
                        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                        Vector3 previousNormal = candidateNormal;
                        Vector3.Dot(ref candidateNormal, ref triangleNormal, out dot);

                        Vector3 p;
                        Vector3.Multiply(ref candidateNormal, dot, out p);
                        Vector3.Subtract(ref candidateNormal, ref p, out candidateNormal);
                        float length = candidateNormal.LengthSquared();
                        if (length > Toolbox.Epsilon)
                        {
                            //Renormalize the corrected normal.
                            Vector3.Divide(ref candidateNormal, (float)Math.Sqrt(length), out candidateNormal);
                            Vector3.Dot(ref candidateNormal, ref previousNormal, out dot);
                            candidateDepth *= dot;
                        }
                        else
                        {
                            contact.PenetrationDepth = float.MaxValue;
                            contact.Normal           = new Vector3();
                        }
                    }

                    if (candidateDepth < contact.PenetrationDepth)
                    {
                        contact.Normal           = candidateNormal;
                        contact.PenetrationDepth = candidateDepth;
                    }


                    //Try the depth along the positive triangle normal.

                    //If it's clockwise, this direction is unnecessary (the resulting normal would be invalidated by the onesidedness of the triangle).
                    if (triangle.sidedness != TriangleSidedness.Clockwise)
                    {
                        MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref triangleNormal,
                                                    out candidateDepth, out candidateNormal);
                        if (candidateDepth < contact.PenetrationDepth)
                        {
                            contact.Normal           = candidateNormal;
                            contact.PenetrationDepth = candidateDepth;
                        }
                    }

                    //Try the depth along the negative triangle normal.

                    //If it's counterclockwise, this direction is unnecessary (the resulting normal would be invalidated by the onesidedness of the triangle).
                    if (triangle.sidedness != TriangleSidedness.Counterclockwise)
                    {
                        Vector3.Negate(ref triangleNormal, out triangleNormal);
                        MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref triangleNormal,
                                                    out candidateDepth, out candidateNormal);
                        if (candidateDepth < contact.PenetrationDepth)
                        {
                            contact.Normal           = candidateNormal;
                            contact.PenetrationDepth = candidateDepth;
                        }
                    }
                }


                MPRToolbox.RefinePenetration(convex, triangle, ref Toolbox.RigidIdentity, contact.PenetrationDepth,
                                             ref contact.Normal, out contact.PenetrationDepth, out contact.Normal, out contact.Position);

                //It's possible for the normal to still face the 'wrong' direction according to one sided triangles.
                if (triangle.sidedness != TriangleSidedness.DoubleSided)
                {
                    Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot);
                    if (dot < 0)
                    //Skip the add process.
                    {
                        goto InnerSphere;
                    }
                }


                contact.Id = -1;

                if (contact.PenetrationDepth < convex.collisionMargin + triangle.collisionMargin)
                {
                    state = CollisionState
                            .ExternalNear; //If it's emerged from the deep contact, we can go back to using the preferred GJK method.
                }

                contactList.Add(ref contact);
            }

InnerSphere:

            if (TryInnerSphereContact(triangle, out contact))
            {
                contactList.Add(ref contact);
            }

            if (contactList.Count > 0)
            {
                return(true);
            }

            state = CollisionState.ExternalSeparated;
            return(false);
        }
Exemple #9
0
        /// <summary>
        /// Casts a convex shape against the collidable.
        /// </summary>
        /// <param name="castShape">Shape to cast.</param>
        /// <param name="startingTransform">Initial transform of the shape.</param>
        /// <param name="sweep">Sweep to apply to the shape.</param>
        /// <param name="hit">Hit data, if any.</param>
        /// <returns>Whether or not the cast hit anything.</returns>
        public override bool ConvexCast(ConvexShape castShape, ref RigidTransform startingTransform, ref Vector3 sweep, out RayHit hit)
        {
            if (Shape.solidity == MobileMeshSolidity.Solid)
            {
                //If the convex cast is inside the mesh and the mesh is solid, it should return t = 0.
                var ray = new Ray()
                {
                    Position = startingTransform.Position, Direction = Toolbox.UpVector
                };
                if (Shape.IsLocalRayOriginInMesh(ref ray, out hit))
                {
                    hit = new RayHit()
                    {
                        Location = startingTransform.Position, Normal = new Vector3(), T = 0
                    };
                    return(true);
                }
            }
            hit = new RayHit();
            BoundingBox boundingBox;
            var         transform = new AffineTransform {
                Translation = worldTransform.Position
            };

            Matrix3x3.CreateFromQuaternion(ref worldTransform.Orientation, out transform.LinearTransform);
            castShape.GetSweptLocalBoundingBox(ref startingTransform, ref transform, ref sweep, out boundingBox);
            var tri         = PhysicsThreadResources.GetTriangle();
            var hitElements = CommonResources.GetIntList();

            if (this.Shape.TriangleMesh.Tree.GetOverlaps(boundingBox, hitElements))
            {
                hit.T = float.MaxValue;
                for (int i = 0; i < hitElements.Count; i++)
                {
                    Shape.TriangleMesh.Data.GetTriangle(hitElements[i], out tri.vA, out tri.vB, out tri.vC);
                    AffineTransform.Transform(ref tri.vA, ref transform, out tri.vA);
                    AffineTransform.Transform(ref tri.vB, ref transform, out tri.vB);
                    AffineTransform.Transform(ref tri.vC, ref transform, out tri.vC);
                    Vector3 center;
                    Vector3.Add(ref tri.vA, ref tri.vB, out center);
                    Vector3.Add(ref center, ref tri.vC, out center);
                    Vector3.Multiply(ref center, 1f / 3f, out center);
                    Vector3.Subtract(ref tri.vA, ref center, out tri.vA);
                    Vector3.Subtract(ref tri.vB, ref center, out tri.vB);
                    Vector3.Subtract(ref tri.vC, ref center, out tri.vC);
                    tri.MaximumRadius = tri.vA.LengthSquared();
                    float radius = tri.vB.LengthSquared();
                    if (tri.MaximumRadius < radius)
                    {
                        tri.MaximumRadius = radius;
                    }
                    radius = tri.vC.LengthSquared();
                    if (tri.MaximumRadius < radius)
                    {
                        tri.MaximumRadius = radius;
                    }
                    tri.MaximumRadius   = (float)Math.Sqrt(tri.MaximumRadius);
                    tri.collisionMargin = 0;
                    var triangleTransform = new RigidTransform {
                        Orientation = Quaternion.Identity, Position = center
                    };
                    RayHit tempHit;
                    if (MPRToolbox.Sweep(castShape, tri, ref sweep, ref Toolbox.ZeroVector, ref startingTransform, ref triangleTransform, out tempHit) && tempHit.T < hit.T)
                    {
                        hit = tempHit;
                    }
                }
                tri.MaximumRadius = 0;
                PhysicsThreadResources.GiveBack(tri);
                CommonResources.GiveBack(hitElements);
                return(hit.T != float.MaxValue);
            }
            PhysicsThreadResources.GiveBack(tri);
            CommonResources.GiveBack(hitElements);
            return(false);
        }
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public MPRTestDemo(DemosGame game)
            : base(game)
        {
            var shapeA = new BoxShape(1, 1, 1);

            shapeA.CollisionMargin = 0;
            var shapeB = new BoxShape(1, 1, 1);

            shapeB.CollisionMargin = 0;

            var     transformA = new RigidTransform(new Vector3(0, 0, 0));
            var     transformB = new RigidTransform(new Vector3(.5m, .5m, 0));
            Vector3 overlap;
            bool    overlapped = MPRToolbox.GetLocalOverlapPosition(shapeA, shapeB, ref transformB, out overlap);
            Vector3 normal;
            Fix64   depth;
            Vector3 direction = new Vector3(0, -1, 0);

            MPRToolbox.LocalSurfaceCast(shapeA, shapeB, ref transformB, ref direction, out depth, out normal);

            ContactData contactData;

            //bool overlappedOld = MPRToolboxOld.AreObjectsColliding(shapeA, shapeB, ref transformA, ref transformB, out contactData);

            //Random rand = new Random(0);
            //for (int i = 0; i < 10000000; i++)
            //{
            //    transformA = new RigidTransform(new Vector3((Fix64)rand.NextDouble() * 10 - 5, (Fix64)rand.NextDouble() * 10 - 5, (Fix64)rand.NextDouble() * 10 - 5),
            //        Quaternion.CreateFromYawPitchRoll((Fix64)rand.NextDouble() * 1000, (Fix64)rand.NextDouble() * 1000, (Fix64)rand.NextDouble() * 1000));
            //    transformB = new RigidTransform(new Vector3((Fix64)rand.NextDouble() * 10 - 5, (Fix64)rand.NextDouble() * 10 - 5, (Fix64)rand.NextDouble() * 10 - 5),
            //        Quaternion.CreateFromYawPitchRoll((Fix64)rand.NextDouble() * 1000, (Fix64)rand.NextDouble() * 1000, (Fix64)rand.NextDouble() * 1000));

            //    overlapped = MPRTesting.GetOverlapPosition(shapeA, shapeB, ref transformA, ref transformB, out overlap);

            //    overlappedOld = MPRToolbox.AreObjectsColliding(shapeA, shapeB, ref transformA, ref transformB, out contactData);

            //    if (overlapped && !overlappedOld &&
            //        (!MPRToolbox.IsPointInsideShape(ref overlap, shapeA, ref transformA) ||
            //        !MPRToolbox.IsPointInsideShape(ref overlap, shapeB, ref transformB)))
            //        Debug.WriteLine("Break.");
            //    if (overlappedOld && !overlapped &&
            //        (!MPRToolbox.IsPointInsideShape(ref contactData.Position, shapeA, ref transformA) ||
            //        !MPRToolbox.IsPointInsideShape(ref contactData.Position, shapeB, ref transformB)))
            //        Debug.WriteLine("Break.");
            //    if (overlapped && overlappedOld &&
            //        (!MPRToolbox.IsPointInsideShape(ref overlap, shapeA, ref transformA) ||
            //        !MPRToolbox.IsPointInsideShape(ref overlap, shapeB, ref transformB) ||
            //        !MPRToolbox.IsPointInsideShape(ref contactData.Position, shapeA, ref transformA) ||
            //        !MPRToolbox.IsPointInsideShape(ref contactData.Position, shapeB, ref transformB)))
            //        Debug.WriteLine("Break.");
            //}

            //Do these tests with rotationally immobile objects.
            CollisionDetectionSettings.DefaultMargin = 0;
            groundWidth  = 10;
            groundHeight = .1m;
            groundLength = 10;
            //a = new Box(new Vector3(0, -5, 0), groundWidth, groundHeight, groundLength, 1);
            //a = new TransformableEntity(new Vector3(0,0,0), new TriangleShape(new Vector3(-5, -5, -5), new Vector3(5, -5, -5), new Vector3(-5, -5, 5)), Matrix3x3.Identity);
            a = new Triangle(new Vector3(0, -5, 0), new Vector3(5, -5, 0), new Vector3(5, -5, 5), 1);
            Space.Add(a);

            Space.ForceUpdater.Gravity = new Vector3();
            boxWidth  = .25m;
            boxHeight = .05m;
            boxLength = 1;
            b         = new TransformableEntity(new Vector3(0, 2, 0), new BoxShape(boxWidth, boxHeight, boxLength), Matrix3x3.Identity, 1);
            //b = new Cone(new Vector3(0, 2, 0), .2m, .1m, 1);
            //b = new Capsule(new Vector3(0, 2, 0), 1, .5m, 1);
            //b = new Capsule(new Vector3(0, 2, 0), 1, .5m, 1);
            b.LocalInertiaTensorInverse = new Matrix3x3();
            CollisionRules.AddRule(b, a, CollisionRule.NoSolver);
            b.ActivityInformation.IsAlwaysActive = true;
            Space.Add(b);
            //Space.Add(new TransformableEntity(new Vector3(0, 4, 0), new BoxShape(1, 1, 1), Matrix3x3.Identity, 1));
            //Space.Add( new TransformableEntity(new Vector3(0, 6, 0), new BoxShape(1, 1, 1), Matrix3x3.Identity, 1));

            //Vector3[] vertices = new Vector3[] { new Vector3(0, -5, 0), new Vector3(5, -5, 0), new Vector3(5, -5, 5), new Vector3(0, -60, 5) };
            //int[] indices = new int[] { 0, 1, 2 , 0, 2, 3 };
            //StaticMesh mesh = new StaticMesh(vertices, indices);
            //Space.Add(mesh);
            //mesh.ImproveBoundaryBehavior = true;
            //mesh.Sidedness = TriangleSidedness.Counterclockwise;
            //game.ModelDrawer.Add(mesh);
            //mesh.CollisionRules.Personal = CollisionRule.NoSolver;
        }
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public BooleanConvexTestDemo(DemosGame game)
            : base(game)
        {
            var random = new Random();

            int numberOfConfigurations        = 1000;
            int numberOfTestsPerConfiguration = 10000;


            float size            = 2;
            var   aPositionBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size));
            var   bPositionBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size));

            size = 1;
            var aShapeBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size));
            var bShapeBounds = new BoundingBox(new Vector3(-size, -size, -size), new Vector3(size, size, size));
            int pointsInA    = 10;
            int pointsInB    = 10;



            RawList <Vector3> points = new RawList <Vector3>();

            long accumulatedMPR = 0;
            long accumulatedGJK = 0;
            long accumulatedGJKSeparatingAxis = 0;


            for (int i = 0; i < numberOfConfigurations; i++)
            {
                //Create two convex hull shapes.
                for (int j = 0; j < pointsInA; j++)
                {
                    Vector3 point;
                    GetRandomPointInBoundingBox(random, ref aShapeBounds, out point);
                    points.Add(point);
                }
                var a = new ConvexHullShape(points);
                points.Clear();
                for (int j = 0; j < pointsInB; j++)
                {
                    Vector3 point;
                    GetRandomPointInBoundingBox(random, ref bShapeBounds, out point);
                    points.Add(point);
                }
                var b = new ConvexHullShape(points);
                points.Clear();

                //Generate some random tranforms for the shapes.
                RigidTransform aTransform;
                var            axis  = Vector3.Normalize(new Vector3((float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2)));
                var            angle = (float)random.NextDouble() * MathHelper.TwoPi;
                Quaternion.CreateFromAxisAngle(ref axis, angle, out aTransform.Orientation);
                GetRandomPointInBoundingBox(random, ref aPositionBounds, out aTransform.Position);

                RigidTransform bTransform;
                axis  = Vector3.Normalize(new Vector3((float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2), (float)((random.NextDouble() - .5f) * 2)));
                angle = (float)random.NextDouble() * MathHelper.TwoPi;
                Quaternion.CreateFromAxisAngle(ref axis, angle, out bTransform.Orientation);
                GetRandomPointInBoundingBox(random, ref bPositionBounds, out bTransform.Position);

                //Perform MPR tests.
                //Warm up the cache a bit.
                MPRToolbox.AreShapesOverlapping(a, b, ref aTransform, ref bTransform);
                long start = Stopwatch.GetTimestamp();
                for (int j = 0; j < numberOfTestsPerConfiguration; j++)
                {
                    if (MPRToolbox.AreShapesOverlapping(a, b, ref aTransform, ref bTransform))
                    {
                        overlapsMPR++;
                    }
                }
                long end = Stopwatch.GetTimestamp();
                accumulatedMPR += end - start;

                //Perform GJK tests.
                //Warm up the cache a bit.
                GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform);
                start = Stopwatch.GetTimestamp();
                for (int j = 0; j < numberOfTestsPerConfiguration; j++)
                {
                    if (GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform))
                    {
                        overlapsGJK++;
                    }
                }
                end             = Stopwatch.GetTimestamp();
                accumulatedGJK += end - start;

                //Perform GJK Separating Axis tests.
                //Warm up the cache a bit.
                Vector3 localSeparatingAxis = Vector3.Up;
                GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform, ref localSeparatingAxis);
                start = Stopwatch.GetTimestamp();
                for (int j = 0; j < numberOfTestsPerConfiguration; j++)
                {
                    if (GJKToolbox.AreShapesIntersecting(a, b, ref aTransform, ref bTransform, ref localSeparatingAxis))
                    {
                        overlapsGJKSeparatingAxis++;
                    }
                }
                end = Stopwatch.GetTimestamp();
                accumulatedGJKSeparatingAxis += end - start;
            }

            //Compute the actual time per test.
            long denominator = Stopwatch.Frequency * numberOfConfigurations * numberOfTestsPerConfiguration;

            timeMPR = (double)accumulatedMPR / denominator;
            timeGJK = (double)accumulatedGJK / denominator;
            timeGJKSeparatingAxis = (double)accumulatedGJKSeparatingAxis / denominator;
        }
Exemple #12
0
        public static void Test()
        {
            var f0 = BuildHull();

            f0.CollisionMargin = 0;


            //Generate spheres all around the central froxel in such a way that we know that they're not colliding.
            var froxelSphereSurface = new BoundingBox(new Vector3(-1.51f, -1.51f, -1.51f), new Vector3(1.51f, 1.51f, 1.51f));

            int         testIterations             = 1000;
            int         innerIterations            = 1000;
            Random      random                     = new Random(5);
            long        sphereFroxelSeparatedTicks = 0;
            SphereShape sphere                     = new SphereShape(1);

            for (int i = 0; i < testIterations; ++i)
            {
                var   ray = GetRandomRay(ref froxelSphereSurface, random);
                float t;
                ray.Intersects(ref froxelSphereSurface, out t);
                var sphereTransform = new RigidTransform {
                    Position = ray.Position + ray.Direction * t, Orientation = Quaternion.Identity
                };

                var start = Stopwatch.GetTimestamp();
                for (int j = 0; j < innerIterations; ++j)
                {
                    if (MPRToolbox.AreLocalShapesOverlapping(f0, sphere, ref sphereTransform))
                    {
                        Trace.Fail("By construction there can be no intersection!");
                    }
                }
                var end = Stopwatch.GetTimestamp();
                sphereFroxelSeparatedTicks += (end - start);
            }
            Console.WriteLine($"Sphere-froxel separated: {(1e6 * sphereFroxelSeparatedTicks) / (testIterations * innerIterations * Stopwatch.Frequency)}");

            //Do the same kind of test, but now with intersection.
            froxelSphereSurface = new BoundingBox(new Vector3(-0.5f, -0.5f, -0.5f), new Vector3(0.5f, 0.5f, 0.5f));

            long sphereFroxelIntersectingTicks = 0;

            for (int i = 0; i < testIterations; ++i)
            {
                var   ray = GetRandomRay(ref froxelSphereSurface, random);
                float t;
                ray.Intersects(ref froxelSphereSurface, out t);
                var sphereTransform = new RigidTransform {
                    Position = ray.Position + ray.Direction * (t - 0.99f), Orientation = Quaternion.Identity
                };

                var start = Stopwatch.GetTimestamp();
                for (int j = 0; j < innerIterations; ++j)
                {
                    if (!MPRToolbox.AreLocalShapesOverlapping(f0, sphere, ref sphereTransform))
                    {
                        Trace.Fail("By construction there can be no separation!");
                    }
                }
                var end = Stopwatch.GetTimestamp();
                sphereFroxelIntersectingTicks += (end - start);
            }
            Console.WriteLine($"Sphere-froxel intersecting: {(1e6 * sphereFroxelIntersectingTicks) / (testIterations * innerIterations * Stopwatch.Frequency)}");

            //Create a surface for the rays to hit such that every query froxel will be just outside of the central froxel.
            var froxelFroxelSurface = new BoundingBox(new Vector3(-1.01f, -1.01f, -1.01f), new Vector3(1.01f, 1.01f, 1.01f));

            var queryHull = BuildHull();

            queryHull.CollisionMargin = 0;
            long froxelFroxelSeparatedTicks = 0;

            for (int i = 0; i < testIterations; ++i)
            {
                var   ray = GetRandomRay(ref froxelFroxelSurface, random);
                float t;
                ray.Intersects(ref froxelFroxelSurface, out t);

                var queryTransform = new RigidTransform(ray.Position + ray.Direction * t);

                var start = Stopwatch.GetTimestamp();
                for (int j = 0; j < innerIterations; ++j)
                {
                    if (MPRToolbox.AreLocalShapesOverlapping(f0, queryHull, ref queryTransform))
                    {
                        Trace.Fail("By construction there can be no intersection!");
                    }
                }
                var end = Stopwatch.GetTimestamp();
                froxelFroxelSeparatedTicks += (end - start);
            }
            Console.WriteLine($"Froxel-froxel separated: {(1e6 * froxelFroxelSeparatedTicks) / (testIterations * innerIterations * Stopwatch.Frequency)}");

            //Same thing as above, but now with slight intersection.
            froxelFroxelSurface = new BoundingBox(new Vector3(-.99f, -.99f, -.99f), new Vector3(0.99f, 0.99f, 0.99f));

            long froxelFroxelIntersectingTicks = 0;

            for (int i = 0; i < testIterations; ++i)
            {
                var   ray = GetRandomRay(ref froxelFroxelSurface, random);
                float t;
                ray.Intersects(ref froxelFroxelSurface, out t);

                var queryTransform = new RigidTransform(ray.Position + ray.Direction * t);

                var start = Stopwatch.GetTimestamp();
                for (int j = 0; j < innerIterations; ++j)
                {
                    if (!MPRToolbox.AreLocalShapesOverlapping(f0, queryHull, ref queryTransform))
                    {
                        Trace.Fail("By construction there can be no separation!");
                    }
                }
                var end = Stopwatch.GetTimestamp();
                froxelFroxelIntersectingTicks += (end - start);
            }
            Console.WriteLine($"Froxel-froxel intersecting: {(1e6 * froxelFroxelIntersectingTicks) / (testIterations * innerIterations * Stopwatch.Frequency)}");
        }
Exemple #13
0
        private bool TryInnerSphereContact(out ContactData contact)
        {
            Vector3 closestPoint;

            Toolbox.GetClosestPointOnTriangleToPoint(ref triangle.vA, ref triangle.vB, ref triangle.vC, ref Toolbox.ZeroVector, out closestPoint);
            float length        = closestPoint.LengthSquared();
            float minimumRadius = convex.minimumRadius * (MotionSettings.CoreShapeScaling + .01f);

            if (length < minimumRadius * minimumRadius)
            {
                Vector3 triangleNormal, ab, ac;
                Vector3.Subtract(ref triangle.vB, ref triangle.vA, out ab);
                Vector3.Subtract(ref triangle.vC, ref triangle.vA, out ac);
                Vector3.Cross(ref ab, ref ac, out triangleNormal);
                float dot;
                Vector3.Dot(ref closestPoint, ref triangleNormal, out dot);
                if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0))
                {
                    //Normal was facing the wrong way.
                    contact = new ContactData();
                    return(false);
                }

                length           = (float)Math.Sqrt(length);
                contact.Position = closestPoint;

                if (length > Toolbox.Epsilon) //Watch out for NaN's!
                {
                    Vector3.Divide(ref closestPoint, length, out contact.Normal);
                }
                else
                {
                    //The direction is undefined.  Use the triangle's normal.
                    //One sided triangles can only face in the appropriate direction.
                    float normalLength = triangleNormal.LengthSquared();
                    if (triangleNormal.LengthSquared() > Toolbox.Epsilon)
                    {
                        Vector3.Divide(ref triangleNormal, (float)Math.Sqrt(normalLength), out triangleNormal);
                        if (triangle.sidedness == TriangleSidedness.Clockwise)
                        {
                            contact.Normal = triangleNormal;
                        }
                        else
                        {
                            Vector3.Negate(ref triangleNormal, out contact.Normal);
                        }
                    }
                    else
                    {
                        //Degenerate triangle!
                        contact = new ContactData();
                        return(false);
                    }
                }

                //Compute the actual depth of the contact.
                MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref contact.Normal, out contact.PenetrationDepth, out triangleNormal); //Trash the 'corrected' normal.  We want to use the spherical normal.
                contact.Id = -1;
                return(true);
            }
            contact = new ContactData();
            return(false);
        }
Exemple #14
0
 public override bool ConvexCast(ConvexShape castShape, ref MathExtensions.RigidTransform startingTransform, ref Vector3 sweep, out RayHit hit)
 {
     return(MPRToolbox.Sweep(castShape, Shape, ref sweep, ref Toolbox.ZeroVector, ref startingTransform, ref worldTransform, out hit));
 }
Exemple #15
0
        private bool DoDeepContact(out TinyStructList <ContactData> contactList)
        {
            //Find the origin to triangle center offset.
            Vector3 center;

            Vector3.Add(ref triangle.vA, ref triangle.vB, out center);
            Vector3.Add(ref center, ref triangle.vC, out center);
            Vector3.Multiply(ref center, 1f / 3f, out center);

            ContactData contact;

            contactList = new TinyStructList <ContactData>();

            if (MPRToolbox.AreLocalShapesOverlapping(convex, triangle, ref center, ref Toolbox.RigidIdentity))
            {
                float dot;


                Vector3 triangleNormal, ab, ac;
                Vector3.Subtract(ref triangle.vB, ref triangle.vA, out ab);
                Vector3.Subtract(ref triangle.vC, ref triangle.vA, out ac);
                Vector3.Cross(ref ab, ref ac, out triangleNormal);
                float lengthSquared = triangleNormal.LengthSquared();
                if (lengthSquared < Toolbox.Epsilon * .01f)
                {
                    //Degenerate triangle! That's no good.
                    //Just use the direction pointing from A to B, "B" being the triangle.  That direction is center - origin, or just center.
                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref center, out contact.PenetrationDepth, out contact.Normal, out contact.Position);
                }
                else
                {
                    //Normalize the normal.
                    Vector3.Divide(ref triangleNormal, (float)Math.Sqrt(lengthSquared), out triangleNormal);


                    ////The first direction to check is one of the triangle's edge normals.  Choose the one that is most aligned with the offset from A to B.
                    ////Project the direction onto the triangle plane.
                    //Vector3.Dot(ref triangleNormal, ref center, out dot);
                    //Vector3 trianglePlaneDirection;
                    //Vector3.Multiply(ref triangleNormal, dot, out trianglePlaneDirection);
                    //Vector3.Subtract(ref trianglePlaneDirection, ref center, out trianglePlaneDirection);

                    ////To find out which edge to use, compute which region the direction is in.
                    ////This is done by constructing three planes which segment the triangle into three sub-triangles.

                    ////These planes are defined by A, origin, center; B, origin, center; C, origin, center.
                    ////The plane tests against the direction can be reordered to:
                    ////(center x direction) * A
                    ////(center x direction) * B
                    ////(center x direction) * C
                    //Vector3 OxD;
                    //Vector3.Cross(ref trianglePlaneDirection, ref center, out OxD);
                    //Vector3 p;

                    //float dotA, dotB, dotC;
                    //Vector3.Dot(ref triangle.vA, ref OxD, out dotA);
                    //Vector3.Dot(ref triangle.vB, ref OxD, out dotB);
                    //Vector3.Dot(ref triangle.vC, ref OxD, out dotC);

                    //if (dotA >= 0 && dotB <= 0)
                    //{
                    //    //Direction is in the AB edge zone.
                    //    //Compute the edge normal using AB x (AO x AB).
                    //    Vector3 AB, AO;
                    //    Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB);
                    //    Vector3.Subtract(ref center, ref triangle.vA, out AO);
                    //    Vector3.Cross(ref AO, ref AB, out p);
                    //    Vector3.Cross(ref AB, ref p, out trianglePlaneDirection);
                    //}
                    //else if (dotB >= 0 && dotC <= 0)
                    //{
                    //    //Direction is in the BC edge zone.
                    //    //Compute the edge normal using BC x (BO x BC).
                    //    Vector3 BC, BO;
                    //    Vector3.Subtract(ref triangle.vC, ref triangle.vB, out BC);
                    //    Vector3.Subtract(ref center, ref triangle.vB, out BO);
                    //    Vector3.Cross(ref BO, ref BC, out p);
                    //    Vector3.Cross(ref BC, ref p, out trianglePlaneDirection);

                    //}
                    //else // dotC > 0 && dotA < 0
                    //{
                    //    //Direction is in the CA edge zone.
                    //    //Compute the edge normal using CA x (CO x CA).
                    //    Vector3 CA, CO;
                    //    Vector3.Subtract(ref triangle.vA, ref triangle.vC, out CA);
                    //    Vector3.Subtract(ref center, ref triangle.vC, out CO);
                    //    Vector3.Cross(ref CO, ref CA, out p);
                    //    Vector3.Cross(ref CA, ref p, out trianglePlaneDirection);
                    //}



                    //dot = trianglePlaneDirection.LengthSquared();
                    //if (dot > Toolbox.Epsilon)
                    //{
                    //    Vector3.Divide(ref trianglePlaneDirection, (float)Math.Sqrt(dot), out trianglePlaneDirection);
                    //    MPRTesting.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref trianglePlaneDirection, out contact.PenetrationDepth, out contact.Normal);
                    //    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    //    Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot);
                    //    if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0))
                    //    {
                    //        //Normal was facing the wrong way.
                    //        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                    //        Vector3 previousNormal = contact.Normal;
                    //        Vector3.Dot(ref contact.Normal, ref triangleNormal, out dot);

                    //        Vector3.Multiply(ref contact.Normal, dot, out p);
                    //        Vector3.Subtract(ref contact.Normal, ref p, out contact.Normal);
                    //        float length = contact.Normal.LengthSquared();
                    //        if (length > Toolbox.Epsilon)
                    //        {
                    //            //Renormalize the corrected normal.
                    //            Vector3.Divide(ref contact.Normal, (float)Math.Sqrt(length), out contact.Normal);
                    //            Vector3.Dot(ref contact.Normal, ref previousNormal, out dot);
                    //            contact.PenetrationDepth *= dot;
                    //        }
                    //        else
                    //        {
                    //            contact.PenetrationDepth = float.MaxValue;
                    //            contact.Normal = new Vector3();
                    //        }
                    //    }
                    //}
                    //else
                    //{
                    //    contact.PenetrationDepth = float.MaxValue;
                    //    contact.Normal = new Vector3();
                    //}

                    //TODO: This tests all three edge axes with a full MPR raycast.  That's not really necessary; the correct edge normal should be discoverable, resulting in a single MPR raycast.

                    //Find the edge directions that will be tested with MPR.
                    Vector3 AO, BO, CO;
                    Vector3 AB, BC, CA;
                    Vector3.Subtract(ref center, ref triangle.vA, out AO);
                    Vector3.Subtract(ref center, ref triangle.vB, out BO);
                    Vector3.Subtract(ref center, ref triangle.vC, out CO);
                    Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB);
                    Vector3.Subtract(ref triangle.vC, ref triangle.vB, out BC);
                    Vector3.Subtract(ref triangle.vA, ref triangle.vC, out CA);


                    //We don't have to worry about degenerate triangles here because we've already handled that possibility above.
                    Vector3 ABnormal, BCnormal, CAnormal;

                    //Project the center onto the edge to find the direction from the center to the edge AB.
                    Vector3.Dot(ref AO, ref AB, out dot);
                    Vector3.Multiply(ref AB, dot / AB.LengthSquared(), out ABnormal);
                    Vector3.Subtract(ref AO, ref ABnormal, out ABnormal);
                    ABnormal.Normalize();

                    //Project the center onto the edge to find the direction from the center to the edge BC.
                    Vector3.Dot(ref BO, ref BC, out dot);
                    Vector3.Multiply(ref BC, dot / BC.LengthSquared(), out BCnormal);
                    Vector3.Subtract(ref BO, ref BCnormal, out BCnormal);
                    BCnormal.Normalize();

                    //Project the center onto the edge to find the direction from the center to the edge BC.
                    Vector3.Dot(ref CO, ref CA, out dot);
                    Vector3.Multiply(ref CA, dot / CA.LengthSquared(), out CAnormal);
                    Vector3.Subtract(ref CO, ref CAnormal, out CAnormal);
                    CAnormal.Normalize();


                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref ABnormal, out contact.PenetrationDepth, out contact.Normal);
                    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot);
                    if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0))
                    {
                        //Normal was facing the wrong way.
                        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                        Vector3 previousNormal = contact.Normal;
                        Vector3.Dot(ref contact.Normal, ref triangleNormal, out dot);

                        Vector3 p;
                        Vector3.Multiply(ref contact.Normal, dot, out p);
                        Vector3.Subtract(ref contact.Normal, ref p, out contact.Normal);
                        float length = contact.Normal.LengthSquared();
                        if (length > Toolbox.Epsilon)
                        {
                            //Renormalize the corrected normal.
                            Vector3.Divide(ref contact.Normal, (float)Math.Sqrt(length), out contact.Normal);
                            Vector3.Dot(ref contact.Normal, ref previousNormal, out dot);
                            contact.PenetrationDepth *= dot;
                        }
                        else
                        {
                            contact.PenetrationDepth = float.MaxValue;
                            contact.Normal           = new Vector3();
                        }
                    }



                    Vector3 candidateNormal;
                    float   candidateDepth;

                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref BCnormal, out candidateDepth, out candidateNormal);
                    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    Vector3.Dot(ref triangleNormal, ref candidateNormal, out dot);
                    if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0))
                    {
                        //Normal was facing the wrong way.
                        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                        Vector3 previousNormal = candidateNormal;
                        Vector3.Dot(ref candidateNormal, ref triangleNormal, out dot);

                        Vector3 p;
                        Vector3.Multiply(ref candidateNormal, dot, out p);
                        Vector3.Subtract(ref candidateNormal, ref p, out candidateNormal);
                        float length = candidateNormal.LengthSquared();
                        if (length > Toolbox.Epsilon)
                        {
                            //Renormalize the corrected normal.
                            Vector3.Divide(ref candidateNormal, (float)Math.Sqrt(length), out candidateNormal);
                            Vector3.Dot(ref candidateNormal, ref previousNormal, out dot);
                            candidateDepth *= dot;
                        }
                        else
                        {
                            contact.PenetrationDepth = float.MaxValue;
                            contact.Normal           = new Vector3();
                        }
                    }
                    if (candidateDepth < contact.PenetrationDepth)
                    {
                        contact.Normal           = candidateNormal;
                        contact.PenetrationDepth = candidateDepth;
                    }



                    MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref CAnormal, out candidateDepth, out candidateNormal);
                    //Check to see if the normal is facing in the proper direction, considering that this may not be a two-sided triangle.
                    Vector3.Dot(ref triangleNormal, ref candidateNormal, out dot);
                    if ((triangle.sidedness == TriangleSidedness.Clockwise && dot > 0) || (triangle.sidedness == TriangleSidedness.Counterclockwise && dot < 0))
                    {
                        //Normal was facing the wrong way.
                        //Instead of ignoring it entirely, correct the direction to as close as it can get by removing any component parallel to the triangle normal.
                        Vector3 previousNormal = candidateNormal;
                        Vector3.Dot(ref candidateNormal, ref triangleNormal, out dot);

                        Vector3 p;
                        Vector3.Multiply(ref candidateNormal, dot, out p);
                        Vector3.Subtract(ref candidateNormal, ref p, out candidateNormal);
                        float length = candidateNormal.LengthSquared();
                        if (length > Toolbox.Epsilon)
                        {
                            //Renormalize the corrected normal.
                            Vector3.Divide(ref candidateNormal, (float)Math.Sqrt(length), out candidateNormal);
                            Vector3.Dot(ref candidateNormal, ref previousNormal, out dot);
                            candidateDepth *= dot;
                        }
                        else
                        {
                            contact.PenetrationDepth = float.MaxValue;
                            contact.Normal           = new Vector3();
                        }
                    }
                    if (candidateDepth < contact.PenetrationDepth)
                    {
                        contact.Normal           = candidateNormal;
                        contact.PenetrationDepth = candidateDepth;
                    }



                    //Try the depth along the positive triangle normal.

                    //If it's clockwise, this direction is unnecessary (the resulting normal would be invalidated by the onesidedness of the triangle).
                    if (triangle.sidedness != TriangleSidedness.Clockwise)
                    {
                        MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref triangleNormal, out candidateDepth, out candidateNormal);
                        if (candidateDepth < contact.PenetrationDepth)
                        {
                            contact.Normal           = candidateNormal;
                            contact.PenetrationDepth = candidateDepth;
                        }
                    }

                    //Try the depth along the negative triangle normal.

                    //If it's counterclockwise, this direction is unnecessary (the resulting normal would be invalidated by the onesidedness of the triangle).
                    if (triangle.sidedness != TriangleSidedness.Counterclockwise)
                    {
                        Vector3.Negate(ref triangleNormal, out triangleNormal);
                        MPRToolbox.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref triangleNormal, out candidateDepth, out candidateNormal);
                        if (candidateDepth < contact.PenetrationDepth)
                        {
                            contact.Normal           = candidateNormal;
                            contact.PenetrationDepth = candidateDepth;
                        }
                    }
                }



                MPRToolbox.RefinePenetration(convex, triangle, ref Toolbox.RigidIdentity, contact.PenetrationDepth, ref contact.Normal, out contact.PenetrationDepth, out contact.Normal, out contact.Position);

                //It's possible for the normal to still face the 'wrong' direction according to one sided triangles.
                if (triangle.sidedness != TriangleSidedness.DoubleSided)
                {
                    Vector3.Dot(ref triangleNormal, ref contact.Normal, out dot);
                    if (dot < 0)
                    {
                        return(false);
                    }
                }


                ////The local casting can optionally continue.  Eventually, it will converge to the local minimum.
                //int optimizingCount = 0;
                //while (true)
                //{

                //    MPRTesting.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref contact.Normal, out candidateDepth, out candidateNormal);
                //    if (contact.PenetrationDepth - candidateDepth <= Toolbox.BigEpsilon ||
                //        ++optimizingCount < 4)
                //    {
                //        //If we've reached the end due to convergence, the normal will be extremely close to correct (if not 100% correct).
                //        //The candidateDepth computed is the previous contact normal's depth.
                //        //The reason why the previous normal is kept is that the last raycast computed the depth for that normal, not the new normal.
                //        contact.PenetrationDepth = candidateDepth;
                //        break;
                //    }


                //    contact.PenetrationDepth = candidateDepth;
                //    contact.Normal = candidateNormal;
                //}

                //Correct the penetration depth.
                //MPRTesting.LocalSurfaceCast(convex, triangle, ref Toolbox.RigidIdentity, ref contact.Normal, out contact.PenetrationDepth, out center); //Center is just a trash variable now.

                contact.Id = -1;

                if (contact.PenetrationDepth < convex.collisionMargin + triangle.collisionMargin)
                {
                    state = CollisionState.ExternalNear; //If it's emerged from the deep contact, we can go back to using the preferred GJK method.
                }
                contactList.Add(ref contact);
            }



            if (TryInnerSphereContact(out contact))
            {
                contactList.Add(ref contact);
            }
            if (contactList.count > 0)
            {
                return(true);
            }

            state = CollisionState.ExternalSeparated;
            return(false);
        }
        public override void Update(Fix64 dt)
        {
            if (Game.KeyboardInput.IsKeyDown(Keys.Left))
            {
                rayCastDirection = Matrix3x3.Transform(rayCastDirection, Matrix3x3.CreateFromAxisAngle(Vector3.Forward, .01m));
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.Right))
            {
                rayCastDirection = Matrix3x3.Transform(rayCastDirection, Matrix3x3.CreateFromAxisAngle(Vector3.Forward, -.01m));
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.Down))
            {
                rayCastDirection = Matrix3x3.Transform(rayCastDirection, Matrix3x3.CreateFromAxisAngle(Vector3.Right, .01m));
            }
            if (Game.KeyboardInput.IsKeyDown(Keys.Up))
            {
                rayCastDirection = Matrix3x3.Transform(rayCastDirection, Matrix3x3.CreateFromAxisAngle(Vector3.Right, -.01m));
            }


            if (Game.KeyboardInput.IsKeyDown(Keys.P))
            {
                Debug.WriteLine("Break.");
            }

            base.Update(dt);

            RigidTransform localTransformB;
            RigidTransform aTransform = a.CollisionInformation.WorldTransform, bTransform = b.CollisionInformation.WorldTransform;

            MinkowskiToolbox.GetLocalTransform(ref aTransform, ref bTransform, out localTransformB);

            Vector3 position;

            if (MPRToolbox.GetLocalOverlapPosition((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, out position))
            {
                //Vector3 rayCastDirection = new Vector3(1,0,0);// (Vector3.Normalize(localDirection) + Vector3.Normalize(collidableB.worldTransform.Position - collidableA.worldTransform.Position)) / 2;
                Fix64   previousT;
                Vector3 previousNormal;
                Fix64   t;
                Vector3 normal;

                rayCastDirection = localTransformB.Position;
                MPRToolbox.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref rayCastDirection, out previousT, out previousNormal);
                //Vector3 secondDirection = Vector3.Cross(rayCastDirection, Vector3.Up);
                //MPRTesting.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref secondDirection, out t, out normal);
                //if (t < previousT)
                //{
                //    previousNormal = normal;
                //    previousT = t;
                //}
                //Vector3 thirdDirection = Vector3.Cross(secondDirection, rayCastDirection);
                //MPRTesting.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref thirdDirection, out t, out normal);
                //if (t < previousT)
                //{
                //    previousNormal = normal;
                //    previousT = t;
                //}
                //Vector3 fourthDirection = -secondDirection;
                //MPRTesting.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref fourthDirection, out t, out normal);
                //if (t < previousT)
                //{
                //    previousNormal = normal;
                //    previousT = t;
                //}
                //Vector3 fifthDirection = -thirdDirection;
                //MPRTesting.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref fifthDirection, out t, out normal);
                //if (t < previousT)
                //{
                //    previousNormal = normal;
                //    previousT = t;
                //}

                //Correct the penetration depth.

                MPRToolbox.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref previousNormal, out t, out normal);
                contactDepth  = t;
                contactNormal = previousNormal;

                ////Converge to local minimum.
                //while (true)
                //{
                //    MPRTesting.LocalSurfaceCast((a.CollisionInformation.Shape as ConvexShape), (b.CollisionInformation.Shape as ConvexShape), ref localTransformB, ref previousNormal, out t, out normal);
                //    if (previousT - t <= Toolbox.BigEpsilon)
                //        break;

                //    previousT = t;
                //    previousNormal = normal;
                //}
            }

            #region Box Box minkowski sum
            ////Construct explicit minkowski sum.
            //Vector3[] aLines = new Vector3[8];
            //aLines[0] = new Vector3(-boxWidth / 2, -boxHeight / 2, -boxLength / 2);
            //aLines[1] = new Vector3(-boxWidth / 2, -boxHeight / 2, boxLength / 2);
            //aLines[2] = new Vector3(-boxWidth / 2, boxHeight / 2, -boxLength / 2);
            //aLines[3] = new Vector3(-boxWidth / 2, boxHeight / 2, boxLength / 2);
            //aLines[4] = new Vector3(boxWidth / 2, -boxHeight / 2, -boxLength / 2);
            //aLines[5] = new Vector3(boxWidth / 2, -boxHeight / 2, boxLength / 2);
            //aLines[6] = new Vector3(boxWidth / 2, boxHeight / 2, -boxLength / 2);
            //aLines[7] = new Vector3(boxWidth / 2, boxHeight / 2, boxLength / 2);

            //Vector3[] bLines = new Vector3[8];
            //bLines[0] = new Vector3(-groundWidth / 2, -groundHeight / 2, -groundLength / 2);
            //bLines[1] = new Vector3(-groundWidth / 2, -groundHeight / 2, groundLength / 2);
            //bLines[2] = new Vector3(-groundWidth / 2, groundHeight / 2, -groundLength / 2);
            //bLines[3] = new Vector3(-groundWidth / 2, groundHeight / 2, groundLength / 2);
            //bLines[4] = new Vector3(groundWidth / 2, -groundHeight / 2, -groundLength / 2);
            //bLines[5] = new Vector3(groundWidth / 2, -groundHeight / 2, groundLength / 2);
            //bLines[6] = new Vector3(groundWidth / 2, groundHeight / 2, -groundLength / 2);
            //bLines[7] = new Vector3(groundWidth / 2, groundHeight / 2, groundLength / 2);

            //for (int i = 0; i < 8; i++)
            //    aLines[i] = Vector3.Transform(aLines[i], localTransformB.Matrix);

            //List<Vector3> vertices = new List<Vector3>();
            //for (int i = 0; i < 8; i++)
            //{
            //    for (int j = 0; j < 8; j++)
            //    {

            //        if (b.CollisionInformation.Pairs.Count > 0)
            //        {
            //            if (b.CollisionInformation.Pairs[0].BroadPhaseOverlap.EntryA == b.CollisionInformation)
            //                vertices.Add(aLines[i] - bLines[j]);
            //            else
            //                vertices.Add(bLines[i] - aLines[j]);
            //        }
            //        else
            //        {
            //            vertices.Add(bLines[i] - aLines[j]);
            //        }
            //    }
            //}

            //var indices = new List<int>();
            //Toolbox.GetConvexHull(vertices, indices);
            #endregion

            #region Arbitrary minkowski sum
            var     vertices = new List <Vector3>();
            Vector3 max;
            var     direction   = new Vector3();
            int     NumSamples  = 16;
            Fix64   angleChange = MathHelper.TwoPi / NumSamples;

            for (int i = 1; i < NumSamples / 2 - 1; i++)
            {
                Fix64 phi    = MathHelper.PiOver2 - i * angleChange;
                var   sinPhi = Fix64.Sin(phi);
                var   cosPhi = Fix64.Cos(phi);
                for (int j = 0; j < NumSamples; j++)
                {
                    Fix64 theta = j * angleChange;
                    direction.X = Fix64.Cos(theta) * cosPhi;
                    direction.Y = sinPhi;
                    direction.Z = Fix64.Sin(theta) * cosPhi;


                    MinkowskiToolbox.GetLocalMinkowskiExtremePoint(a.CollisionInformation.Shape as ConvexShape, b.CollisionInformation.Shape as ConvexShape, ref direction, ref localTransformB, out max);

                    vertices.Add(max);
                }
            }


            MinkowskiToolbox.GetLocalMinkowskiExtremePoint(a.CollisionInformation.Shape as ConvexShape, b.CollisionInformation.Shape as ConvexShape, ref Toolbox.UpVector, ref localTransformB, out max);
            vertices.Add(max);
            MinkowskiToolbox.GetLocalMinkowskiExtremePoint(a.CollisionInformation.Shape as ConvexShape, b.CollisionInformation.Shape as ConvexShape, ref Toolbox.DownVector, ref localTransformB, out max);
            vertices.Add(max);



            var indices = new List <int>();
            ConvexHullHelper.GetConvexHull(vertices, indices);
            #endregion

            minkowskiLines.Clear();
            for (int i = 0; i < indices.Count; i += 3)
            {
                minkowskiLines.Add(new VertexPositionColor(MathConverter.Convert(vertices[indices[i]]), Microsoft.Xna.Framework.Color.Blue));
                minkowskiLines.Add(new VertexPositionColor(MathConverter.Convert(vertices[indices[i + 1]]), Microsoft.Xna.Framework.Color.Blue));

                minkowskiLines.Add(new VertexPositionColor(MathConverter.Convert(vertices[indices[i + 1]]), Microsoft.Xna.Framework.Color.Blue));
                minkowskiLines.Add(new VertexPositionColor(MathConverter.Convert(vertices[indices[i + 2]]), Microsoft.Xna.Framework.Color.Blue));

                minkowskiLines.Add(new VertexPositionColor(MathConverter.Convert(vertices[indices[i + 2]]), Microsoft.Xna.Framework.Color.Blue));
                minkowskiLines.Add(new VertexPositionColor(MathConverter.Convert(vertices[indices[i]]), Microsoft.Xna.Framework.Color.Blue));
            }
        }
Exemple #17
0
        private bool DoDeepContact(out ContactData contact)
        {
            #region Informed search
            if (previousState == CollisionState.Separated) //If it was shallow before, then its closest points will be used to find the normal.
            {
                //It's overlapping! Find the relative velocity at the point relative to the two objects.  The point is still in local space!
                //Vector3 velocityA;
                //Vector3.Cross(ref contact.Position, ref collidableA.entity.angularVelocity, out velocityA);
                //Vector3.Add(ref velocityA, ref collidableA.entity.linearVelocity, out velocityA);
                //Vector3 velocityB;
                //Vector3.Subtract(ref contact.Position, ref localTransformB.Position, out velocityB);
                //Vector3.Cross(ref velocityB, ref collidableB.entity.angularVelocity, out velocityB);
                //Vector3.Add(ref velocityB, ref collidableB.entity.linearVelocity, out velocityB);
                ////The velocity is negated because the direction so point backwards along the velocity.
                //Vector3.Subtract(ref velocityA, ref velocityB, out localDirection);

                //The above takes into account angular velocity, but linear velocity alone is a lot more stable and does the job just fine.
                if (collidableA.entity != null && collidableB.entity != null)
                {
                    Vector3.Subtract(ref collidableA.entity.linearVelocity, ref collidableB.entity.linearVelocity, out localDirection);
                }
                else
                {
                    localDirection = localSeparatingAxis;
                }

                if (localDirection.LengthSquared() < Toolbox.Epsilon)
                {
                    localDirection = Vector3.Up;
                }
            }
            if (MPRToolbox.GetContact(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, ref localDirection, out contact))
            {
                if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin)
                {
                    state = CollisionState.ShallowContact;
                }
                return(true);
            }
            //This is rare, but could happen.
            state = CollisionState.Separated;
            return(false);

            //if (MPRTesting.GetLocalOverlapPosition(collidableA.Shape, collidableB.Shape, ref localTransformB, out contact.Position))
            //{


            //    //First, try to use the heuristically found direction.  This comes from either the GJK shallow contact separating axis or from the relative velocity.
            //    Vector3 rayCastDirection;
            //    float lengthSquared = localDirection.LengthSquared();
            //    if (lengthSquared > Toolbox.Epsilon)
            //    {
            //        Vector3.Divide(ref localDirection, (float)Math.Sqrt(lengthSquared), out rayCastDirection);// (Vector3.Normalize(localDirection) + Vector3.Normalize(collidableB.worldTransform.Position - collidableA.worldTransform.Position)) / 2;
            //        MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref rayCastDirection, out contact.PenetrationDepth, out contact.Normal);
            //    }
            //    else
            //    {
            //        contact.PenetrationDepth = float.MaxValue;
            //        contact.Normal = Toolbox.UpVector;
            //    }
            //    //Try the offset between the origins as a second option.  Sometimes this is a better choice than the relative velocity.
            //    //TODO: Could use the position-finding MPR iteration to find the A-B direction hit by continuing even after the origin has been found (optimization).
            //    Vector3 normalCandidate;
            //    float depthCandidate;
            //    lengthSquared = localTransformB.Position.LengthSquared();
            //    if (lengthSquared > Toolbox.Epsilon)
            //    {
            //        Vector3.Divide(ref localTransformB.Position, (float)Math.Sqrt(lengthSquared), out rayCastDirection);
            //        MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref rayCastDirection, out depthCandidate, out normalCandidate);
            //        if (depthCandidate < contact.PenetrationDepth)
            //        {
            //            contact.Normal = normalCandidate;
            //        }
            //    }


            //    //Correct the penetration depth.
            //    MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref contact.Normal, out contact.PenetrationDepth, out rayCastDirection);


            //    ////The local casting can optionally continue.  Eventually, it will converge to the local minimum.
            //    //while (true)
            //    //{
            //    //    MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref contact.Normal, out depthCandidate, out normalCandidate);
            //    //    if (contact.PenetrationDepth - depthCandidate <= Toolbox.BigEpsilon)
            //    //        break;

            //    //    contact.PenetrationDepth = depthCandidate;
            //    //    contact.Normal = normalCandidate;
            //    //}

            //    contact.Id = -1;
            //    //we're still in local space! transform it all back.
            //    Matrix3X3 orientation;
            //    Matrix3X3.CreateFromQuaternion(ref collidableA.worldTransform.Orientation, out orientation);
            //    Matrix3X3.Transform(ref contact.Normal, ref orientation, out contact.Normal);
            //    //Vector3.Negate(ref contact.Normal, out contact.Normal);
            //    Matrix3X3.Transform(ref contact.Position, ref orientation, out contact.Position);
            //    Vector3.Add(ref contact.Position, ref collidableA.worldTransform.Position, out contact.Position);
            //    if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin)
            //        state = CollisionState.ShallowContact;
            //    return true;
            //}

            ////This is rare, but could happen.
            //state = CollisionState.Separated;
            //contact = new ContactData();
            //return false;
            #endregion

            #region Testing
            //RigidTransform localTransformB;
            //MinkowskiToolbox.GetLocalTransform(ref collidableA.worldTransform, ref collidableB.worldTransform, out localTransformB);
            //contact.Id = -1;
            //if (MPRTesting.GetLocalOverlapPosition(collidableA.Shape, collidableB.Shape, ref localTransformB, out contact.Position))
            //{
            //    Vector3 rayCastDirection = localTransformB.Position;
            //    MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref rayCastDirection, out contact.PenetrationDepth, out contact.Normal);
            //    MPRTesting.LocalSurfaceCast(collidableA.Shape, collidableB.Shape, ref localTransformB, ref contact.Normal, out contact.PenetrationDepth, out rayCastDirection);
            //    RigidTransform.Transform(ref contact.Position, ref collidableA.worldTransform, out contact.Position);
            //    Vector3.Transform(ref contact.Normal, ref collidableA.worldTransform.Orientation, out contact.Normal);
            //    return true;
            //}
            //contact.Normal = new Vector3();
            //contact.PenetrationDepth = 0;
            //return false;
            #endregion

            #region v0.15.2 and before
            //if (MPRToolbox.AreObjectsColliding(collidableA.Shape, collidableB.Shape, ref collidableA.worldTransform, ref collidableB.worldTransform, out contact))
            //{
            //    if (contact.PenetrationDepth < collidableA.Shape.collisionMargin + collidableB.Shape.collisionMargin)
            //        state = CollisionState.ShallowContact; //If it's emerged from the deep contact, we can go back to using the preferred GJK method.
            //    return true;
            //}
            ////This is rare, but could happen.
            //state = CollisionState.Separated;
            //return false;
            #endregion
        }
Exemple #18
0
        public override void UpdateCollision(float dt)
        {
            WasContaining = Containing;
            WasTouching   = Touching;

            mobileTriangle.collisionMargin = mesh.Shape.MeshCollisionMargin;

            //Scan the pairs in sequence, updating the state as we go.
            //Touching can be set to true by a single touching subpair.
            Touching = false;
            //Containing can be set to false by a single noncontaining or nontouching subpair.
            Containing = true;


            var            meshData = mesh.Shape.TriangleMesh.Data;
            RigidTransform mobileTriangleTransform, detectorTriangleTransform;

            mobileTriangleTransform.Orientation   = Quaternion.Identity;
            detectorTriangleTransform.Orientation = Quaternion.Identity;
            for (int i = 0; i < meshData.Indices.Length; i += 3)
            {
                //Grab a triangle associated with the mobile mesh.
                meshData.GetTriangle(i, out mobileTriangle.vA, out mobileTriangle.vB, out mobileTriangle.vC);
                RigidTransform.Transform(ref mobileTriangle.vA, ref mesh.worldTransform, out mobileTriangle.vA);
                RigidTransform.Transform(ref mobileTriangle.vB, ref mesh.worldTransform, out mobileTriangle.vB);
                RigidTransform.Transform(ref mobileTriangle.vC, ref mesh.worldTransform, out mobileTriangle.vC);
                Vector3f.Add(ref mobileTriangle.vA, ref mobileTriangle.vB, out mobileTriangleTransform.Position);
                Vector3f.Add(ref mobileTriangle.vC, ref mobileTriangleTransform.Position, out mobileTriangleTransform.Position);
                Vector3f.Multiply(ref mobileTriangleTransform.Position, 1 / 3f, out mobileTriangleTransform.Position);
                Vector3f.Subtract(ref mobileTriangle.vA, ref mobileTriangleTransform.Position, out mobileTriangle.vA);
                Vector3f.Subtract(ref mobileTriangle.vB, ref mobileTriangleTransform.Position, out mobileTriangle.vB);
                Vector3f.Subtract(ref mobileTriangle.vC, ref mobileTriangleTransform.Position, out mobileTriangle.vC);

                //Go through all the detector volume triangles which are near the mobile mesh triangle.
                bool        triangleTouching, triangleContaining;
                BoundingBox mobileBoundingBox;
                mobileTriangle.GetBoundingBox(ref mobileTriangleTransform, out mobileBoundingBox);
                DetectorVolume.TriangleMesh.Tree.GetOverlaps(mobileBoundingBox, overlaps);
                for (int j = 0; j < overlaps.Count; j++)
                {
                    DetectorVolume.TriangleMesh.Data.GetTriangle(overlaps.Elements[j], out detectorTriangle.vA, out detectorTriangle.vB, out detectorTriangle.vC);
                    Vector3f.Add(ref detectorTriangle.vA, ref detectorTriangle.vB, out detectorTriangleTransform.Position);
                    Vector3f.Add(ref detectorTriangle.vC, ref detectorTriangleTransform.Position, out detectorTriangleTransform.Position);
                    Vector3f.Multiply(ref detectorTriangleTransform.Position, 1 / 3f, out detectorTriangleTransform.Position);
                    Vector3f.Subtract(ref detectorTriangle.vA, ref detectorTriangleTransform.Position, out detectorTriangle.vA);
                    Vector3f.Subtract(ref detectorTriangle.vB, ref detectorTriangleTransform.Position, out detectorTriangle.vB);
                    Vector3f.Subtract(ref detectorTriangle.vC, ref detectorTriangleTransform.Position, out detectorTriangle.vC);

                    //If this triangle collides with the convex, we can stop immediately since we know we're touching and not containing.)))
                    //[MPR is used here in lieu of GJK because the MPR implementation tends to finish quicker than GJK when objects are overlapping.  The GJK implementation does better on separated objects.]
                    if (MPRToolbox.AreShapesOverlapping(detectorTriangle, mobileTriangle, ref detectorTriangleTransform, ref mobileTriangleTransform))
                    {
                        triangleTouching = true;
                        //The convex can't be fully contained if it's still touching the surface.
                        triangleContaining = false;
                        overlaps.Clear();
                        goto finishTriangleTest;
                    }
                }

                overlaps.Clear();
                //If we get here, then there was no shell intersection.
                //If the convex's center point is contained by the mesh, then the convex is fully contained.
                //This test is only needed if containment hasn't yet been outlawed or a touching state hasn't been established.
                if ((!Touching || Containing) && DetectorVolume.IsPointContained(ref mobileTriangleTransform.Position, overlaps))
                {
                    triangleTouching   = true;
                    triangleContaining = true;
                    goto finishTriangleTest;
                }

                //If we get here, then there was no surface intersection and the convex's center is not contained- the volume and convex are separate!
                triangleTouching   = false;
                triangleContaining = false;

finishTriangleTest:
                //Analyze the results of the triangle test.

                if (triangleTouching)
                {
                    Touching = true; //If one child is touching, then we are touching too.
                }
                else
                {
                    Containing = false;  //If one child isn't touching, then we aren't containing.
                }
                if (!triangleContaining) //If one child isn't containing, then we aren't containing.
                {
                    Containing = false;
                }

                if (!Containing && Touching)
                {
                    //If it's touching but not containing, no further pairs will change the state.
                    //Containment has been invalidated by something that either didn't touch or wasn't contained.
                    //Touching has been ensured by at least one object touching.
                    break;
                }
            }

            //There is a possibility that the MobileMesh is solid and fully contains the DetectorVolume.
            //In this case, we should be Touching, but currently we are not.
            if (mesh.Shape.solidity == MobileMeshSolidity.Solid && !Containing && !Touching)
            {
                //To determine if the detector volume is fully contained, check if one of the detector mesh's vertices
                //are in the mobile mesh.

                //This *could* fail if the mobile mesh is actually multiple pieces, but that's not a common or really supported case for solids.
                Vector3f vertex;
                DetectorVolume.TriangleMesh.Data.GetVertexPosition(0, out vertex);
                Ray ray;
                ray.Direction = VectorHelper.Up;
                RayHit hit;
                RigidTransform.TransformByInverse(ref vertex, ref mesh.worldTransform, out ray.Position);
                if (mesh.Shape.IsLocalRayOriginInMesh(ref ray, out hit))
                {
                    Touching = true;
                }
            }

            NotifyDetectorVolumeOfChanges();
        }
Exemple #19
0
 public override bool ConvexCast(ConvexShape castShape, ref RigidTransform startingTransform, ref System.Numerics.Vector3 sweep, out RayHit hit)
 {
     return(MPRToolbox.Sweep(castShape, Shape, ref sweep, ref Toolbox.ZeroVector, ref startingTransform, ref worldTransform, out hit));
 }