Example #1
0
 private static void CalcTrianglePlanes(float3 v0, float3 v1, float3 v2, float3 normalDirection,
                                        out FourTransposedPoints verts, out FourTransposedPoints edges, out FourTransposedPoints perps)
 {
     verts = new FourTransposedPoints(v0, v1, v2, v0);
     edges = verts.V1230 - verts;
     perps = edges.Cross(new FourTransposedPoints(normalDirection));
 }
Example #2
0
        public static Result TriangleSphere(
            float3 vertex0, float3 vertex1, float3 vertex2, float3 normal,
            float3 sphereCenter, float sphereRadius,
            MTransform aFromB)
        {
            // Sphere center in A-space (TODO.ma should probably work in sphere space, typical for triangle verts to be far from the origin in its local space)
            float3 pointB = Mul(aFromB, sphereCenter);

            // Calculate triangle edges and edge planes
            FourTransposedPoints vertsA;
            FourTransposedPoints edgesA;
            FourTransposedPoints perpsA;

            CalcTrianglePlanes(vertex0, vertex1, vertex2, normal, out vertsA, out edgesA, out perpsA);

            // Check if the closest point is on the triangle face
            FourTransposedPoints rels = new FourTransposedPoints(pointB) - vertsA;

            if (PointTriangleFace(pointB, vertex0, normal, vertsA, edgesA, perpsA, rels, out float signedDistance))
            {
                return(new Result
                {
                    PositionOnAinA = pointB + normal * signedDistance,
                    NormalInA = math.select(normal, -normal, signedDistance < 0),
                    Distance = math.abs(signedDistance) - sphereRadius
                });
            }

            // Find the closest point on the triangle edges - project point onto the line through each edge, then clamp to the edge
            float4 nums = rels.Dot(edgesA);
            float4 dens = edgesA.Dot(edgesA);
            float4 sols = math.clamp(nums / dens, 0.0f, 1.0f); // fraction along the edge TODO.ma see how it handles inf/nan from divide by zero
            FourTransposedPoints projs = edgesA.MulT(sols) - rels;
            float4 distancesSq         = projs.Dot(projs);

            float3 proj0 = projs.GetPoint(0);
            float3 proj1 = projs.GetPoint(1);
            float3 proj2 = projs.GetPoint(2);

            // Find the closest projected point
            bool   less1      = distancesSq.x < distancesSq.y;
            float3 direction  = math.select(proj1, proj0, less1);
            float  distanceSq = math.select(distancesSq.y, distancesSq.x, less1);

            bool less2 = distanceSq < distancesSq.z;

            direction  = math.select(proj2, direction, less2);
            distanceSq = math.select(distancesSq.z, distanceSq, less2);

            const float triangleConvexRadius = 0.0f;

            return(PointPoint(pointB, direction, distanceSq, triangleConvexRadius, sphereRadius));
        }
Example #3
0
        // Checks if the closest point on the triangle is on its face.  If so returns true and sets signedDistance to the distance along the normal, otherwise returns false
        private static bool PointTriangleFace(float3 point, float3 v0, float3 normal,
                                              FourTransposedPoints verts, FourTransposedPoints edges, FourTransposedPoints perps, FourTransposedPoints rels, out float signedDistance)
        {
            float4 dots         = perps.Dot(rels);
            float4 dotsSq       = dots * math.abs(dots);
            float4 perpLengthSq = perps.Dot(perps);

            if (math.all(dotsSq <= perpLengthSq * distanceEpsSq))
            {
                // Closest point on face
                signedDistance = math.dot(v0 - point, normal);
                return(true);
            }

            signedDistance = float.MaxValue;
            return(false);
        }
Example #4
0
 public float4 Dot(FourTransposedPoints a) => X * a.X + Y * a.Y + Z * a.Z;
Example #5
0
 public FourTransposedPoints Cross(FourTransposedPoints a)
 {
     return(new FourTransposedPoints {
         m_TransposedPoints = new float4x3(Y * a.Z - Z * a.Y, Z * a.X - X * a.Z, X * a.Y - Y * a.X)
     });
 }
Example #6
0
            /// <summary>
            /// Compute the closest point on the simplex, returns true if the simplex contains a duplicate vertex
            /// </summary>
            public void SolveDistance()
            {
                int inputVertices = NumVertices;

                switch (NumVertices)
                {
                // Point.
                case 1:
                    Direction      = A.Xyz;
                    ScaledDistance = math.lengthsq(Direction);
                    break;

                // Line.
                case 2:
                {
                    float3 delta = B.Xyz - A.Xyz;
                    sfloat den   = math.dot(delta, delta);
                    sfloat num   = math.dot(-A.Xyz, delta);

                    // Reduce if closest point do not project on the line segment.
                    if (num >= den)
                    {
                        NumVertices = 1; A = B; goto case 1;
                    }

                    // Compute support direction
                    Direction      = math.cross(math.cross(delta, A.Xyz), delta);
                    ScaledDistance = math.dot(Direction, A.Xyz);
                }
                break;

                // Triangle.
                case 3:
                {
                    float3 ca = A.Xyz - C.Xyz;
                    float3 cb = B.Xyz - C.Xyz;
                    float3 n  = math.cross(cb, ca);

                    // Reduce if closest point do not project in the triangle.
                    float3 crossA = math.cross(cb, n);
                    float3 crossB = math.cross(n, ca);
                    sfloat detA   = math.dot(crossA, B.Xyz);
                    sfloat detB   = math.dot(crossB, C.Xyz);
                    if (detA < sfloat.Zero)
                    {
                        if (detB >= sfloat.Zero || Det(n, crossA, C.Xyz) < sfloat.Zero)
                        {
                            A = B;
                        }
                    }
                    else if (detB >= sfloat.Zero)
                    {
                        sfloat dot = math.dot(C.Xyz, n);
                        if (dot < sfloat.Zero)
                        {
                            // Reorder vertices so that n points away from the origin
                            SupportVertex temp = A;
                            A   = B;
                            B   = temp;
                            n   = -n;
                            dot = -dot;
                        }
                        Direction      = n;
                        ScaledDistance = dot;
                        break;
                    }

                    B           = C;
                    NumVertices = 2;
                    goto case 2;
                }

                // Tetrahedra.
                case 4:
                {
                    FourTransposedPoints tetra = new FourTransposedPoints(A.Xyz, B.Xyz, C.Xyz, D.Xyz);
                    FourTransposedPoints d     = new FourTransposedPoints(D.Xyz);

                    // This routine finds the closest feature to the origin on the tetra by testing the origin against the planes of the
                    // voronoi diagram. If the origin is near the border of two regions in the diagram, then the plane tests might exclude
                    // it from both because of float rounding.  To avoid this problem we use some tolerance testing the face planes and let
                    // EPA handle those border cases.  1e-5 is a somewhat arbitrary value and the actual distance scales with the tetra, so
                    // this might need to be tuned later!
                    float3 faceTest = tetra.Cross(tetra.V1203).Dot(d).xyz;
                    if (math.all(faceTest >= sfloat.FromRaw(0xb727c5ac)))
                    {
                        // Origin is inside the tetra
                        Direction = float3.zero;
                        break;
                    }

                    // Check if the closest point is on a face
                    bool3 insideFace             = (faceTest >= sfloat.Zero).xyz;
                    FourTransposedPoints edges   = d - tetra;
                    FourTransposedPoints normals = edges.Cross(edges.V1203);
                    bool3 insideEdge0            = (normals.Cross(edges).Dot(d) >= sfloat.Zero).xyz;
                    bool3 insideEdge1            = (edges.V1203.Cross(normals).Dot(d) >= sfloat.Zero).xyz;
                    bool3 onFace = (insideEdge0 & insideEdge1 & !insideFace);
                    if (math.any(onFace))
                    {
                        if (onFace.y)
                        {
                            A = B; B = C;
                        }
                        else if (onFace.z)
                        {
                            B = C;
                        }
                    }
                    else
                    {
                        // Check if the closest point is on an edge
                        // TODO maybe we can safely drop two vertices in this case
                        bool3 insideVertex = (edges.Dot(d) >= 0).xyz;
                        bool3 onEdge       = (!insideEdge0 & !insideEdge1.zxy & insideVertex);
                        if (math.any(onEdge.yz))
                        {
                            A = B; B = C;
                        }
                    }

                    C           = D;
                    NumVertices = 3;
                    goto case 3;
                }
                }
            }
Example #7
0
        public static unsafe Result CapsuleTriangle(CapsuleCollider *capsuleA, PolygonCollider *triangleB, MTransform aFromB)
        {
            // Get vertices
            float3 c0 = capsuleA->Vertex0;
            float3 c1 = capsuleA->Vertex1;
            float3 t0 = Mul(aFromB, triangleB->ConvexHull.Vertices[0]);
            float3 t1 = Mul(aFromB, triangleB->ConvexHull.Vertices[1]);
            float3 t2 = Mul(aFromB, triangleB->ConvexHull.Vertices[2]);

            float3 direction;
            float  distanceSq;
            float3 pointCapsule;
            float  sign = 1.0f; // negated if penetrating
            {
                // Calculate triangle edges and edge planes
                float3 faceNormal = math.mul(aFromB.Rotation, triangleB->ConvexHull.Planes[0].Normal);
                FourTransposedPoints vertsB;
                FourTransposedPoints edgesB;
                FourTransposedPoints perpsB;
                CalcTrianglePlanes(t0, t1, t2, faceNormal, out vertsB, out edgesB, out perpsB);

                // c0 against triangle face
                {
                    FourTransposedPoints rels = new FourTransposedPoints(c0) - vertsB;
                    PointTriangleFace(c0, t0, faceNormal, vertsB, edgesB, perpsB, rels, out float signedDistance);
                    distanceSq = signedDistance * signedDistance;
                    if (distanceSq > distanceEpsSq)
                    {
                        direction = -faceNormal * signedDistance;
                    }
                    else
                    {
                        direction = math.select(faceNormal, -faceNormal, math.dot(c1 - c0, faceNormal) < 0); // rare case, capsule point is exactly on the triangle face
                    }
                    pointCapsule = c0;
                }

                // c1 against triangle face
                {
                    FourTransposedPoints rels = new FourTransposedPoints(c1) - vertsB;
                    PointTriangleFace(c1, t0, faceNormal, vertsB, edgesB, perpsB, rels, out float signedDistance);
                    float  distanceSq1 = signedDistance * signedDistance;
                    float3 direction1;
                    if (distanceSq1 > distanceEpsSq)
                    {
                        direction1 = -faceNormal * signedDistance;
                    }
                    else
                    {
                        direction1 = math.select(faceNormal, -faceNormal, math.dot(c0 - c1, faceNormal) < 0); // rare case, capsule point is exactly on the triangle face
                    }
                    SelectMin(ref direction, ref distanceSq, ref pointCapsule, direction1, distanceSq1, c1);
                }

                // axis against triangle edges
                float3 axis = c1 - c0;
                for (int i = 0; i < 3; i++)
                {
                    float3 closestOnCapsule, closestOnTriangle;
                    SegmentSegment(c0, axis, vertsB.GetPoint(i), edgesB.GetPoint(i), out closestOnCapsule, out closestOnTriangle);
                    float3 edgeDiff       = closestOnCapsule - closestOnTriangle;
                    float  edgeDistanceSq = math.lengthsq(edgeDiff);
                    edgeDiff = math.select(edgeDiff, perpsB.GetPoint(i), edgeDistanceSq < distanceEpsSq); // use edge plane if the capsule axis intersects the edge
                    SelectMin(ref direction, ref distanceSq, ref pointCapsule, edgeDiff, edgeDistanceSq, closestOnCapsule);
                }

                // axis against triangle face
                {
                    // Find the intersection of the axis with the triangle plane
                    float axisDot = math.dot(axis, faceNormal);
                    float dist0   = math.dot(t0 - c0, faceNormal); // distance from c0 to the plane along the normal
                    float t       = dist0 * math.select(math.rcp(axisDot), 0.0f, axisDot == 0.0f);
                    if (t > 0.0f && t < 1.0f)
                    {
                        // If they intersect, check if the intersection is inside the triangle
                        FourTransposedPoints rels = new FourTransposedPoints(c0 + axis * t) - vertsB;
                        float4 dots = perpsB.Dot(rels);
                        if (math.all(dots <= float4.zero))
                        {
                            // Axis intersects the triangle, choose the separating direction
                            float  dist1            = axisDot - dist0;
                            bool   use1             = math.abs(dist1) < math.abs(dist0);
                            float  dist             = math.select(-dist0, dist1, use1);
                            float3 closestOnCapsule = math.select(c0, c1, use1);
                            SelectMin(ref direction, ref distanceSq, ref pointCapsule, dist * faceNormal, dist * dist, closestOnCapsule);

                            // Even if the edge is closer than the face, we now know that the edge hit was penetrating
                            sign = -1.0f;
                        }
                    }
                }
            }

            float  invDistance = math.rsqrt(distanceSq);
            float  distance;
            float3 normal;

            if (distanceSq < distanceEpsSq)
            {
                normal   = math.normalize(direction); // rare case, capsule axis almost exactly touches the triangle
                distance = 0.0f;
            }
            else
            {
                normal   = direction * invDistance * sign; // common case, distanceSq = lengthsq(direction)
                distance = distanceSq * invDistance * sign;
            }
            return(new Result
            {
                NormalInA = normal,
                PositionOnAinA = pointCapsule - normal * capsuleA->Radius,
                Distance = distance - capsuleA->Radius
            });
        }