//-------------------------------------------------------------- public CDIslandLink(CDIsland islandA, CDIsland islandB, float allowedConcavity, float smallIslandBoost, int vertexLimit, bool sampleVertices, bool sampleCenters) { IslandA = islandA; IslandB = islandB; Aabb aabb = IslandA.Aabb; aabb.Grow(IslandB.Aabb); Aabb = aabb; float aabbExtentLength = aabb.Extent.Length; Concavity = GetConcavity(vertexLimit, sampleVertices, sampleCenters); float alpha = allowedConcavity / (10 * aabbExtentLength); // Other options for alpha that we could evaluate: //float alpha = 0.03f / aabbExtentLength; // Independent from concavity. //alpha = 0.001f; float aspectRatio = GetAspectRatio(); float term1 = Concavity / aabbExtentLength; float term2 = alpha * aspectRatio; // This third term is not in the original paper. // The goal is to encourage the merging of two small islands. Without this factor // it can happen that there are few large islands and in each step a single triangle // is merged into a large island. Resulting in approx. O(n) speed. // It is much faster if small islands merge first to target an O(log n) speed. float term3 = smallIslandBoost * Math.Max(IslandA.Triangles.Length, IslandB.Triangles.Length); DecimationCost = term1 + term2 + term3; }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- public CDIslandLink(CDIsland islandA, CDIsland islandB, float allowedConcavity, float smallIslandBoost, int vertexLimit, bool sampleVertices, bool sampleCenters) { IslandA = islandA; IslandB = islandB; Aabb aabb = IslandA.Aabb; aabb.Grow(IslandB.Aabb); Aabb = aabb; float aabbExtentLength = aabb.Extent.Length; Concavity = GetConcavity(vertexLimit, sampleVertices, sampleCenters); float alpha = allowedConcavity / (10 * aabbExtentLength); // Other options for alpha that we could evaluate: //float alpha = 0.03f / aabbExtentLength; // Independent from concavity. //alpha = 0.001f; float aspectRatio = GetAspectRatio(); float term1 = Concavity / aabbExtentLength; float term2 = alpha * aspectRatio; // This third term is not in the original paper. // The goal is to encourage the merging of two small islands. Without this factor // it can happen that there are few large islands and in each step a single triangle // is merged into a large island. Resulting in approx. O(n) speed. // It is much faster if small islands merge first to target an O(log n) speed. float term3 = smallIslandBoost * Math.Max(IslandA.Triangles.Length, IslandB.Triangles.Length); DecimationCost = term1 + term2 + term3; }
private void MergeIslands() { List <CDIslandLink> newLinks = new List <CDIslandLink>(); List <CDIslandLink> obsoleteLinks = (EnableMultithreading) ? new List <CDIslandLink>() : null; while (_links.Count > 0 && !_cancel) { lock (_syncRoot) { // Find link with lowest decimation cost. CDIslandLink bestLink = _links[0]; int bestLinkIndex = 0; for (int i = 0; i < _links.Count; i++) { var link = _links[i]; if (link.DecimationCost < bestLink.DecimationCost) { bestLink = link; bestLinkIndex = i; } } // Remove the found link. _links.RemoveAt(bestLinkIndex); // Ignore links that have exceeded the concavity limit. if (bestLink.Concavity > AllowedConcavity) { continue; } // The created composite shape is now invalid again. _decomposition = null; // Remove island B _islands.Remove(bestLink.IslandB); // Merge the islands of the best link into island A. foreach (var triangle in bestLink.IslandB.Triangles) { triangle.Island = bestLink.IslandA; } bestLink.IslandA.Triangles = bestLink.IslandA.Triangles.Union(bestLink.IslandB.Triangles).ToArray(); bestLink.IslandA.Aabb = bestLink.Aabb; bestLink.IslandA.Vertices = bestLink.Vertices; bestLink.IslandA.ConvexHullBuilder = bestLink.ConvexHullBuilder; // Remove old links where A and B are involved and add new // links with A. if (!EnableMultithreading) { for (int i = _links.Count - 1; i >= 0; i--) { var link = _links[i]; CDIsland otherIsland = null; if (link.IslandA == bestLink.IslandA || link.IslandA == bestLink.IslandB) { otherIsland = link.IslandB; } else if (link.IslandB == bestLink.IslandA || link.IslandB == bestLink.IslandB) { otherIsland = link.IslandA; } // This link does not link to the merged islands. if (otherIsland == null) { continue; } // Remove link. _links.RemoveAt(i); // If _newLinks already contains a link with otherIsland we are done. bool linkExists = false; foreach (var newLink in newLinks) { if (newLink.IslandA == otherIsland || newLink.IslandB == otherIsland) { linkExists = true; break; } } if (linkExists) { continue; } // Create link between otherIsland and bestLink.IslandA. link = new CDIslandLink(otherIsland, bestLink.IslandA, AllowedConcavity, SmallIslandBoost, IntermediateVertexLimit, SampleTriangleVertices, SampleTriangleCenters); newLinks.Add(link); } } else { // Experimental multithreading hack. // Note: When multithreading is enabled the result is non-deterministic // because the order of the links in the _links list change... Parallel.ForEach(_links, link => { CDIsland otherIsland = null; if (link.IslandA == bestLink.IslandA || link.IslandA == bestLink.IslandB) { otherIsland = link.IslandB; } else if (link.IslandB == bestLink.IslandA || link.IslandB == bestLink.IslandB) { otherIsland = link.IslandA; } // This link does not link to the merged islands. if (otherIsland == null) { return; } // Remove link. lock (obsoleteLinks) obsoleteLinks.Add(link); // If _newLinks already contains a link with otherIsland we are done. lock (newLinks) { foreach (var newLink in newLinks) { if (newLink.IslandA == otherIsland || newLink.IslandB == otherIsland) { return; } } } // Create link between otherIsland and bestLink.IslandA. link = new CDIslandLink(otherIsland, bestLink.IslandA, AllowedConcavity, SmallIslandBoost, IntermediateVertexLimit, SampleTriangleVertices, SampleTriangleCenters); // Add link but only if another thread did not add a similar link. // TODO: Can this happen or can we remove this check. lock (newLinks) { foreach (var newLink in newLinks) { if (newLink.IslandA == otherIsland || newLink.IslandB == otherIsland) { return; } } newLinks.Add(link); } }); foreach (var link in obsoleteLinks) { _links.Remove(link); } obsoleteLinks.Clear(); } // Add new links. _links.AddRange(newLinks); newLinks.Clear(); OnProgressChanged((_mesh.NumberOfTriangles - _islands.Count) * 100 / _mesh.NumberOfTriangles); } } }
private void CreateDualGraph() { var triangles = new List <CDTriangle>(); // Convert to TriangleMesh. var triangleMesh = _mesh as TriangleMesh; if (triangleMesh == null) { triangleMesh = new TriangleMesh(); triangleMesh.Add(_mesh, false); triangleMesh.WeldVertices(); } // Initialize vertex normals. var normals = new Vector3F[triangleMesh.Vertices.Count]; // Vertex normals. var neighborCounts = new int[triangleMesh.Vertices.Count]; // Numbers of triangles that touch each vertex. for (int i = 0; i < triangleMesh.Vertices.Count; i++) { normals[i] = Vector3F.Zero; neighborCounts[i] = 0; } // Go through all triangles. Add the normal to normals and increase the neighborCounts for (int i = 0; i < triangleMesh.NumberOfTriangles; i++) { Triangle triangle = triangleMesh.GetTriangle(i); var normal = triangle.Normal; for (int j = 0; j < 3; j++) { var vertexIndex = triangleMesh.Indices[(i * 3) + j]; normals[vertexIndex] = normals[vertexIndex] + normal; neighborCounts[vertexIndex] = neighborCounts[vertexIndex] + 1; } } // Create triangles. for (int i = 0; i < triangleMesh.NumberOfTriangles; i++) { Triangle triangle = triangleMesh.GetTriangle(i); var cdTriangle = new CDTriangle { Id = i, Vertices = new[] { triangle.Vertex0, triangle.Vertex1, triangle.Vertex2 }, Normal = triangle.Normal, // TODO: Special care for degenerate triangles needed? }; for (int j = 0; j < 3; j++) { var vertexIndex = triangleMesh.Indices[(i * 3) + j]; var normalSum = normals[vertexIndex]; var neighborCount = neighborCounts[vertexIndex]; if (neighborCount > 0) { var normal = normalSum / neighborCount; normal.TryNormalize(); cdTriangle.VertexNormals[j] = normal; } } triangles.Add(cdTriangle); } // Create an island for each triangle. _islands = new List <CDIsland>(triangles.Count); for (int i = 0; i < triangles.Count; i++) { var triangle = triangles[i]; var island = new CDIsland(); island.Id = i; island.Triangles = new[] { triangle }; island.Vertices = triangle.Vertices; island.Aabb = new Aabb(triangle.Vertices[0], triangle.Vertices[0]); island.Aabb.Grow(triangle.Vertices[1]); island.Aabb.Grow(triangle.Vertices[2]); triangle.Island = island; _islands.Add(island); } // Find connectivity (= add neighbor links). for (int i = 0; i < triangles.Count; i++) { var a = triangles[i]; for (int j = i + 1; j < triangles.Count; j++) { var b = triangles[j]; CDTriangle.FindNeighbors(a, b); } } // Create links. _links = new List <CDIslandLink>(); for (int i = 0; i < _islands.Count; i++) { var island = _islands[i]; var triangle = island.Triangles[0]; // Go through all neighbors. // If there is a neighbor, create a link. // To avoid two links per triangle, we create the link only if the id of this triangle // is less than the other island id. for (int j = 0; j < 3; j++) { CDTriangle neighborTriangle = triangle.Neighbors[j]; if (neighborTriangle != null && neighborTriangle.Island.Id > i) { var link = new CDIslandLink(island, neighborTriangle.Island, AllowedConcavity, SmallIslandBoost, IntermediateVertexLimit, SampleTriangleVertices, SampleTriangleCenters); _links.Add(link); } } } // Now, we have a lot of islands with 1 triangle each. }
private void CreateDualGraph() { var triangles = new List<CDTriangle>(); // Convert to TriangleMesh. var triangleMesh = _mesh as TriangleMesh; if (triangleMesh == null) { triangleMesh = new TriangleMesh(); triangleMesh.Add(_mesh, false); triangleMesh.WeldVertices(); } // Initialize vertex normals. var normals = new Vector3F[triangleMesh.Vertices.Count]; // Vertex normals. var neighborCounts = new int[triangleMesh.Vertices.Count]; // Numbers of triangles that touch each vertex. for (int i = 0; i < triangleMesh.Vertices.Count; i++) { normals[i] = Vector3F.Zero; neighborCounts[i] = 0; } // Go through all triangles. Add the normal to normals and increase the neighborCounts for (int i = 0; i < triangleMesh.NumberOfTriangles; i++) { Triangle triangle = triangleMesh.GetTriangle(i); var normal = triangle.Normal; for (int j = 0; j < 3; j++) { var vertexIndex = triangleMesh.Indices[(i * 3) + j]; normals[vertexIndex] = normals[vertexIndex] + normal; neighborCounts[vertexIndex] = neighborCounts[vertexIndex] + 1; } } // Create triangles. for (int i = 0; i < triangleMesh.NumberOfTriangles; i++) { Triangle triangle = triangleMesh.GetTriangle(i); var cdTriangle = new CDTriangle { Id = i, Vertices = new[] { triangle.Vertex0, triangle.Vertex1, triangle.Vertex2 }, Normal = triangle.Normal, // TODO: Special care for degenerate triangles needed? }; for (int j = 0; j < 3; j++) { var vertexIndex = triangleMesh.Indices[(i * 3) + j]; var normalSum = normals[vertexIndex]; var neighborCount = neighborCounts[vertexIndex]; if (neighborCount > 0) { var normal = normalSum / neighborCount; normal.TryNormalize(); cdTriangle.VertexNormals[j] = normal; } } triangles.Add(cdTriangle); } // Create an island for each triangle. _islands = new List<CDIsland>(triangles.Count); for (int i = 0; i < triangles.Count; i++) { var triangle = triangles[i]; var island = new CDIsland(); island.Id = i; island.Triangles = new[] { triangle }; island.Vertices = triangle.Vertices; island.Aabb = new Aabb(triangle.Vertices[0], triangle.Vertices[0]); island.Aabb.Grow(triangle.Vertices[1]); island.Aabb.Grow(triangle.Vertices[2]); triangle.Island = island; _islands.Add(island); } // Find connectivity (= add neighbor links). for (int i = 0; i < triangles.Count; i++) { var a = triangles[i]; for (int j = i + 1; j < triangles.Count; j++) { var b = triangles[j]; CDTriangle.FindNeighbors(a, b); } } // Create links. _links = new List<CDIslandLink>(); for (int i = 0; i < _islands.Count; i++) { var island = _islands[i]; var triangle = island.Triangles[0]; // Go through all neighbors. // If there is a neighbor, create a link. // To avoid two links per triangle, we create the link only if the id of this triangle // is less than the other island id. for (int j = 0; j < 3; j++) { CDTriangle neighborTriangle = triangle.Neighbors[j]; if (neighborTriangle != null && neighborTriangle.Island.Id > i) { var link = new CDIslandLink(island, neighborTriangle.Island, AllowedConcavity, SmallIslandBoost, IntermediateVertexLimit, SampleTriangleVertices, SampleTriangleCenters); _links.Add(link); } } } // Now, we have a lot of islands with 1 triangle each. }