Ejemplo n.º 1
0
        public override void OnDrawGizmosSelected()
        {
            if (!drawGizmo)
            {
                return;
            }
            Vector3    position = transform.position;
            Quaternion rotation = transform.rotation;
            Vector3    scale    = m_localScaling;

            Gizmos.color = Color.yellow;
            BoxShape shape = GetCollisionShape() as BoxShape;

            Gizmos.matrix = this.transform.localToWorldMatrix * Matrix4x4.Scale(transform.lossyScale).inverse;
            for (int i = 0; i < shape.NumEdges; i++)
            {
                BulletSharp.Math.Vector3 vertex1;
                BulletSharp.Math.Vector3 vertex2;
                shape.GetEdge(i, out vertex1, out vertex2);
                Vector3 vertexUnity1 = BSExtensionMethods2.ToUnity(vertex1);
                Vector3 vertexUnity2 = BSExtensionMethods2.ToUnity(vertex2);
                Gizmos.DrawLine(vertexUnity1, vertexUnity2);
            }
            //BUtility.DebugDrawBox(position, rotation, scale, extents, Color.yellow);
        }
Ejemplo n.º 2
0
        public override void ComputeCollision(ContactSet contactSet, CollisionQueryType type)
        {
            // Invoke GJK for closest points.
            if (type == CollisionQueryType.ClosestPoints)
            {
                throw new GeometryException("This collision algorithm cannot handle closest-point queries. Use GJK instead.");
            }

            CollisionObject  collisionObjectA = contactSet.ObjectA;
            CollisionObject  collisionObjectB = contactSet.ObjectB;
            IGeometricObject geometricObjectA = collisionObjectA.GeometricObject;
            IGeometricObject geometricObjectB = collisionObjectB.GeometricObject;
            BoxShape         boxA             = geometricObjectA.Shape as BoxShape;
            BoxShape         boxB             = geometricObjectB.Shape as BoxShape;

            // Check if collision objects shapes are correct.
            if (boxA == null || boxB == null)
            {
                throw new ArgumentException("The contact set must contain box shapes.", "contactSet");
            }

            Vector3F scaleA = Vector3F.Absolute(geometricObjectA.Scale);
            Vector3F scaleB = Vector3F.Absolute(geometricObjectB.Scale);
            Pose     poseA  = geometricObjectA.Pose;
            Pose     poseB  = geometricObjectB.Pose;

            // We perform the separating axis test in the local space of A.
            // The following variables are in local space of A.

            // Center of box B.
            Vector3F cB = poseA.ToLocalPosition(poseB.Position);
            // Orientation matrix of box B
            Matrix33F mB = poseA.Orientation.Transposed * poseB.Orientation;
            // Absolute of mB.
            Matrix33F aMB = Matrix33F.Absolute(mB);

            // Half extent vectors of the boxes.
            Vector3F eA = 0.5f * boxA.Extent * scaleA;
            Vector3F eB = 0.5f * boxB.Extent * scaleB;

            // ----- Separating Axis tests
            // If the boxes are separated, we immediately return.
            // For the case of interpenetration, we store the smallest penetration depth.
            float    smallestPenetrationDepth = float.PositiveInfinity;
            int      separatingAxisNumber     = 0;
            Vector3F normal           = Vector3F.UnitX;
            bool     isNormalInverted = false;

            contactSet.HaveContact = false;   // Assume no contact.

            #region ----- Case 1: Separating Axis: (1, 0, 0) -----
            float separation = Math.Abs(cB.X) - (eA.X + eB.X * aMB.M00 + eB.Y * aMB.M01 + eB.Z * aMB.M02);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean && -separation < smallestPenetrationDepth)
            {
                normal = Vector3F.UnitX;
                smallestPenetrationDepth = -separation;
                isNormalInverted         = cB.X < 0;
                separatingAxisNumber     = 1;
            }
            #endregion

            #region ----- Case 2: Separating Axis: (0, 1, 0) -----
            separation = Math.Abs(cB.Y) - (eA.Y + eB.X * aMB.M10 + eB.Y * aMB.M11 + eB.Z * aMB.M12);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean && -separation < smallestPenetrationDepth)
            {
                normal = Vector3F.UnitY;
                smallestPenetrationDepth = -separation;
                isNormalInverted         = cB.Y < 0;
                separatingAxisNumber     = 2;
            }
            #endregion

            #region ----- Case 3: Separating Axis: (0, 0, 1) -----
            separation = Math.Abs(cB.Z) - (eA.Z + eB.X * aMB.M20 + eB.Y * aMB.M21 + eB.Z * aMB.M22);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean && -separation < smallestPenetrationDepth)
            {
                normal = Vector3F.UnitZ;
                smallestPenetrationDepth = -separation;
                isNormalInverted         = cB.Z < 0;
                separatingAxisNumber     = 3;
            }
            #endregion

            #region ----- Case 4: Separating Axis: OrientationB * (1, 0, 0) -----
            float expression = cB.X * mB.M00 + cB.Y * mB.M10 + cB.Z * mB.M20;
            separation = Math.Abs(expression) - (eB.X + eA.X * aMB.M00 + eA.Y * aMB.M10 + eA.Z * aMB.M20);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean && -separation < smallestPenetrationDepth)
            {
                normal = mB.GetColumn(0);
                smallestPenetrationDepth = -separation;
                isNormalInverted         = expression < 0;
                separatingAxisNumber     = 4;
            }
            #endregion

            #region ----- Case 5: Separating Axis: OrientationB * (0, 1, 0) -----
            expression = cB.X * mB.M01 + cB.Y * mB.M11 + cB.Z * mB.M21;
            separation = Math.Abs(expression) - (eB.Y + eA.X * aMB.M01 + eA.Y * aMB.M11 + eA.Z * aMB.M21);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean && -separation < smallestPenetrationDepth)
            {
                normal = mB.GetColumn(1);
                smallestPenetrationDepth = -separation;
                isNormalInverted         = expression < 0;
                separatingAxisNumber     = 5;
            }
            #endregion

            #region ----- Case 6: Separating Axis: OrientationB * (0, 0, 1) -----
            expression = cB.X * mB.M02 + cB.Y * mB.M12 + cB.Z * mB.M22;
            separation = Math.Abs(expression) - (eB.Z + eA.X * aMB.M02 + eA.Y * aMB.M12 + eA.Z * aMB.M22);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean && -separation < smallestPenetrationDepth)
            {
                normal = mB.GetColumn(2);
                smallestPenetrationDepth = -separation;
                isNormalInverted         = expression < 0;
                separatingAxisNumber     = 6;
            }
            #endregion

            // The next 9 tests are edge-edge cases. The normal vector has to be normalized
            // to get the right penetration depth.
            // normal = Normalize(edgeA x edgeB)
            Vector3F separatingAxis;
            float    length;

            #region ----- Case 7: Separating Axis: (1, 0, 0) x (OrientationB * (1, 0, 0)) -----
            expression = cB.Z * mB.M10 - cB.Y * mB.M20;
            separation = Math.Abs(expression) - (eA.Y * aMB.M20 + eA.Z * aMB.M10 + eB.Y * aMB.M02 + eB.Z * aMB.M01);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(0, -mB.M20, mB.M10);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 7;
                }
            }
            #endregion

            #region ----- Case 8: Separating Axis: (1, 0, 0) x (OrientationB * (0, 1, 0)) -----
            expression = cB.Z * mB.M11 - cB.Y * mB.M21;
            separation = Math.Abs(expression) - (eA.Y * aMB.M21 + eA.Z * aMB.M11 + eB.X * aMB.M02 + eB.Z * aMB.M00);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(0, -mB.M21, mB.M11);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 8;
                }
            }
            #endregion

            #region ----- Case 9: Separating Axis: (1, 0, 0) x (OrientationB * (0, 0, 1)) -----
            expression = cB.Z * mB.M12 - cB.Y * mB.M22;
            separation = Math.Abs(expression) - (eA.Y * aMB.M22 + eA.Z * aMB.M12 + eB.X * aMB.M01 + eB.Y * aMB.M00);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(0, -mB.M22, mB.M12);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 9;
                }
            }
            #endregion

            #region ----- Case 10: Separating Axis: (0, 1, 0) x (OrientationB * (1, 0, 0)) -----
            expression = cB.X * mB.M20 - cB.Z * mB.M00;
            separation = Math.Abs(expression) - (eA.X * aMB.M20 + eA.Z * aMB.M00 + eB.Y * aMB.M12 + eB.Z * aMB.M11);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(mB.M20, 0, -mB.M00);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 10;
                }
            }
            #endregion

            #region ----- Case 11: Separating Axis: (0, 1, 0) x (OrientationB * (0, 1, 0)) -----
            expression = cB.X * mB.M21 - cB.Z * mB.M01;
            separation = Math.Abs(expression) - (eA.X * aMB.M21 + eA.Z * aMB.M01 + eB.X * aMB.M12 + eB.Z * aMB.M10);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(mB.M21, 0, -mB.M01);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 11;
                }
            }
            #endregion

            #region ----- Case 12: Separating Axis: (0, 1, 0) x (OrientationB * (0, 0, 1)) -----
            expression = cB.X * mB.M22 - cB.Z * mB.M02;
            separation = Math.Abs(expression) - (eA.X * aMB.M22 + eA.Z * aMB.M02 + eB.X * aMB.M11 + eB.Y * aMB.M10);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(mB.M22, 0, -mB.M02);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 12;
                }
            }
            #endregion

            #region ----- Case 13: Separating Axis: (0, 0, 1) x (OrientationB * (1, 0, 0)) -----
            expression = cB.Y * mB.M00 - cB.X * mB.M10;
            separation = Math.Abs(expression) - (eA.X * aMB.M10 + eA.Y * aMB.M00 + eB.Y * aMB.M22 + eB.Z * aMB.M21);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(-mB.M10, mB.M00, 0);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 13;
                }
            }
            #endregion

            #region ----- Case 14: Separating Axis: (0, 0, 1) x (OrientationB * (0, 1, 0)) -----
            expression = cB.Y * mB.M01 - cB.X * mB.M11;
            separation = Math.Abs(expression) - (eA.X * aMB.M11 + eA.Y * aMB.M01 + eB.X * aMB.M22 + eB.Z * aMB.M20);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(-mB.M11, mB.M01, 0);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 14;
                }
            }
            #endregion

            #region ----- Case 15: Separating Axis: (0, 0, 1) x (OrientationB * (0, 0, 1)) -----
            expression = cB.Y * mB.M02 - cB.X * mB.M12;
            separation = Math.Abs(expression) - (eA.X * aMB.M12 + eA.Y * aMB.M02 + eB.X * aMB.M21 + eB.Y * aMB.M20);
            if (separation > 0)
            {
                return;
            }

            if (type != CollisionQueryType.Boolean)
            {
                separatingAxis = new Vector3F(-mB.M12, mB.M02, 0);
                length         = separatingAxis.Length;
                separation    /= length;
                if (-separation < smallestPenetrationDepth)
                {
                    normal = separatingAxis / length;
                    smallestPenetrationDepth = -separation;
                    isNormalInverted         = expression < 0;
                    separatingAxisNumber     = 15;
                }
            }
            #endregion

            // We have a contact.
            contactSet.HaveContact = true;

            // HaveContact queries can exit here.
            if (type == CollisionQueryType.Boolean)
            {
                return;
            }

            // Lets find the contact info.
            Debug.Assert(smallestPenetrationDepth >= 0, "The smallest penetration depth should be greater than or equal to 0.");

            if (isNormalInverted)
            {
                normal = -normal;
            }

            // Transform normal from local space of A to world space.
            Vector3F normalWorld = poseA.ToWorldDirection(normal);

            if (separatingAxisNumber > 6)
            {
                // The intersection was detected by an edge-edge test.
                // Get the intersecting edges.
                // Separating axes:
                //  7 = x edge on A, x edge on B
                //  8 = x edge on A, y edge on B
                //  9 = x edge on A, Z edge on B
                // 10 = y edge on A, x edge on B
                // ...
                // 15 = z edge on A, z edge on B
                var edgeA = boxA.GetEdge((separatingAxisNumber - 7) / 3, normal, scaleA);
                var edgeB = boxB.GetEdge((separatingAxisNumber - 7) % 3, Matrix33F.MultiplyTransposed(mB, -normal), scaleB);
                edgeB.Start = mB * edgeB.Start + cB;
                edgeB.End   = mB * edgeB.End + cB;

                Vector3F position;
                Vector3F dummy;
                GeometryHelper.GetClosestPoints(edgeA, edgeB, out position, out dummy);
                position = position - normal * (smallestPenetrationDepth / 2); // Position is between the positions of the box surfaces.

                // Convert back position from local space of A to world space;
                position = poseA.ToWorldPosition(position);

                Contact contact = ContactHelper.CreateContact(contactSet, position, normalWorld, smallestPenetrationDepth, false);
                ContactHelper.Merge(contactSet, contact, type, CollisionDetection.ContactPositionTolerance);
            }
            else if (1 <= separatingAxisNumber && separatingAxisNumber <= 6)
            {
                // The intersection was detected by a face vs. * test.
                // The separating axis is perpendicular to a face.

                #region ----- Credits -----
                // The face vs. * test is based on the algorithm of the Bullet Continuous Collision
                // Detection and Physics Library. DigitalRune Geometry contains a new and improved
                // implementation of the original algorithm.
                //
                // The box-box detector in Bullet contains the following remarks:
                //
                //    Box-Box collision detection re-distributed under the ZLib license with permission from Russell L. Smith
                //    Original version is from Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.
                //    All rights reserved.  Email: [email protected]   Web: www.q12.org
                //
                //    Bullet Continuous Collision Detection and Physics Library
                //    Copyright (c) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
                //
                //    This software is provided 'as-is', without any express or implied warranty.
                //    In no event will the authors be held liable for any damages arising from the use of this software.
                //    Permission is granted to anyone to use this software for any purpose,
                //    including commercial applications, and to alter it and redistribute it freely,
                //    subject to the following restrictions:
                //
                //    1. The origin of this software must not be misrepresented; you must not claim that you wrote the
                //       original software. If you use this software in a product, an acknowledgment in the product
                //       documentation would be appreciated but is not required.
                //    2. Altered source versions must be plainly marked as such, and must not be misrepresented as being
                //       the original software.
                //    3. This notice may not be removed or altered from any source distribution.
                #endregion

                // We define the face perpendicular to the separating axis to be the "reference face".
                // The face of the other box closest to the reference face is called the "incident face".
                // Accordingly, we will call the box containing the reference face the "reference box" and
                // the box containing the incident face the "incident box".
                //
                // We will transform the incident face into the 2D space of reference face. Then we will
                // clip the incident face against the reference face. The polygon resulting from the
                // intersection will be transformed back into world space and the points of the polygon will
                // be the candidates for the contact points.

                Pose     poseR;      // Pose of reference box.
                Pose     poseI;      // Pose of incident box.
                Vector3F boxExtentR; // Half extent of reference box.
                Vector3F boxExtentI; // Half extent of incident box.

                // Contact normal (= normal of reference face) in world space.
                if (separatingAxisNumber <= 3)
                {
                    poseR            = poseA;
                    poseI            = poseB;
                    boxExtentR       = eA;
                    boxExtentI       = eB;
                    isNormalInverted = false;
                }
                else
                {
                    poseR            = poseB;
                    poseI            = poseA;
                    boxExtentR       = eB;
                    boxExtentI       = eA;
                    normalWorld      = -normalWorld;
                    isNormalInverted = true;
                }

                // Contact normal in local space of incident box.
                Vector3F normalI = poseI.ToLocalDirection(normalWorld);

                Vector3F absNormal = normalI;
                absNormal.Absolute();

                Vector3F xAxisInc, yAxisInc; // The basis of the incident-face space.
                float    absFaceOffsetI;     // The offset of the incident face to the center of the box.
                Vector2F faceExtentI;        // The half extent of the incident face.
                Vector3F faceNormal;         // The normal of the incident face in world space.
                float    faceDirection;      // A value indicating the direction of the incident face.

                // Find the largest component of the normal. The largest component indicates which face is
                // the incident face.
                switch (Vector3F.Absolute(normalI).IndexOfLargestComponent)
                {
                case 0:
                    faceExtentI.X  = boxExtentI.Y;
                    faceExtentI.Y  = boxExtentI.Z;
                    absFaceOffsetI = boxExtentI.X;
                    faceNormal     = poseI.Orientation.GetColumn(0);
                    xAxisInc       = poseI.Orientation.GetColumn(1);
                    yAxisInc       = poseI.Orientation.GetColumn(2);
                    faceDirection  = normalI.X;
                    break;

                case 1:
                    faceExtentI.X  = boxExtentI.X;
                    faceExtentI.Y  = boxExtentI.Z;
                    absFaceOffsetI = boxExtentI.Y;
                    faceNormal     = poseI.Orientation.GetColumn(1);
                    xAxisInc       = poseI.Orientation.GetColumn(0);
                    yAxisInc       = poseI.Orientation.GetColumn(2);
                    faceDirection  = normalI.Y;
                    break;

                // case 2:
                default:
                    faceExtentI.X  = boxExtentI.X;
                    faceExtentI.Y  = boxExtentI.Y;
                    absFaceOffsetI = boxExtentI.Z;
                    faceNormal     = poseI.Orientation.GetColumn(2);
                    xAxisInc       = poseI.Orientation.GetColumn(0);
                    yAxisInc       = poseI.Orientation.GetColumn(1);
                    faceDirection  = normalI.Z;
                    break;
                }

                // Compute center of incident face relative to the center of the reference box in world space.
                float    faceOffset    = (faceDirection < 0) ? absFaceOffsetI : -absFaceOffsetI;
                Vector3F centerOfFaceI = faceNormal * faceOffset + poseI.Position - poseR.Position;

                // (Note: We will use the center of the incident face to compute the points of the incident
                // face and transform the points into the reference-face frame. The center of the incident
                // face is relative to the center of the reference box. We could also get center of the
                // incident face relative to the center of the reference face. But since we are projecting
                // the points from 3D to 2D this does not matter.)

                Vector3F xAxisR, yAxisR; // The basis of the reference-face space.
                float    faceOffsetR;    // The offset of the reference face to the center of the box.
                Vector2F faceExtentR;    // The half extent of the reference face.
                switch (separatingAxisNumber)
                {
                case 1:
                case 4:
                    faceExtentR.X = boxExtentR.Y;
                    faceExtentR.Y = boxExtentR.Z;
                    faceOffsetR   = boxExtentR.X;
                    xAxisR        = poseR.Orientation.GetColumn(1);
                    yAxisR        = poseR.Orientation.GetColumn(2);
                    break;

                case 2:
                case 5:
                    faceExtentR.X = boxExtentR.X;
                    faceExtentR.Y = boxExtentR.Z;
                    faceOffsetR   = boxExtentR.Y;
                    xAxisR        = poseR.Orientation.GetColumn(0);
                    yAxisR        = poseR.Orientation.GetColumn(2);
                    break;

                // case 3:
                // case 6:
                default:
                    faceExtentR.X = boxExtentR.X;
                    faceExtentR.Y = boxExtentR.Y;
                    faceOffsetR   = boxExtentR.Z;
                    xAxisR        = poseR.Orientation.GetColumn(0);
                    yAxisR        = poseR.Orientation.GetColumn(1);
                    break;
                }

                // Compute the center of the incident face in the reference-face frame.
                // We can simply project centerOfFaceI onto the x- and y-axis of the reference
                // face.
                Vector2F centerOfFaceIInR;

                //centerOfFaceIInR.X = Vector3F.Dot(centerOfFaceI, xAxisR);
                // ----- Optimized version:
                centerOfFaceIInR.X = centerOfFaceI.X * xAxisR.X + centerOfFaceI.Y * xAxisR.Y + centerOfFaceI.Z * xAxisR.Z;

                //centerOfFaceIInR.Y = Vector3F.Dot(centerOfFaceI, yAxisR);
                // ----- Optimized version:
                centerOfFaceIInR.Y = centerOfFaceI.X * yAxisR.X + centerOfFaceI.Y * yAxisR.Y + centerOfFaceI.Z * yAxisR.Z;

                // Now, we have the center of the incident face in reference-face coordinates.
                // To compute the corners of the incident face in reference-face coordinates, we need
                // transform faceExtentI (the half extent vector of the incident face) from the incident-
                // face frame to the reference-face frame to compute the corners.
                //
                // The reference-face frame has the basis
                //   mR = (xAxisR, yAxisR, ?)
                //
                // The incident-face frame has the basis
                //   mI = (xAxisI, yAxisI, ?)
                //
                // Rotation from incident-face frame to reference-face frame is
                //   mIToR = mR^-1 * mI
                //
                // The corner offsets in incident-face space is are vectors (x, y, 0). To transform these
                // vectors from incident-face space to reference-face space we need to calculate:
                //   mIToR * v
                //
                // Since the z-components are 0 and we are only interested in the resulting x, y coordinates
                // in reference-space when can reduce the rotation to a 2 x 2 matrix. (The other components
                // are not needed.)

                // ----- Optimized version: (Original on the right)
                Matrix22F mIToR;
                mIToR.M00 = xAxisR.X * xAxisInc.X + xAxisR.Y * xAxisInc.Y + xAxisR.Z * xAxisInc.Z; // mIToR.M00 = Vector3F.Dot(xAxisR, xAxisInc);
                mIToR.M01 = xAxisR.X * yAxisInc.X + xAxisR.Y * yAxisInc.Y + xAxisR.Z * yAxisInc.Z; // mIToR.M01 = Vector3F.Dot(xAxisR, yAxisInc);
                mIToR.M10 = yAxisR.X * xAxisInc.X + yAxisR.Y * xAxisInc.Y + yAxisR.Z * xAxisInc.Z; // mIToR.M10 = Vector3F.Dot(yAxisR, xAxisInc);
                mIToR.M11 = yAxisR.X * yAxisInc.X + yAxisR.Y * yAxisInc.Y + yAxisR.Z * yAxisInc.Z; // mIToR.M11 = Vector3F.Dot(yAxisR, yAxisInc);

                // The corner offsets in incident-face space are:
                //  (-faceExtentI.X, -faceExtentI.Y) ... left, bottom corner
                //  ( faceExtentI.X, -faceExtentI.Y) ... right, bottom corner
                //  ( faceExtentI.X,  faceExtentI.Y) ... right, top corner
                //  (-faceExtentI.X,  faceExtentI.Y) ... left, top corner
                //
                // Instead of transforming each corner offset, we can optimize the computation: Do the
                // matrix-vector multiplication once, keep the intermediate products, apply the sign
                // of the components when adding the intermediate results.

                float           k1   = mIToR.M00 * faceExtentI.X; // Products of matrix-vector multiplication.
                float           k2   = mIToR.M01 * faceExtentI.Y;
                float           k3   = mIToR.M10 * faceExtentI.X;
                float           k4   = mIToR.M11 * faceExtentI.Y;
                List <Vector2F> quad = DigitalRune.ResourcePools <Vector2F> .Lists.Obtain();

                quad.Add(new Vector2F(centerOfFaceIInR.X - k1 - k2, centerOfFaceIInR.Y - k3 - k4));
                quad.Add(new Vector2F(centerOfFaceIInR.X + k1 - k2, centerOfFaceIInR.Y + k3 - k4));
                quad.Add(new Vector2F(centerOfFaceIInR.X + k1 + k2, centerOfFaceIInR.Y + k3 + k4));
                quad.Add(new Vector2F(centerOfFaceIInR.X - k1 + k2, centerOfFaceIInR.Y - k3 + k4));

                // Clip incident face (quadrilateral) against reference face (rectangle).
                List <Vector2F> contacts2D = ClipQuadrilateralAgainstRectangle(faceExtentR, quad);

                // Transform contact points back to world space and compute penetration depths.
                int             numberOfContacts = contacts2D.Count;
                List <Vector3F> contacts3D       = DigitalRune.ResourcePools <Vector3F> .Lists.Obtain();

                List <float> penetrationDepths = DigitalRune.ResourcePools <float> .Lists.Obtain();

                Matrix22F mRToI = mIToR.Inverse;
                for (int i = numberOfContacts - 1; i >= 0; i--)
                {
                    Vector2F contact2DR = contacts2D[i];                           // Contact in reference-face space.
                    Vector2F contact2DI = mRToI * (contact2DR - centerOfFaceIInR); // Contact in incident-face space.

                    // Transform point in incident-face space to world (relative to center of reference box).
                    // contact3D = mI * (x, y, 0) + centerOfFaceI
                    Vector3F contact3D;
                    contact3D.X = xAxisInc.X * contact2DI.X + yAxisInc.X * contact2DI.Y + centerOfFaceI.X;
                    contact3D.Y = xAxisInc.Y * contact2DI.X + yAxisInc.Y * contact2DI.Y + centerOfFaceI.Y;
                    contact3D.Z = xAxisInc.Z * contact2DI.X + yAxisInc.Z * contact2DI.Y + centerOfFaceI.Z;

                    // Compute penetration depth.

                    //float penetrationDepth = faceOffsetR - Vector3F.Dot(normalWorld, contact3D);
                    // ----- Optimized version:
                    float penetrationDepth = faceOffsetR - (normalWorld.X * contact3D.X + normalWorld.Y * contact3D.Y + normalWorld.Z * contact3D.Z);

                    if (penetrationDepth >= 0)
                    {
                        // Valid contact.
                        contacts3D.Add(contact3D);
                        penetrationDepths.Add(penetrationDepth);
                    }
                    else
                    {
                        // Remove bad contacts from the 2D contacts.
                        // (We might still need the 2D contacts, if we need to reduce the contacts.)
                        contacts2D.RemoveAt(i);
                    }
                }

                numberOfContacts = contacts3D.Count;
                if (numberOfContacts == 0)
                {
                    return; // Should never happen.
                }
                // Revert normal back to original direction.
                normal = (isNormalInverted) ? -normalWorld : normalWorld;

                // Note: normal ........ contact normal pointing from box A to B.
                //       normalWorld ... contact normal pointing from reference box to incident box.

                if (numberOfContacts <= MaxNumberOfContacts)
                {
                    // Add all contacts to contact set.
                    for (int i = 0; i < numberOfContacts; i++)
                    {
                        float penetrationDepth = penetrationDepths[i];

                        // Position is between the positions of the box surfaces.
                        Vector3F position = contacts3D[i] + poseR.Position + normalWorld * (penetrationDepth / 2);

                        Contact contact = ContactHelper.CreateContact(contactSet, position, normal, penetrationDepth, false);
                        ContactHelper.Merge(contactSet, contact, type, CollisionDetection.ContactPositionTolerance);
                    }
                }
                else
                {
                    // Reduce number of contacts, keep the contact with the max penetration depth.
                    int   indexOfDeepest      = 0;
                    float maxPenetrationDepth = penetrationDepths[0];
                    for (int i = 1; i < numberOfContacts; i++)
                    {
                        float penetrationDepth = penetrationDepths[i];
                        if (penetrationDepth > maxPenetrationDepth)
                        {
                            maxPenetrationDepth = penetrationDepth;
                            indexOfDeepest      = i;
                        }
                    }

                    List <int> indicesOfContacts = ReduceContacts(contacts2D, indexOfDeepest, MaxNumberOfContacts);

                    // Add selected contacts to contact set.
                    numberOfContacts = indicesOfContacts.Count;
                    for (int i = 0; i < numberOfContacts; i++)
                    {
                        int   index            = indicesOfContacts[i];
                        float penetrationDepth = penetrationDepths[index];

                        // Position is between the positions of the box surfaces.
                        Vector3F position = contacts3D[index] + poseR.Position + normalWorld * (penetrationDepth / 2);

                        Contact contact = ContactHelper.CreateContact(contactSet, position, normal, penetrationDepths[index], false);
                        ContactHelper.Merge(contactSet, contact, type, CollisionDetection.ContactPositionTolerance);
                    }

                    DigitalRune.ResourcePools <int> .Lists.Recycle(indicesOfContacts);
                }

                DigitalRune.ResourcePools <Vector2F> .Lists.Recycle(contacts2D);

                DigitalRune.ResourcePools <Vector3F> .Lists.Recycle(contacts3D);

                DigitalRune.ResourcePools <float> .Lists.Recycle(penetrationDepths);
            }
        }