public void NaNWithValidation() { GlobalSettings.ValidationLevel = 0xff; var partition = new AabbTree <int>(); partition.EnableSelfOverlaps = true; partition.GetAabbForItem = GetAabbForItem; partition.Add(1); partition.Add(4); partition.Add(2); partition.Add(3); // Full rebuild. Assert.Throws <GeometryException>(() => partition.Update(true)); partition = new AabbTree <int>(); partition.EnableSelfOverlaps = true; partition.GetAabbForItem = GetAabbForItem; partition.Add(1); partition.Add(2); partition.Add(3); partition.Update(true); partition.Add(4); // Partial rebuild. Assert.Throws <GeometryException>(() => partition.Update(false)); }
public void NaN() { GlobalSettings.ValidationLevel = 0x00; var partition = new AabbTree <int> { EnableSelfOverlaps = true, GetAabbForItem = GetAabbForItem }; partition.Add(1); partition.Add(4); partition.Add(2); partition.Add(3); // Aabb builder throws exception. Assert.Throws <GeometryException>(() => partition.Update(false)); }
public void NaN() { GlobalSettings.ValidationLevel = 0x00; var partition = new AabbTree<int> { EnableSelfOverlaps = true, GetAabbForItem = GetAabbForItem }; partition.Add(1); partition.Add(4); partition.Add(2); partition.Add(3); // Aabb builder throws exception. Assert.Throws<GeometryException>(() => partition.Update(false)); }
private float GetConcavity(int vertexLimit, bool sampleVertices, bool sampleCenters) { // Initially we assume that the new vertices are simply the union of the islands' vertices. Vertices = IslandA.Vertices.Union(IslandB.Vertices).ToArray(); try { // Create hull mesh. // Incremental hull building. // Note: Commented out because this building the hull this way is less stable. //bool rebuild = true; //try //{ // if (IslandA.ConvexHullBuilder != null) // { // if (IslandB.ConvexHullBuilder == null || IslandA.Vertices.Length > IslandB.Vertices.Length) // { // ConvexHullBuilder = IslandA.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandB.Vertices, vertexLimit, 0); // } // else // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // } // rebuild = false; // } // else if (IslandB.ConvexHullBuilder != null) // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // rebuild = false; // } //} //catch (GeometryException) //{ // rebuild = true; //} //if (rebuild) { try { ConvexHullBuilder = new ConvexHullBuilder(); ConvexHullBuilder.Grow(Vertices, vertexLimit, 0); } catch (GeometryException) { // Hull building failed. Try again with a randomized order. var random = new Random(1234567); // Fisher-Yates shuffle: for (int i = Vertices.Length - 1; i >= 1; i--) { var v = Vertices[i]; var j = random.NextInteger(0, i); Vertices[i] = Vertices[j]; Vertices[j] = v; } } } var hullMesh = ConvexHullBuilder.Mesh.ToTriangleMesh(); // Now, we have a reduced set of vertices. Vertices = hullMesh.Vertices.ToArray(); // For larger meshes we create an AabbTree as acceleration structure. AabbTree<int> partition = null; if (hullMesh.NumberOfTriangles > 12) { partition = new AabbTree<int> { GetAabbForItem = i => hullMesh.GetTriangle(i).Aabb, BottomUpBuildThreshold = 0, }; for (int i = 0; i < hullMesh.NumberOfTriangles; i++) partition.Add(i); partition.Update(true); } Aabb aabb = Aabb; float aabbExtent = aabb.Extent.Length; // Note: For a speed-up we could skip some ray tests in the next loop and only sample // a few vertices if there would be a lot of tests. // The next loop performs ray casts against the hull mesh to determine the maximum // concavity. We ensure that we make only one ray cast per vertex even if a vertex // is shared by many triangles. float maxConcavity = 0; foreach (var triangle in IslandA.Triangles.Union(IslandB.Triangles)) { if (sampleVertices) { for (int i = 0; i < triangle.Vertices.Length; i++) { // Each vertex can be shared by several triangles of the current islands. // Therefore, we check the edges that contain this vertex. If an edge neighbor // is in the same island, we make sure that the vertex concavity is computed only once // in the triangle with the smallest Id. var neighbor0 = triangle.Neighbors[(i + 1) % 3]; var neighbor1 = triangle.Neighbors[(i + 2) % 3]; if (neighbor0 != null && (neighbor0.Island == IslandA || neighbor0.Island == IslandB) && triangle.Id > neighbor0.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } if (neighbor1 != null && (neighbor1.Island == IslandA || neighbor1.Island == IslandB) && triangle.Id > neighbor1.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } var position = triangle.Vertices[i]; var normal = triangle.VertexNormals[i]; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) continue; // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = position + normal * aabbExtent; float rayLength = (position - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABB tree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABB tree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } if (sampleCenters) { // Test: Also shoot from the triangle centers. var center = (triangle.Vertices[0] + triangle.Vertices[1] + triangle.Vertices[2]) / 3; var normal = triangle.Normal; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) continue; // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = center + normal * aabbExtent; float rayLength = (center - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABBTree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABBTree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } return maxConcavity; } catch (GeometryException) { // Ouch, the convex hull generation failed. This can happen for degenerate inputs // and numerical problems in the convex hull builder. ConvexHullBuilder = null; return 0; } }
private float GetConcavity(int vertexLimit, bool sampleVertices, bool sampleCenters) { // Initially we assume that the new vertices are simply the union of the islands' vertices. Vertices = IslandA.Vertices.Union(IslandB.Vertices).ToArray(); try { // Create hull mesh. // Incremental hull building. // Note: Commented out because this building the hull this way is less stable. //bool rebuild = true; //try //{ // if (IslandA.ConvexHullBuilder != null) // { // if (IslandB.ConvexHullBuilder == null || IslandA.Vertices.Length > IslandB.Vertices.Length) // { // ConvexHullBuilder = IslandA.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandB.Vertices, vertexLimit, 0); // } // else // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // } // rebuild = false; // } // else if (IslandB.ConvexHullBuilder != null) // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // rebuild = false; // } //} //catch (GeometryException) //{ // rebuild = true; //} //if (rebuild) { try { ConvexHullBuilder = new ConvexHullBuilder(); ConvexHullBuilder.Grow(Vertices, vertexLimit, 0); } catch (GeometryException) { // Hull building failed. Try again with a randomized order. var random = new Random(1234567); // Fisher-Yates shuffle: for (int i = Vertices.Length - 1; i >= 1; i--) { var v = Vertices[i]; var j = random.NextInteger(0, i); Vertices[i] = Vertices[j]; Vertices[j] = v; } } } var hullMesh = ConvexHullBuilder.Mesh.ToTriangleMesh(); // Now, we have a reduced set of vertices. Vertices = hullMesh.Vertices.ToArray(); // For larger meshes we create an AabbTree as acceleration structure. AabbTree <int> partition = null; if (hullMesh.NumberOfTriangles > 12) { partition = new AabbTree <int> { GetAabbForItem = i => hullMesh.GetTriangle(i).Aabb, BottomUpBuildThreshold = 0, }; for (int i = 0; i < hullMesh.NumberOfTriangles; i++) { partition.Add(i); } partition.Update(true); } Aabb aabb = Aabb; float aabbExtent = aabb.Extent.Length; // Note: For a speed-up we could skip some ray tests in the next loop and only sample // a few vertices if there would be a lot of tests. // The next loop performs ray casts against the hull mesh to determine the maximum // concavity. We ensure that we make only one ray cast per vertex even if a vertex // is shared by many triangles. float maxConcavity = 0; foreach (var triangle in IslandA.Triangles.Union(IslandB.Triangles)) { if (sampleVertices) { for (int i = 0; i < triangle.Vertices.Length; i++) { // Each vertex can be shared by several triangles of the current islands. // Therefore, we check the edges that contain this vertex. If an edge neighbor // is in the same island, we make sure that the vertex concavity is computed only once // in the triangle with the smallest Id. var neighbor0 = triangle.Neighbors[(i + 1) % 3]; var neighbor1 = triangle.Neighbors[(i + 2) % 3]; if (neighbor0 != null && (neighbor0.Island == IslandA || neighbor0.Island == IslandB) && triangle.Id > neighbor0.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } if (neighbor1 != null && (neighbor1.Island == IslandA || neighbor1.Island == IslandB) && triangle.Id > neighbor1.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } var position = triangle.Vertices[i]; var normal = triangle.VertexNormals[i]; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) { continue; } // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = position + normal * aabbExtent; float rayLength = (position - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABB tree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABB tree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } if (sampleCenters) { // Test: Also shoot from the triangle centers. var center = (triangle.Vertices[0] + triangle.Vertices[1] + triangle.Vertices[2]) / 3; var normal = triangle.Normal; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) { continue; } // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = center + normal * aabbExtent; float rayLength = (center - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABBTree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABBTree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } return(maxConcavity); } catch (GeometryException) { // Ouch, the convex hull generation failed. This can happen for degenerate inputs // and numerical problems in the convex hull builder. ConvexHullBuilder = null; return(0); } }
public void NaNWithValidation() { GlobalSettings.ValidationLevel = 0xff; var partition = new AabbTree<int>(); partition.EnableSelfOverlaps = true; partition.GetAabbForItem = GetAabbForItem; partition.Add(1); partition.Add(4); partition.Add(2); partition.Add(3); // Full rebuild. Assert.Throws<GeometryException>(() => partition.Update(true)); partition = new AabbTree<int>(); partition.EnableSelfOverlaps = true; partition.GetAabbForItem = GetAabbForItem; partition.Add(1); partition.Add(2); partition.Add(3); partition.Update(true); partition.Add(4); // Partial rebuild. Assert.Throws<GeometryException>(() => partition.Update(false)); }