private static int GetBestCutPlane(List <Plane> planes, DcelMesh mesh) { // The plane cost is inspired by Stan Melax's convex hull implementation. // It can be found in John Ratcliff's Convex Decomposition library. float bestPlaneCost = 0; int bestPlane = -1; for (int i = 0; i < planes.Count; i++) { var plane = planes[i]; // Get min and max distance. float minDistance = 0; float maxDistance = 0; foreach (var v in mesh.Vertices) { float distance = Vector3.Dot(v.Position, plane.Normal) - plane.DistanceFromOrigin; minDistance = Math.Min(distance, minDistance); maxDistance = Math.Max(distance, maxDistance); } float diff = maxDistance - minDistance; // The plane cost is the normalized maxDistance. float planeCost = maxDistance / diff; if (planeCost > bestPlaneCost) { bestPlaneCost = planeCost; bestPlane = i; } } return(bestPlane); }
/// <summary> /// Initializes a new instance of the <see cref="VertexAdjacency"/> class. /// </summary> /// <param name="mesh">The mesh for which the adjacency information is built.</param> /// <exception cref="NotSupportedException"> /// Too many vertices in convex hull. Max. 65534 vertices in convex hull are supported. /// </exception> public VertexAdjacency(DcelMesh mesh) { if (mesh == null) { throw new ArgumentNullException("mesh"); } int numberOfVertices = mesh.Vertices.Count; if (numberOfVertices >= ushort.MaxValue) { throw new NotSupportedException("Too many vertices in convex hull. Max. 65534 vertices in convex hull are supported."); } Debug.Assert(mesh.Vertices.All(v => v.Tag == 0), "Tags of DcelMesh should be cleared."); // Store the index of each vertex in its tag for fast lookup. for (int i = 0; i < numberOfVertices; i++) { mesh.Vertices[i].Tag = i; } ListIndices = new ushort[numberOfVertices + 1]; List <ushort> adjacencyLists = new List <ushort>(numberOfVertices * 4); for (int i = 0; i < numberOfVertices; i++) { DcelVertex vertex = mesh.Vertices[i]; ListIndices[i] = (ushort)adjacencyLists.Count; // Gather all adjacent vertices. DcelEdge startEdge = vertex.Edge; DcelEdge edge = startEdge; do { DcelVertex adjacentVertex = edge.Next.Origin; int adjacentIndex = adjacentVertex.Tag; Debug.Assert(mesh.Vertices[adjacentIndex] == adjacentVertex, "Indices stored in DcelVertex.Tag are invalid."); adjacencyLists.Add((ushort)adjacentIndex); edge = edge.Twin.Next; } while (edge != startEdge); } // Add one additional entry which determines the end of the last adjacency list. ListIndices[numberOfVertices] = (ushort)adjacencyLists.Count; // Copy adjacency lists into static array. Lists = adjacencyLists.ToArray(); }
public static DcelMesh FromTriangleMesh(TriangleMesh mesh) { // TODO: To optimize, check tricks of TriangleMeshShape.ComputeNeighbors. if (mesh == null) { throw new ArgumentNullException("mesh"); } if (mesh.Vertices == null || mesh.Indices == null) { throw new ArgumentException("Input mesh has no vertices or vertex indices."); } // Create vertices. int numberOfVertices = mesh.Vertices.Count; var vertices = new List <DcelVertex>(numberOfVertices); foreach (var position in mesh.Vertices) { vertices.Add(new DcelVertex(position, null)); } // Weld similar vertices. for (int i = 0; i < numberOfVertices; i++) { for (int j = i + 1; j < numberOfVertices; j++) { if (Vector3.AreNumericallyEqual(vertices[i].Position, vertices[j].Position)) { vertices[i] = vertices[j]; } } } // Create edges and faces for each triangle. // We need at least 3 edges for each triangle. We might need more edges if we have to // connect unconnected islands of triangles. var edges = new List <DcelEdge>(mesh.NumberOfTriangles * 3 * 2); var faces = new List <DcelFace>(mesh.NumberOfTriangles); for (int i = 0; i < mesh.NumberOfTriangles; i++) { // Get triangle indices. var index0 = mesh.Indices[i * 3 + 0]; var index1 = mesh.Indices[i * 3 + 1]; var index2 = mesh.Indices[i * 3 + 2]; // Get DCEL vertices. var vertex0 = vertices[index0]; var vertex1 = vertices[index1]; var vertex2 = vertices[index2]; // Create 3 edges. var edge0 = new DcelEdge(); var edge1 = new DcelEdge(); var edge2 = new DcelEdge(); // Create 1 face. var face = new DcelFace(); // Fill out face info. face.Boundary = edge0; // Fill out vertex info. vertex0.Edge = edge0; vertex1.Edge = edge1; vertex2.Edge = edge2; // Fill out edge info. // Twin links are created later. edge0.Face = face; edge0.Origin = vertex0; edge0.Next = edge1; edge0.Previous = edge2; edge1.Face = face; edge1.Origin = vertex1; edge1.Next = edge2; edge1.Previous = edge0; edge2.Face = face; edge2.Origin = vertex2; edge2.Next = edge0; edge2.Previous = edge1; // Add to lists. edges.Add(edge0); edges.Add(edge1); edges.Add(edge2); faces.Add(face); } // Connect triangles that share an edge. for (int i = 0; i < faces.Count; i++) { // Get face and its 3 edges. var faceI = faces[i]; var edgeI0 = faceI.Boundary; var edgeI1 = edgeI0.Next; var edgeI2 = edgeI1.Next; Debug.Assert(edgeI2.Next == edgeI0); for (int j = i + 1; j < faces.Count; j++) { // Get face and its 3 edges. var faceJ = faces[j]; var edgeJ0 = faceJ.Boundary; var edgeJ1 = edgeJ0.Next; var edgeJ2 = edgeJ1.Next; Debug.Assert(edgeJ2.Next == edgeJ0); TryLink(edgeI0, edgeJ0); TryLink(edgeI0, edgeJ1); TryLink(edgeI0, edgeJ2); TryLink(edgeI1, edgeJ0); TryLink(edgeI1, edgeJ1); TryLink(edgeI1, edgeJ2); TryLink(edgeI2, edgeJ0); TryLink(edgeI2, edgeJ1); TryLink(edgeI2, edgeJ2); } } // If the mesh is not closed, we have to add twin edges at the boundaries foreach (var edge in edges.ToArray()) { if (edge.Twin == null) { var twin = new DcelEdge(); twin.Origin = edge.Next.Origin; twin.Twin = edge; edge.Twin = twin; edges.Add(twin); } } // Yet, twin.Next/Previous were not set. foreach (var edge in edges) { if (edge.Previous == null) { // The previous edge has not been set. // Search the edges around the origin until we find the previous unconnected edge. var origin = edge.Origin; var originEdge = edge.Twin.Next; // Another edge with the same origin. while (originEdge.Twin.Next != null) { Debug.Assert(originEdge.Origin == origin); originEdge = originEdge.Twin.Next; } var previous = originEdge.Twin; previous.Next = edge; edge.Previous = previous; } } // Check if we have one connected mesh. if (vertices.Count > 0) { const int Tag = 1; TagLinkedComponents(vertices[0], Tag); // Check if all components were reached. if (vertices.Any(v => v.Tag != Tag) || edges.Any(e => e.Tag != Tag) || faces.Any(f => f.Tag != Tag)) { throw new NotSupportedException("The triangle mesh consists of several unconnected components or sub-meshes."); } } var dcelMesh = new DcelMesh { Vertex = vertices.FirstOrDefault() }; dcelMesh.ResetTags(); return(dcelMesh); }
/// <summary> /// Initializes a new instance of the <see cref="DcelMesh" /> class that is a copy of the given /// mesh. /// </summary> /// <param name="mesh"> /// The source mesh to copy. Can be <see langword="null"/> to create an empty /// <see cref="DcelMesh"/>. /// </param> /// <remarks> /// This constructor creates a new <see cref="DcelMesh"/> which is a copy of the given /// <paramref name="mesh"/>. /// </remarks> public DcelMesh(DcelMesh mesh) { if (mesh == null) { return; } var originalVertices = mesh.Vertices; var originalEdges = mesh.Edges; var originalFaces = mesh.Faces; // 3 empty lists. _vertices = new List <DcelVertex>(originalVertices.Count); _edges = new List <DcelEdge>(originalEdges.Count); _faces = new List <DcelFace>(originalFaces.Count); // Use the internal tags to store list indices. for (int i = 0; i < originalVertices.Count; i++) { originalVertices[i].InternalTag = i; } for (int i = 0; i < originalEdges.Count; i++) { originalEdges[i].InternalTag = i; } for (int i = 0; i < originalFaces.Count; i++) { originalFaces[i].InternalTag = i; } // Add empty instances to the new lists. for (int i = 0; i < originalVertices.Count; i++) { _vertices.Add(new DcelVertex()); } for (int i = 0; i < originalEdges.Count; i++) { _edges.Add(new DcelEdge()); } for (int i = 0; i < originalFaces.Count; i++) { _faces.Add(new DcelFace()); } // Clone the DCEL parts. for (int i = 0; i < originalVertices.Count; i++) { var s = originalVertices[i]; // source var t = _vertices[i]; // target t.Position = s.Position; if (s.Edge != null) { t.Edge = _edges[s.Edge.InternalTag]; } t.Tag = s.Tag; t.UserData = s.UserData; } for (int i = 0; i < originalEdges.Count; i++) { var s = originalEdges[i]; var t = _edges[i]; if (s.Origin != null) { t.Origin = _vertices[s.Origin.InternalTag]; } if (s.Twin != null) { t.Twin = _edges[s.Twin.InternalTag]; } if (s.Face != null) { t.Face = _faces[s.Face.InternalTag]; } if (s.Next != null) { t.Next = _edges[s.Next.InternalTag]; } if (s.Previous != null) { t.Previous = _edges[s.Previous.InternalTag]; } t.Tag = s.Tag; t.UserData = s.UserData; } for (int i = 0; i < originalFaces.Count; i++) { var s = originalFaces[i]; var t = _faces[i]; if (s.Boundary != null) { t.Boundary = _edges[s.Boundary.InternalTag]; } if (s.Holes != null) { t.Holes = new List <DcelEdge>(s.Holes.Count); for (int j = 0; j < s.Holes.Count; j++) { if (s.Holes[j] != null) { t.Holes.Add(_edges[s.Holes[j].InternalTag]); } } } t.Tag = s.Tag; t.UserData = s.UserData; } Dirty = false; if (mesh.Vertex != null) { Vertex = _vertices[mesh.Vertex.InternalTag]; } mesh.ResetInternalTags(); Debug.Assert(_vertices.Count == mesh._vertices.Count, "Cloning of DcelMesh failed. Clone has different number of vertices."); Debug.Assert(_edges.Count == mesh._edges.Count, "Cloning of DcelMesh failed. Clone has different number of edges."); Debug.Assert(_faces.Count == mesh._faces.Count, "Cloning of DcelMesh failed. Clone has different number of faces."); }