How can I get penetration depth from Minkowski Portal Refinement / Xenocollide?

Posted by Raven Dreamer on Game Development See other posts from Game Development or by Raven Dreamer
Published on 2012-12-17T00:17:11Z Indexed on 2012/12/17 5:14 UTC
Read the original article Hit count: 871

I recently got an implementation of Minkowski Portal Refinement (MPR) successfully detecting collision. Even better, my implementation returns a good estimate (local minimum) direction for the minimum penetration depth.

So I took a stab at adjusting the algorithm to return the penetration depth in an arbitrary direction, and was modestly successful - my altered method works splendidly for face-edge collision resolution!

What it doesn't currently do, is correctly provide the minimum penetration depth for edge-edge scenarios, such as the case on the right:

enter image description here

What I perceive to be happening, is that my current method returns the minimum penetration depth to the nearest vertex - which works fine when the collision is actually occurring on the plane of that vertex, but not when the collision happens along an edge.

Is there a way I can alter my method to return the penetration depth to the point of collision, rather than the nearest vertex?

Here's the method that's supposed to return the minimum penetration distance along a specific direction:

public static Vector3 CalcMinDistance(List<Vector3> shape1, List<Vector3> shape2, Vector3 dir)
    {
        //holding variables
        Vector3 n = Vector3.zero;
        Vector3 swap = Vector3.zero;

        // v0 = center of Minkowski sum
        v0 = Vector3.zero;

        // Avoid case where centers overlap -- any direction is fine in this case
        //if (v0 == Vector3.zero) return Vector3.zero; //always pass in a valid direction.

        // v1 = support in direction of origin
        n = -dir;

        //get the differnce of the minkowski sum
        Vector3 v11 = GetSupport(shape1, -n);
        Vector3 v12 = GetSupport(shape2, n);

        v1 = v12 - v11;

        //if the support point is not in the direction of the origin
        if (v1.Dot(n) <= 0)
        {           
            //Debug.Log("Could find no points this direction");
            return Vector3.zero;
        }

        // v2 - support perpendicular to v1,v0
        n = v1.Cross(v0);
        if (n == Vector3.zero)
        {
            //v1 and v0 are parallel, which means 
            //the direction leads directly to an endpoint
            n = v1 - v0;

            //shortest distance is just n
            //Debug.Log("2 point return");
            return n;
        }

        //get the new support point
        Vector3 v21 = GetSupport(shape1, -n);
        Vector3 v22 = GetSupport(shape2, n);
        v2 = v22 - v21;

        if (v2.Dot(n) <= 0)
        {
            //can't reach the origin in this direction, ergo, no collision
            //Debug.Log("Could not reach edge?");
            return Vector2.zero;
        }

        // Determine whether origin is on + or - side of plane (v1,v0,v2)
        //tests linesegments v0v1 and v0v2
        n = (v1 - v0).Cross(v2 - v0);
        float dist = n.Dot(v0);

        // If the origin is on the - side of the plane, reverse the direction of the plane
        if (dist > 0)
        {
            //swap the winding order of v1 and v2
            swap = v1;
            v1 = v2;
            v2 = swap;

            //swap the winding order of v11 and v12
            swap = v12;
            v12 = v11;
            v11 = swap;

            //swap the winding order of v11 and v12
            swap = v22;
            v22 = v21;
            v21 = swap;

            //and swap the plane normal
            n = -n;
        }


        ///
        // Phase One: Identify a portal

        while (true)
        {
            // Obtain the support point in a direction perpendicular to the existing plane
            // Note: This point is guaranteed to lie off the plane
            Vector3 v31 = GetSupport(shape1, -n);
            Vector3 v32 = GetSupport(shape2, n); 
            v3 = v32 - v31;

            if (v3.Dot(n) <= 0)
            {
                //can't enclose the origin within our tetrahedron
                //Debug.Log("Could not reach edge after portal?");
                return Vector3.zero;
            }

            // If origin is outside (v1,v0,v3), then eliminate v2 and loop
            if (v1.Cross(v3).Dot(v0) < 0)
            {
                //failed to enclose the origin, adjust points;
                v2 = v3;
                v21 = v31;
                v22 = v32;
                n = (v1 - v0).Cross(v3 - v0);
                continue;
            }

            // If origin is outside (v3,v0,v2), then eliminate v1 and loop
            if (v3.Cross(v2).Dot(v0) < 0)
            {
                //failed to enclose the origin, adjust points;
                v1 = v3;
                v11 = v31;
                v12 = v32;
                n = (v3 - v0).Cross(v2 - v0);
                continue;
            }


            bool hit = false;

            ///
            // Phase Two: Refine the portal

            int phase2 = 0;

            // We are now inside of a wedge...
            while (phase2 < 20)
            {
                phase2++;

                // Compute normal of the wedge face
                n = (v2 - v1).Cross(v3 - v1);

                n.Normalize();

                // Compute distance from origin to wedge face
                float d = n.Dot(v1);

                // If the origin is inside the wedge, we have a hit
                if (d > 0 )
                {
                    //Debug.Log("Do plane test here");

                    float T = n.Dot(v2) / n.Dot(dir);
                    Vector3 pointInPlane = (dir * T);
                    return pointInPlane;
                }


                // Find the support point in the direction of the wedge face
                Vector3 v41 = GetSupport(shape1, -n);
                Vector3 v42 = GetSupport(shape2, n);
                v4 = v42 - v41;


                float delta = (v4 - v3).Dot(n);
                float separation = -(v4.Dot(n));

                if (delta <= kCollideEpsilon || separation >= 0)
                {
                    //Debug.Log("Non-convergance detected");    
                    //Debug.Log("Do plane test here");

                    return Vector3.zero;
                }

                // Compute the tetrahedron dividing face (v4,v0,v1)
                float d1 = v4.Cross(v1).Dot(v0);

                // Compute the tetrahedron dividing face (v4,v0,v2)
                float d2 = v4.Cross(v2).Dot(v0);

                // Compute the tetrahedron dividing face (v4,v0,v3)
                float d3 = v4.Cross(v3).Dot(v0);

                if (d1 < 0)
                {
                    if (d2 < 0)
                    {
                        // Inside d1 & inside d2 ==> eliminate v1
                        v1 = v4;
                        v11 = v41;
                        v12 = v42;
                    }
                    else
                    {
                        // Inside d1 & outside d2 ==> eliminate v3
                        v3 = v4;
                        v31 = v41;
                        v32 = v42;
                    }
                }
                else
                {
                    if (d3 < 0)
                    {
                        // Outside d1 & inside d3 ==> eliminate v2
                        v2 = v4;
                        v21 = v41;
                        v22 = v42;
                    }
                    else
                    {
                        // Outside d1 & outside d3 ==> eliminate v1
                        v1 = v4;
                        v11 = v41;
                        v12 = v42;
                    }
                }
            }   

            return Vector3.zero;
        }
    }

© Game Development or respective owner

Related posts about c#

Related posts about collision-detection