public void Infinite() { GlobalSettings.ValidationLevel = 0xff; var partition = new AabbTree <int> { EnableSelfOverlaps = true, GetAabbForItem = GetAabbForItem }; partition.Add(1); partition.Add(0); partition.Add(2); partition.Add(3); Assert.AreEqual(new Aabb(new Vector3F(float.NegativeInfinity), new Vector3F(float.PositiveInfinity)), partition.Aabb); var overlaps = partition.GetOverlaps().ToArray(); Assert.AreEqual(4, overlaps.Length); Assert.IsTrue(overlaps.Contains(new Pair <int>(0, 1))); Assert.IsTrue(overlaps.Contains(new Pair <int>(0, 2))); Assert.IsTrue(overlaps.Contains(new Pair <int>(0, 3))); Assert.IsTrue(overlaps.Contains(new Pair <int>(1, 2))); }
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)); }
protected override AabbTree<T> Read(ContentReader input, AabbTree<T> existingInstance) { if (existingInstance == null) existingInstance = new AabbTree<T>(); else existingInstance.Clear(); existingInstance.EnableSelfOverlaps = input.ReadBoolean(); existingInstance.BottomUpBuildThreshold = input.ReadInt32(); input.ReadSharedResource<IPairFilter<T>>(filter => existingInstance.Filter = filter); return existingInstance; }
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)); }
public void Clone() { AabbTree<int> partition = new AabbTree<int>(); partition.GetAabbForItem = i => new Aabb(); partition.EnableSelfOverlaps = true; partition.Filter = new DelegatePairFilter<int>(pair => true); partition.Add(0); partition.Add(1); partition.Add(2); partition.Add(3); var clone = partition.Clone(); Assert.NotNull(clone); Assert.AreNotSame(clone, partition); Assert.AreEqual(clone.EnableSelfOverlaps, partition.EnableSelfOverlaps); Assert.AreEqual(clone.Filter, partition.Filter); Assert.AreEqual(0, clone.Count); clone.Add(0); Assert.AreEqual(4, partition.Count); Assert.AreEqual(1, clone.Count); }
public void Clone() { AabbTree <int> partition = new AabbTree <int>(); partition.GetAabbForItem = i => new Aabb(); partition.EnableSelfOverlaps = true; partition.Filter = new DelegatePairFilter <int>(pair => true); partition.Add(0); partition.Add(1); partition.Add(2); partition.Add(3); var clone = partition.Clone(); Assert.NotNull(clone); Assert.AreNotSame(clone, partition); Assert.AreEqual(clone.EnableSelfOverlaps, partition.EnableSelfOverlaps); Assert.AreEqual(clone.Filter, partition.Filter); Assert.AreEqual(0, clone.Count); clone.Add(0); Assert.AreEqual(4, partition.Count); Assert.AreEqual(1, clone.Count); }
public void Infinite() { GlobalSettings.ValidationLevel = 0xff; var partition = new AabbTree<int> { EnableSelfOverlaps = true, GetAabbForItem = GetAabbForItem }; partition.Add(1); partition.Add(0); partition.Add(2); partition.Add(3); Assert.AreEqual(new Aabb(new Vector3F(float.NegativeInfinity), new Vector3F(float.PositiveInfinity)), partition.Aabb); var overlaps = partition.GetOverlaps().ToArray(); Assert.AreEqual(4, overlaps.Length); Assert.IsTrue(overlaps.Contains(new Pair<int>(0, 1))); Assert.IsTrue(overlaps.Contains(new Pair<int>(0, 2))); Assert.IsTrue(overlaps.Contains(new Pair<int>(0, 3))); Assert.IsTrue(overlaps.Contains(new Pair<int>(1, 2))); }
protected override void Write(ContentWriter output, AabbTree <T> value) { output.Write(value.EnableSelfOverlaps); output.Write(value.BottomUpBuildThreshold); output.WriteSharedResource(value.Filter); }
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)); }
/// <summary> /// Compresses an AABB tree. /// </summary> /// <param name="compressedNodes">The list of compressed AABB nodes.</param> /// <param name="uncompressedNode">The root of the uncompressed AABB tree.</param> private void CompressTree(List<Node> compressedNodes, AabbTree<int>.Node uncompressedNode) { if (uncompressedNode.IsLeaf) { // Compress leaf node. Node node = new Node(); node.Item = uncompressedNode.Item; SetAabb(ref node, uncompressedNode.Aabb); compressedNodes.Add(node); } else { // Node is internal node. int currentIndex = compressedNodes.Count; Node node = new Node(); SetAabb(ref node, uncompressedNode.Aabb); compressedNodes.Add(node); // Compress child nodes. CompressTree(compressedNodes, uncompressedNode.LeftChild); CompressTree(compressedNodes, uncompressedNode.RightChild); // Set escape offset. (Escape offset = size of subtree) node.EscapeOffset = compressedNodes.Count - currentIndex; compressedNodes[currentIndex] = node; } }
public static void RayCast(Vector3 from, Vector3 to, AabbTree <RayMarchedShape> .RayCastCallback callback) { s_tree.RayCast(from, to, callback); }
public static void Query(Aabb bounds, AabbTree <RayMarchedShape> .QueryCallbcak callback) { s_tree.Query(bounds, callback); }