/// <summary>
        /// Finds the point on the surface of a hull closest to a world point.
        /// </summary>
        public static float3 ClosestPoint(RigidTransform t, NativeHull hull, float3 point)
        {
            float       distance         = -float.MaxValue;
            int         closestFaceIndex = -1;
            NativePlane closestPlane     = default;

            // Find the closest face plane.
            for (int i = 0; i < hull.FaceCount; ++i)
            {
                NativePlane plane = t * hull.GetPlane(i);
                float       d     = plane.Distance(point);
                if (d > distance)
                {
                    distance         = d;
                    closestFaceIndex = i;
                    closestPlane     = plane;
                }
            }

            var closestPlanePoint = closestPlane.ClosestPoint(point);

            if (distance > 0)
            {
                // Use a point along the closest edge if the plane point would be outside the face bounds.
                ref NativeFace     face    = ref hull.GetFaceRef(closestFaceIndex);
                ref NativeHalfEdge start   = ref hull.GetEdgeRef(face.Edge);
        public static CollisionInfo GetDebugCollisionInfo(RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            CollisionInfo result = default;

            QueryFaceDistance(out result.Face1, transform1, hull1, transform2, hull2);
            QueryFaceDistance(out result.Face2, transform2, hull2, transform1, hull1);
            QueryEdgeDistance(out result.Edge, transform1, hull1, transform2, hull2);
            result.IsColliding = result.Face1.Distance < 0 && result.Face2.Distance < 0 && result.Edge.Distance < 0;
            return(result);
        }
        /// <summary>
        /// Determines if a world point is contained within a hull
        /// </summary>
        public static bool Contains(RigidTransform t, NativeHull hull, float3 point)
        {
            float maxDistance = -float.MaxValue;

            for (int i = 0; i < hull.FaceCount; ++i)
            {
                NativePlane plane = t * hull.GetPlane(i);
                float       d     = plane.Distance(point);
                if (d > maxDistance)
                {
                    maxDistance = d;
                }
            }
            return(maxDistance < 0);
        }
Пример #4
0
        public static void DrawBasicHull(NativeHull hull1, RigidTransform t, Color?color = null, int duration = 1)
        {
            if (!hull1.IsValid)
            {
                throw new ArgumentException("Hull is not valid", nameof(hull1));
            }

            if (!color.HasValue)
            {
                color = UnityColors.Blue;
            }
            foreach (var edge in hull1.GetEdges())
            {
                var a = math.transform(t, hull1.GetVertex(edge.Origin));
                var b = math.transform(t, hull1.GetVertex(hull1.GetEdge(edge.Twin).Origin));
                Debug.DrawLine(a, b, color.Value);
            }
        }
        public static bool DrawNativeHullHullIntersection(RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            for (int i = 0; i < hull2.FaceCount; i++)
            {
                var tmp = new NativeManifold(Allocator.Temp);

                ClipFace(ref tmp, i, transform2, hull2, transform1, hull1);

                HullDrawingUtility.DebugDrawManifold(tmp);
                tmp.Dispose();
            }

            for (int i = 0; i < hull1.FaceCount; i++)
            {
                var tmp = new NativeManifold(Allocator.Temp);

                ClipFace(ref tmp, i, transform1, hull1, transform2, hull2);

                HullDrawingUtility.DebugDrawManifold(tmp);
                tmp.Dispose();
            }
            return(true);
        }
        public static bool IsColliding(RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            FaceQueryResult faceQuery;

            QueryFaceDistance(out faceQuery, transform1, hull1, transform2, hull2);
            if (faceQuery.Distance > 0)
            {
                return(false);
            }

            QueryFaceDistance(out faceQuery, transform2, hull2, transform1, hull1);
            if (faceQuery.Distance > 0)
            {
                return(false);
            }

            QueryEdgeDistance(out EdgeQueryResult edgeQuery, transform1, hull1, transform2, hull2);
            if (edgeQuery.Distance > 0)
            {
                return(false);
            }

            return(true);
        }
        public static unsafe void QueryEdgeDistance(out EdgeQueryResult result, RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            // Perform computations in the local space of the second hull.
            RigidTransform transform = math.mul(math.inverse(transform2), transform1);

            float3 C1 = transform.pos;

            result.Distance = -float.MaxValue;
            result.Index1   = -1;
            result.Index2   = -1;

            for (int i = 0; i < hull1.EdgeCount; i += 2)
            {
                NativeHalfEdge *edge1 = hull1.GetEdgePtr(i);
                NativeHalfEdge *twin1 = hull1.GetEdgePtr(i + 1);

                Debug.Assert(edge1->Twin == i + 1 && twin1->Twin == i);

                float3 P1 = math.transform(transform, hull1.GetVertex(edge1->Origin));
                float3 Q1 = math.transform(transform, hull1.GetVertex(twin1->Origin));
                float3 E1 = Q1 - P1;

                float3 U1 = math.rotate(transform, hull1.GetPlane(edge1->Face).Normal);
                float3 V1 = math.rotate(transform, hull1.GetPlane(twin1->Face).Normal);

                for (int j = 0; j < hull2.EdgeCount; j += 2)
                {
                    NativeHalfEdge *edge2 = hull2.GetEdgePtr(j);
                    NativeHalfEdge *twin2 = hull2.GetEdgePtr(j + 1);

                    Debug.Assert(edge2->Twin == j + 1 && twin2->Twin == j);

                    float3 P2 = hull2.GetVertex(edge2->Origin);
                    float3 Q2 = hull2.GetVertex(twin2->Origin);
                    float3 E2 = Q2 - P2;

                    float3 U2 = hull2.GetPlane(edge2->Face).Normal;
                    float3 V2 = hull2.GetPlane(twin2->Face).Normal;

                    if (IsMinkowskiFace(U1, V1, -E1, -U2, -V2, -E2))
                    {
                        float distance = Project(P1, E1, P2, E2, C1);
                        if (distance > result.Distance)
                        {
                            result.Index1   = i;
                            result.Index2   = j;
                            result.Distance = distance;
                        }
                    }
                }
            }
        }
Пример #8
0
        public static void DrawDebugHull(NativeHull hull, RigidTransform t, DebugHullFlags options = DebugHullFlags.All, Color BaseColor = default)
        {
            if (!hull.IsValid)
            {
                throw new ArgumentException("Hull is not valid", nameof(hull));
            }

            if (options == DebugHullFlags.None)
            {
                return;
            }

            if (BaseColor == default)
            {
                BaseColor = Color.yellow;
            }

            float faceExplosionDistance = (options & DebugHullFlags.ExplodeFaces) != 0 ? 0.3f : 0;

            // Iterate each twin pair at the same time.
            for (int j = 0; j < hull.EdgeCount; j = j + 2)
            {
                var edge = hull.GetEdge(j);
                var twin = hull.GetEdge(j + 1);

                var edgePlane = edge.Face != -1 ? hull.GetPlane(edge.Face) : new NativePlane();
                var twinPlane = twin.Face != -1 ? hull.GetPlane(twin.Face) : new NativePlane();

                var rotatedEdgeNormal = math.rotate(t, edgePlane.Normal);
                var rotatedTwinNormal = math.rotate(t, twinPlane.Normal);

                var edgeVertex1 = math.transform(t, hull.GetVertex(edge.Origin));
                var twinVertex1 = math.transform(t, hull.GetVertex(twin.Origin));
                var edgeVertex2 = math.transform(t, hull.GetVertex(edge.Origin));
                var twinVertex2 = math.transform(t, hull.GetVertex(twin.Origin));

                if ((options & DebugHullFlags.Outline) != 0)
                {
                    Debug.DrawLine(edgeVertex1 + rotatedEdgeNormal * faceExplosionDistance, twinVertex1 + rotatedEdgeNormal * faceExplosionDistance, BaseColor);
                    Debug.DrawLine(edgeVertex2 + rotatedTwinNormal * faceExplosionDistance, twinVertex2 + rotatedTwinNormal * faceExplosionDistance, BaseColor);
                }

                if ((options & DebugHullFlags.EdgeLinks) != 0)
                {
                    Debug.DrawLine((edgeVertex1 + twinVertex1) / 2 + rotatedEdgeNormal * faceExplosionDistance, (edgeVertex2 + twinVertex2) / 2 + rotatedTwinNormal * faceExplosionDistance, Color.gray);
                }
            }

            if ((options & DebugHullFlags.PlaneNormals) != 0)
            {
                for (int i = 0; i < hull.FaceCount; i++)
                {
                    var centroid = math.transform(t, hull.CalculateFaceCentroid(hull.GetFace(i)));
                    var normal   = math.rotate(t, hull.GetPlane(i).Normal);
                    DebugDrawer.DrawArrow(centroid, normal * 0.2f, BaseColor);
                }
            }

            if ((options & DebugHullFlags.Indices) != 0)
            {
                var dupeCheck = new HashSet <Vector3>();
                for (int i = 0; i < hull.VertexCount; i++)
                {
                    // Offset the label if multiple verts are on the same position.
                    var v      = math.transform(t, hull.GetVertex(i));
                    var offset = dupeCheck.Contains(v) ? (float3)Vector3.forward * 0.05f : 0;

                    DebugDrawer.DrawLabel(v + offset, i.ToString());
                    dupeCheck.Add(v);
                }
            }

            if ((options & DebugHullFlags.FaceWinding) != 0)
            {
                for (int i = 0; i < hull.FaceCount; i++)
                {
                    var face        = hull.GetFace(i);
                    var plane       = hull.GetPlane(i);
                    var tPlane      = t * plane;
                    var edge        = hull.GetEdge(face.Edge);
                    var startOrigin = edge.Origin;

                    do
                    {
                        var nextEdge  = hull.GetEdge(edge.Next);
                        var startVert = math.transform(t, hull.GetVertex(edge.Origin));
                        var endVert   = math.transform(t, hull.GetVertex(nextEdge.Origin));

                        var center = (endVert + startVert) / 2;
                        var dir    = math.normalize(endVert - startVert);

                        var insetDir = math.normalize(math.cross(tPlane.Normal, dir));

                        if ((options & DebugHullFlags.ExplodeFaces) != 0)
                        {
                            DebugDrawer.DrawArrow(center + tPlane.Normal * faceExplosionDistance, dir * 0.2f, Color.black);
                        }
                        else
                        {
                            DebugDrawer.DrawArrow(center + tPlane.Normal * faceExplosionDistance + insetDir * 0.1f, dir * 0.2f, Color.black);
                        }

                        edge = nextEdge;
                    } while (edge.Origin != startOrigin);
                }
            }
        }
Пример #9
0
 public EdgeEnumerator(NativeHull hull, int faceIndex) : this()
 {
     _hull         = hull;
     _offset       = hull.GetFace(faceIndex).Edge;
     _currentIndex = -1;
 }
        public unsafe static void SetFromFaces(ref NativeHull hull, NativeHullDef def)
        {
            Debug.Assert(def.FaceCount > 0);
            Debug.Assert(def.VertexCount > 0);

            hull.VertexCount = def.VertexCount;
            var arr = def.VerticesNative.ToArray();

            hull.VerticesNative = new NativeArrayNoLeakDetection <float3>(arr, Allocator.Persistent);
            hull.Vertices       = (float3 *)hull.VerticesNative.GetUnsafePtr();
            hull.FaceCount      = def.FaceCount;
            hull.FacesNative    = new NativeArrayNoLeakDetection <NativeFace>(hull.FaceCount, Allocator.Persistent);
            hull.Faces          = (NativeFace *)hull.FacesNative.GetUnsafePtr();

            // Initialize all faces by assigning -1 to each edge reference.
            for (int k = 0; k < def.FaceCount; ++k)
            {
                NativeFace *f = hull.Faces + k;
                f->Edge = -1;
            }

            CreateFacesPlanes(ref hull, ref def);

            var edgeMap   = new Dictionary <(int v1, int v2), int>();
            var edgesList = new NativeHalfEdge[10000]; // todo lol

            // Loop through all faces.
            for (int i = 0; i < def.FaceCount; ++i)
            {
                NativeFaceDef face      = def.FacesNative[i];
                int           vertCount = face.VertexCount;

                Debug.Assert(vertCount >= 3);

                int *vertices = face.Vertices;

                var faceHalfEdges = new List <int>();

                // Loop through all face edges.
                for (int j = 0; j < vertCount; ++j)
                {
                    int v1 = vertices[j];
                    int v2 = j + 1 < vertCount ? vertices[j + 1] : vertices[0];

                    bool edgeFound12 = edgeMap.TryGetValue((v1, v2), out int iter12);
                    bool edgeFound21 = edgeMap.ContainsKey((v2, v1));

                    Debug.Assert(edgeFound12 == edgeFound21);

                    if (edgeFound12)
                    {
                        // The edge is shared by two faces.
                        int e12 = iter12;

                        // Link adjacent face to edge.
                        if (edgesList[e12].Face == -1)
                        {
                            edgesList[e12].Face = i;
                        }
                        else
                        {
                            throw new Exception("Two shared edges can't have the same vertices in the same order");
                        }

                        if (hull.Faces[i].Edge == -1)
                        {
                            hull.Faces[i].Edge = e12;
                        }

                        faceHalfEdges.Add(e12);
                    }
                    else
                    {
                        // The next edge of the current half edge in the array is the twin edge.
                        int e12 = hull.EdgeCount++;
                        int e21 = hull.EdgeCount++;

                        if (hull.Faces[i].Edge == -1)
                        {
                            hull.Faces[i].Edge = e12;
                        }

                        faceHalfEdges.Add(e12);

                        edgesList[e12].Prev   = -1;
                        edgesList[e12].Next   = -1;
                        edgesList[e12].Twin   = e21;
                        edgesList[e12].Face   = i;
                        edgesList[e12].Origin = v1;

                        edgesList[e21].Prev   = -1;
                        edgesList[e21].Next   = -1;
                        edgesList[e21].Twin   = e12;
                        edgesList[e21].Face   = -1;
                        edgesList[e21].Origin = v2;

                        // Add edges to map.
                        edgeMap[(v1, v2)] = e12;
        public static unsafe void QueryFaceDistance(out FaceQueryResult result, RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            // Perform computations in the local space of the second hull.
            RigidTransform transform = math.mul(math.inverse(transform2), transform1);

            result.Distance = -float.MaxValue;
            result.Index    = -1;

            for (int i = 0; i < hull1.FaceCount; ++i)
            {
                NativePlane plane    = transform * hull1.GetPlane(i);
                float3      support  = hull2.GetSupport(-plane.Normal);
                float       distance = plane.Distance(support);

                if (distance > result.Distance)
                {
                    result.Distance = distance;
                    result.Index    = i;
                }
            }
        }
        public static bool NativeHullHullContact(ref NativeManifold result, RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            FaceQueryResult faceQuery1;

            HullCollision.QueryFaceDistance(out faceQuery1, transform1, hull1, transform2, hull2);
            if (faceQuery1.Distance > 0)
            {
                return(false);
            }

            FaceQueryResult faceQuery2;

            HullCollision.QueryFaceDistance(out faceQuery2, transform2, hull2, transform1, hull1);
            if (faceQuery2.Distance > 0)
            {
                return(false);
            }

            HullCollision.QueryEdgeDistance(out EdgeQueryResult edgeQuery, transform1, hull1, transform2, hull2);
            if (edgeQuery.Distance > 0)
            {
                return(false);
            }

            float kRelEdgeTolerance = 0.90f; //90%
            float kRelFaceTolerance = 0.95f; //95%
            float kAbsTolerance     = 0.5f * 0.005f;

            // Favor face contacts over edge contacts.
            float maxFaceSeparation = math.max(faceQuery1.Distance, faceQuery2.Distance);

            if (edgeQuery.Distance > kRelEdgeTolerance * maxFaceSeparation + kAbsTolerance)
            {
                CreateEdgeContact(ref result, edgeQuery, transform1, hull1, transform2, hull2);
            }
            else
            {
                // Favor first hull face to avoid face flip-flops.
                if (faceQuery2.Distance > kRelFaceTolerance * faceQuery1.Distance + kAbsTolerance)
                {
                    // 2 = reference, 1 = incident.
                    CreateFaceContact(ref result, faceQuery2, transform2, hull2, transform1, hull1, true);
                }
                else
                {
                    // 1 = reference, 2 = incident.
                    CreateFaceContact(ref result, faceQuery1, transform1, hull1, transform2, hull2, false);
                }
            }

            return(true);
        }
        public unsafe static void CreateFaceContact(ref NativeManifold output, FaceQueryResult input, RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2, bool flipNormal)
        {
            var         refPlane       = hull1.GetPlane(input.Index);
            NativePlane referencePlane = transform1 * refPlane;

            var clippingPlanesStackPtr = stackalloc ClipPlane[hull1.FaceCount];
            var clippingPlanes         = new NativeBuffer <ClipPlane>(clippingPlanesStackPtr, hull1.FaceCount);

            //NativeList<ClipPlane> clippingPlanes = new NativeList<ClipPlane>((int)hull1.FaceCount, Allocator.Temp);

            // Find only the side planes of the reference face
            GetFaceSidePlanes(ref clippingPlanes, referencePlane, input.Index, transform1, hull1);

            var incidentPolygonStackPtr = stackalloc ClipVertex[hull1.FaceCount];
            var incidentPolygon         = new NativeBuffer <ClipVertex>(incidentPolygonStackPtr, hull1.VertexCount);

            var incidentFaceIndex = ComputeIncidentFaceIndex(referencePlane, transform2, hull2);

            ComputeFaceClippingPolygon(ref incidentPolygon, incidentFaceIndex, transform2, hull2);

            //HullDrawingUtility.DrawFaceWithOutline(incidentFaceIndex, transform2, hull2, Color.yellow.ToOpacity(0.3f));

            var outputPolygonStackPtr = stackalloc ClipVertex[hull1.FaceCount];

            // Clip face polygon against the clipping planes.
            for (int i = 0; i < clippingPlanes.Length; ++i)
            {
                var outputPolygon = new NativeBuffer <ClipVertex>(outputPolygonStackPtr, hull1.FaceCount);

                Clip(clippingPlanes[i], ref incidentPolygon, ref outputPolygon);

                if (outputPolygon.Length == 0)
                {
                    return;
                }

                incidentPolygon = outputPolygon;
            }

            // Get all contact points below reference face.
            for (int i = 0; i < incidentPolygon.Length; ++i)
            {
                ClipVertex vertex   = incidentPolygon[i];
                float      distance = referencePlane.Distance(vertex.position);

                if (distance <= 0)
                {
                    // Below reference plane -> position constraint violated.
                    ContactID id = default;
                    id.FeaturePair = vertex.featurePair;

                    if (flipNormal)
                    {
                        output.Normal = -referencePlane.Normal;
                        Swap(id.FeaturePair.InEdge1, id.FeaturePair.InEdge2);
                        Swap(id.FeaturePair.OutEdge1, id.FeaturePair.OutEdge2);
                    }
                    else
                    {
                        output.Normal = referencePlane.Normal;
                    }

                    // Project clipped point onto reference plane.
                    float3 position = referencePlane.ClosestPoint(vertex.position);
                    // Add point and distance to the plane to the manifold.
                    output.Add(position, distance, id);
                }
            }

            //clippingPlanes.Dispose();
            //incidentPolygon.Dispose();
        }
        public static unsafe void CreateEdgeContact(ref NativeManifold output, EdgeQueryResult input, RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            Debug.Assert(output.IsCreated);

            ContactPoint cp = default;

            if (input.Index1 < 0 || input.Index2 < 0)
            {
                return;
            }

            NativeHalfEdge *edge1 = hull1.GetEdgePtr(input.Index1);
            NativeHalfEdge *twin1 = hull1.GetEdgePtr(edge1->Twin);

            float3 P1 = math.transform(transform1, hull1.GetVertex(edge1->Origin));
            float3 Q1 = math.transform(transform1, hull1.GetVertex(twin1->Origin));
            float3 E1 = Q1 - P1;

            NativeHalfEdge *edge2 = hull2.GetEdgePtr(input.Index2);
            NativeHalfEdge *twin2 = hull2.GetEdgePtr(edge2->Twin);

            float3 P2 = math.transform(transform1, hull2.GetVertex(edge2->Origin));
            float3 Q2 = math.transform(transform1, hull2.GetVertex(twin2->Origin));
            float3 E2 = Q2 - P2;

            float3 normal = math.normalize(math.cross(Q1 - P1, Q2 - P2));
            float3 C2C1   = transform2.pos - transform1.pos;

            if (math.dot(normal, C2C1) < 0)
            {
                // Flip
                output.Normal              = -normal;
                cp.Id.FeaturePair.InEdge1  = input.Index2;
                cp.Id.FeaturePair.OutEdge1 = input.Index2 + 1;

                cp.Id.FeaturePair.InEdge2  = input.Index1 + 1;
                cp.Id.FeaturePair.OutEdge2 = input.Index1;
            }
            else
            {
                output.Normal = normal;

                cp.Id.FeaturePair.InEdge1  = input.Index1;
                cp.Id.FeaturePair.OutEdge1 = input.Index1 + 1;

                cp.Id.FeaturePair.InEdge2  = input.Index2 + 1;
                cp.Id.FeaturePair.OutEdge2 = input.Index2;
            }

            // Compute the closest points between the two edges (center point of penetration)
            ClosestPointsSegmentSegment(P1, Q1, P2, Q2, out float3 C1, out float3 C2);

            float3 position = 0.5f * (C1 + C2);

            //// the closest points on each hull
            //cp.positionOnTarget = Math3d.ProjectPointOnLineSegment(P2, Q2, C2);
            //cp.positionOnSource = Math3d.ProjectPointOnLineSegment(P1, Q1, C1);

            cp.Penetration = C1 - C2;
            cp.Position    = position;
            cp.Distance    = input.Distance;

            output.Add(cp);
        }
        /// <summary>
        /// Populates a list with transformed face vertices.
        /// </summary>
        public static unsafe void ComputeFaceClippingPolygon(ref NativeBuffer <ClipVertex> output, int faceIndex, RigidTransform t, NativeHull hull)
        {
            Debug.Assert(output.IsCreated);

            NativeFace *    face    = hull.GetFacePtr(faceIndex);
            NativePlane     plane   = hull.GetPlane(faceIndex);
            NativeHalfEdge *start   = hull.GetEdgePtr(face->Edge);
            NativeHalfEdge *current = start;

            do
            {
                NativeHalfEdge *twin   = hull.GetEdgePtr(current->Twin);
                float3          vertex = hull.GetVertex(current->Origin);
                float3          P      = math.transform(t, vertex);

                ClipVertex clipVertex;
                clipVertex.featurePair.InEdge1  = -1;
                clipVertex.featurePair.OutEdge1 = -1;
                clipVertex.featurePair.InEdge2  = (sbyte)current->Next;
                clipVertex.featurePair.OutEdge2 = (sbyte)twin->Twin;
                clipVertex.position             = P;
                clipVertex.hull2local           = vertex;
                clipVertex.plane = plane;

                output.Add(clipVertex);

                current = hull.GetEdgePtr(current->Next);
            } while (current != start);
        }
        public static unsafe void GetFaceSidePlanes(ref NativeBuffer <ClipPlane> output, NativePlane facePlane, int faceIndex, RigidTransform transform, NativeHull hull)
        {
            NativeHalfEdge *start   = hull.GetEdgePtr(hull.GetFacePtr(faceIndex)->Edge);
            NativeHalfEdge *current = start;

            do
            {
                NativeHalfEdge *twin = hull.GetEdgePtr(current->Twin);
                float3          P    = math.transform(transform, hull.GetVertex(current->Origin));
                float3          Q    = math.transform(transform, hull.GetVertex(twin->Origin));

                ClipPlane clipPlane = default;
                clipPlane.edgeId       = twin->Twin; //edge ID.
                clipPlane.plane.Normal = math.normalize(math.cross(Q - P, facePlane.Normal));
                clipPlane.plane.Offset = math.dot(clipPlane.plane.Normal, P);
                output.Add(clipPlane);

                current = hull.GetEdgePtr(current->Next);
            }while (current != start);
        }
        /// <summary>
        /// Populates a list with transformed face planes
        /// </summary>
        public static unsafe void GetClippingPlanes(ref NativeBuffer <ClipPlane> output, NativePlane facePlane, int faceIndex, RigidTransform transform, NativeHull hull)
        {
            Debug.Assert(output.IsCreated);

            for (int i = 0; i < hull.FaceCount; i++)
            {
                var p = hull.GetPlane(i);
                output.Add(new ClipPlane
                {
                    plane = transform * p,
                });
            }
        }
        /// <summary>
        /// Finds the index to the least parallel face on the other hull.
        /// </summary>
        /// <param name="facePlane"></param>
        /// <param name="transform"></param>
        /// <param name="hull"></param>
        /// <returns></returns>
        public static unsafe int ComputeIncidentFaceIndex(NativePlane facePlane, RigidTransform transform, NativeHull hull)
        {
            int   faceIndex = 0;
            float min       = math.dot(facePlane.Normal, (transform * hull.GetPlane(faceIndex)).Normal);

            for (int i = 1; i < hull.FaceCount; ++i)
            {
                float dot = math.dot(facePlane.Normal, (transform * hull.GetPlane(i)).Normal);
                if (dot < min)
                {
                    min       = dot;
                    faceIndex = i;
                }
            }
            return(faceIndex);
        }
        private static void ClipFace(ref NativeManifold tmp, int i, RigidTransform transform1, NativeHull hull1, RigidTransform transform2, NativeHull hull2)
        {
            NativePlane plane             = transform1 * hull1.GetPlane(i);
            var         incidentFaceIndex = ComputeIncidentFaceIndex(plane, transform2, hull2);

            ClipFaceAgainstAnother(ref tmp, incidentFaceIndex, transform2, hull2, i, transform1, hull1);
        }
        public static int ClipFaceAgainstAnother(ref NativeManifold output, int referenceFaceIndex, RigidTransform transform1, NativeHull hull1, int incidentFaceIndex, RigidTransform transform2, NativeHull hull2)
        {
            Debug.Assert(output.IsCreated);

            var         refPlane       = hull1.GetPlane(referenceFaceIndex);
            NativePlane referencePlane = transform1 * refPlane;

            NativeBuffer <ClipPlane> clippingPlanes = new NativeBuffer <ClipPlane>(hull1.FaceCount, Allocator.Temp);

            // Get every plane on the other polygon
            GetClippingPlanes(ref clippingPlanes, referencePlane, referenceFaceIndex, transform1, hull1);

            // Create face polygon.
            NativeBuffer <ClipVertex> incidentPolygon = new NativeBuffer <ClipVertex>(hull1.VertexCount, Allocator.Temp);

            ComputeFaceClippingPolygon(ref incidentPolygon, incidentFaceIndex, transform2, hull2);

            // Clip face polygon against the clipping planes.
            for (int i = 0; i < clippingPlanes.Length; ++i)
            {
                NativeBuffer <ClipVertex> outputPolygon = new NativeBuffer <ClipVertex>(math.max(hull1.VertexCount, hull2.VertexCount), Allocator.Temp);

                Clip(clippingPlanes[i], ref incidentPolygon, ref outputPolygon);

                if (outputPolygon.Length == 0)
                {
                    return(-1);
                }

                incidentPolygon.Dispose();
                incidentPolygon = outputPolygon;
            }

            for (int i = 0; i < incidentPolygon.Length; ++i)
            {
                ClipVertex vertex   = incidentPolygon[i];
                float      distance = referencePlane.Distance(vertex.position);
                output.Add(vertex.position, distance, new ContactID {
                    FeaturePair = vertex.featurePair
                });
            }

            clippingPlanes.Dispose();
            incidentPolygon.Dispose();

            return(incidentFaceIndex);
        }
        public static unsafe NativeHull CreateFromMesh(Mesh mesh)
        {
            var faces       = new List <DetailedFaceDef>();
            var verts       = mesh.vertices.Select(RoundVertex).ToArray();
            var uniqueVerts = verts.Distinct().ToList();
            var indices     = mesh.triangles;

            // Create faces from Triangles and collapse multiple vertices with same position into shared vertices.
            for (int i = 0; i < mesh.triangles.Length; i = i + 3)
            {
                var idx1 = i;
                var idx2 = i + 1;
                var idx3 = i + 2;

                Vector3 p1 = verts[indices[idx1]];
                Vector3 p2 = verts[indices[idx2]];
                Vector3 p3 = verts[indices[idx3]];

                var normal = math.normalize(math.cross(p3 - p2, p1 - p2));

                // Round normal so that faces with only slight variances can be grouped properly together.
                var roundedNormal = RoundVertex(normal);

                faces.Add(new DetailedFaceDef
                {
                    Center = ((p1 + p2 + p3) / 3),
                    Normal = roundedNormal,
                    Verts  = new List <float3> {
                        p1, p2, p3
                    },
                    Indices = new List <int>
                    {
                        uniqueVerts.IndexOf(p1),
                        uniqueVerts.IndexOf(p2),
                        uniqueVerts.IndexOf(p3)
                    }
                });
            }

            var faceDefs      = new List <NativeFaceDef>();
            var orphanIndices = new HashSet <int>();

            // Merge all faces with the same normal and shared vertex
            var mergedFaces = GroupBySharedVertex(GroupByNormal(faces));

            foreach (var faceGroup in mergedFaces)
            {
                var indicesFromMergedFaces = faceGroup.SelectMany(face => face.Indices).ToArray();

                // Collapse points inside the new combined face by using only the border vertices.
                var border        = PolygonPerimeter.CalculatePerimeter(indicesFromMergedFaces, ref uniqueVerts);
                var borderIndices = border.Select(b => b.EndIndex).ToArray();

                foreach (var idx in indicesFromMergedFaces.Except(borderIndices))
                {
                    orphanIndices.Add(idx);
                }

                var v   = stackalloc int[borderIndices.Length];
                int max = 0;
                for (int i = 0; i < borderIndices.Length; i++)
                {
                    var idx = borderIndices[i];
                    if (idx > max)
                    {
                        max = idx;
                    }
                    v[i] = idx;
                }

                faceDefs.Add(new NativeFaceDef
                {
                    HighestIndex = max,
                    VertexCount  = borderIndices.Length,
                    Vertices     = v,
                });
            }

            // Remove vertices with no edges connected to them and fix all impacted face vertex references.
            foreach (var orphanIdx in orphanIndices.OrderByDescending(i => i))
            {
                uniqueVerts.RemoveAt(orphanIdx);

                foreach (var face in faceDefs.Where(f => f.HighestIndex >= orphanIdx))
                {
                    for (int i = 0; i < face.VertexCount; i++)
                    {
                        var faceVertIdx = face.Vertices[i];
                        if (faceVertIdx >= orphanIdx)
                        {
                            face.Vertices[i] = --faceVertIdx;
                        }
                    }
                }
            }

            var result = new NativeHull();

            using (var faceNative = new NativeArray <NativeFaceDef>(faceDefs.ToArray(), Allocator.Temp))
                using (var vertsNative = new NativeArray <float3>(uniqueVerts.ToArray(), Allocator.Temp))
                {
                    NativeHullDef hullDef;
                    hullDef.VertexCount    = vertsNative.Length;
                    hullDef.VerticesNative = vertsNative;
                    hullDef.FaceCount      = faceNative.Length;
                    hullDef.FacesNative    = faceNative;
                    SetFromFaces(ref result, hullDef);
                }

            result.IsCreated = true;

            HullValidation.ValidateHull(result);

            return(result);
        }
Пример #22
0
 public static void DrawEdge(int i, RigidTransform t1, NativeHull hull1, Color?color = null)
 {
     if (i > 0 && i < hull1.EdgeCount - 1)
     {
         ref var localEdge = ref hull1.GetEdgeRef(i);
         ref var twinEdge  = ref hull1.GetEdgeRef(localEdge.Twin);
        public static unsafe NativeHull CreateBox(float3 scale)
        {
            float3[] cubeVertices =
            {
                new float3(0.5f,   0.5f, -0.5f),
                new float3(-0.5f,  0.5f, -0.5f),
                new float3(-0.5f, -0.5f, -0.5f),
                new float3(0.5f,  -0.5f, -0.5f),
                new float3(0.5f,   0.5f,  0.5f),
                new float3(-0.5f,  0.5f,  0.5f),
                new float3(-0.5f, -0.5f,  0.5f),
                new float3(0.5f,  -0.5f,  0.5f),
            };

            for (int i = 0; i < 8; ++i)
            {
                cubeVertices[i].x *= scale.x;
                cubeVertices[i].y *= scale.y;
                cubeVertices[i].z *= scale.z;
            }

            int *left  = stackalloc int[] { 1, 2, 6, 5 };
            int *right = stackalloc int[] { 4, 7, 3, 0 };
            int *down  = stackalloc int[] { 3, 7, 6, 2 };
            int *up    = stackalloc int[] { 0, 1, 5, 4 };
            int *back  = stackalloc int[] { 4, 5, 6, 7 };
            int *front = stackalloc int[] { 0, 3, 2, 1 };

            NativeFaceDef[] boxFaces =
            {
                new NativeFaceDef {
                    VertexCount = 4, Vertices = left
                },
                new NativeFaceDef {
                    VertexCount = 4, Vertices = right
                },
                new NativeFaceDef {
                    VertexCount = 4, Vertices = down
                },
                new NativeFaceDef {
                    VertexCount = 4, Vertices = up
                },
                new NativeFaceDef {
                    VertexCount = 4, Vertices = back
                },
                new NativeFaceDef {
                    VertexCount = 4, Vertices = front
                },
            };

            var result = new NativeHull();

            using (var boxFacesNative = new NativeArray <NativeFaceDef>(boxFaces, Allocator.Temp))
                using (var cubeVertsNative = new NativeArray <float3>(cubeVertices, Allocator.Temp))
                {
                    NativeHullDef hullDef;
                    hullDef.VertexCount    = 8;
                    hullDef.VerticesNative = cubeVertsNative;
                    hullDef.FaceCount      = 6;
                    hullDef.FacesNative    = boxFacesNative;
                    SetFromFaces(ref result, hullDef);
                }

            result.IsCreated = true;
            return(result);
        }
Пример #24
0
 public EdgeEnumerator(NativeHull hull) : this()
 {
     _hull         = hull;
     _offset       = -1;
     _currentIndex = -1;
 }