diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs index fa68cd9cb1..4d30b2e36b 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using Stride.Core; +using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Core.Serialization; @@ -19,15 +21,15 @@ public class BowyerWatsonTetrahedralization // TODO: Make this customizable public const float ExtrapolationDistance = 100.0f; - private readonly List badTetrahedra = []; - private readonly List holeFaces = []; - private readonly List edges = []; - private readonly List freeTetrahedra = []; + private readonly List badTetrahedra = new List(); + private readonly FastList holeFaces = new FastList(); + private readonly List edges = new List(); + private readonly List freeTetrahedra = new List(); private readonly Predicates predicates = new(); private Vector3[] vertices; - private List tetrahedralization; + private FastList tetrahedralization; public struct Result { @@ -36,8 +38,8 @@ public struct Result /// Any vertex in after this index are added automatically for boundaries. /// public int UserVertexCount; - public List Tetrahedra; - public List Faces; + public FastList Tetrahedra; + public FastList Faces; } [DataSerializer(typeof(Face.Serializer))] @@ -110,7 +112,7 @@ public Result Compute(IReadOnlyList vertices) // TODO: Another approach would be to receive a IList/Array directly and have a method GetVertexAtIndex(i) with special care for i in [vertices.Length; vertices.Length + 4[ range. this.vertices = vertices.Concat(new Vector3[4]).ToArray(); - tetrahedralization = []; + tetrahedralization = new FastList(); freeTetrahedra.Clear(); // Create super-tetrahedra that encompass everything @@ -149,52 +151,52 @@ private unsafe void GenerateExtrapolationProbes() var outerVertexNormals = new Vector3[vertices.Length]; var extraVertices = new List(); - - var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Sum normals on outer vertices - for (int index = 0; index < tetrahedralizationSpan.Length; index++) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - var currentTetrahedron = tetrahedralizationSpan[index]; - - var superTetrahedronVertexCount = 0; - var superTetrahedronVertexIndex = -1; - for (int i = 0; i < 4; ++i) + for (int index = 0; index < tetrahedralization.Count; index++) { - if (currentTetrahedron.Vertices[i] >= vertices.Length - 4) + var currentTetrahedron = &tetrahedra[index]; + + var superTetrahedronVertexCount = 0; + var superTetrahedronVertexIndex = -1; + for (int i = 0; i < 4; ++i) { - superTetrahedronVertexCount++; - superTetrahedronVertexIndex = i; + if (currentTetrahedron->Vertices[i] >= vertices.Length - 4) + { + superTetrahedronVertexCount++; + superTetrahedronVertexIndex = i; + } } - } - // One vertex at infinity, 3 inside (face) - if (superTetrahedronVertexCount == 1) - { - var vertex0 = currentTetrahedron.Vertices[(superTetrahedronVertexIndex + 1) % 4]; - var vertex1 = currentTetrahedron.Vertices[(superTetrahedronVertexIndex + 2) % 4]; - var vertex2 = currentTetrahedron.Vertices[(superTetrahedronVertexIndex + 3) % 4]; - var superTetrahedronVertex = currentTetrahedron.Vertices[superTetrahedronVertexIndex]; - - // Compute normal - Vector3 edge1, edge2, faceNormal; - Vector3.Subtract(ref vertices[vertex1], ref vertices[vertex0], out edge1); - Vector3.Subtract(ref vertices[vertex2], ref vertices[vertex0], out edge2); - Vector3.Cross(ref edge1, ref edge2, out faceNormal); - faceNormal.Normalize(); - - // Reverse it if not facing proper direction (extraVertex should be on the positive side) - var plane = new Plane(vertices[vertex0], faceNormal); - if (CollisionHelper.DistancePlanePoint(ref plane, ref vertices[superTetrahedronVertex]) < 0.0f) - faceNormal = -faceNormal; + // One vertex at infinity, 3 inside (face) + if (superTetrahedronVertexCount == 1) + { + var vertex0 = currentTetrahedron->Vertices[(superTetrahedronVertexIndex + 1) % 4]; + var vertex1 = currentTetrahedron->Vertices[(superTetrahedronVertexIndex + 2) % 4]; + var vertex2 = currentTetrahedron->Vertices[(superTetrahedronVertexIndex + 3) % 4]; + var superTetrahedronVertex = currentTetrahedron->Vertices[superTetrahedronVertexIndex]; + + // Compute normal + Vector3 edge1, edge2, faceNormal; + Vector3.Subtract(ref vertices[vertex1], ref vertices[vertex0], out edge1); + Vector3.Subtract(ref vertices[vertex2], ref vertices[vertex0], out edge2); + Vector3.Cross(ref edge1, ref edge2, out faceNormal); + faceNormal.Normalize(); + + // Reverse it if not facing proper direction (extraVertex should be on the positive side) + var plane = new Plane(vertices[vertex0], faceNormal); + if (CollisionHelper.DistancePlanePoint(ref plane, ref vertices[superTetrahedronVertex]) < 0.0f) + faceNormal = -faceNormal; // Sum normal to 3 vertices - outerVertexNormals[vertex0] += faceNormal; - outerVertexNormals[vertex1] += faceNormal; - outerVertexNormals[vertex2] += faceNormal; + outerVertexNormals[vertex0] += faceNormal; + outerVertexNormals[vertex1] += faceNormal; + outerVertexNormals[vertex2] += faceNormal; + } } } - // Normalize and generate extrapolated probe for (int i = 0; i < outerVertexNormals.Length; ++i) @@ -219,74 +221,70 @@ private unsafe void GenerateExtrapolationProbes() /// /// Generate faces info and normal. /// - private unsafe List GenerateFaces() + private unsafe FastList GenerateFaces() { - var faces = new List(); + var faces = new FastList(); var currentFace = new Face(); - var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - var facesSpan = CollectionsMarshal.AsSpan(faces); - for (int index = 0; index < tetrahedralizationSpan.Length; index++) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - var currentTetrahedron = tetrahedralizationSpan[index]; - - // Process each face - for (int i = 0; i < 4; ++i) + for (int index = 0; index < tetrahedralization.Count; index++) { - var neighbourTetrahedronIndex = currentTetrahedron.Neighbours[i]; - - // If no neighbour, it means there is no face - if (neighbourTetrahedronIndex == -1) - continue; + var currentTetrahedron = &tetrahedra[index]; - // Check if face is already created in neighbour tetrahedron - // If index is lower, it means it already exists (processed before) - if (neighbourTetrahedronIndex < index) + // Process each face + for (int i = 0; i < 4; ++i) { - // Find which face are we in the neighbour tetrahedron - var neighbourTetrahedron = tetrahedralizationSpan[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + var neighbourTetrahedronIndex = currentTetrahedron->Neighbours[i]; + + // If no neighbour, it means there is no face + if (neighbourTetrahedronIndex == -1) + continue; + + // Check if face is already created in neighbour tetrahedron + // If index is lower, it means it already exists (processed before) + if (neighbourTetrahedronIndex < index) { - if (neighbourTetrahedron.Neighbours[j] == index) + // Find which face are we in the neighbour tetrahedron + var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - // We store the bitwise complement since normal is opposite - var oppositeFaceIndex = neighbourTetrahedron.Faces[j]; - - ref var currentTetrahedronRef = ref tetrahedralizationSpan[index]; - currentTetrahedronRef.Faces[i] = ~oppositeFaceIndex; - - ref var oppositeFaceRef = ref facesSpan[oppositeFaceIndex]; - oppositeFaceRef.BackTetrahedron = index; - oppositeFaceRef.BackFace = (sbyte)i; - break; + if (neighbourTetrahedron->Neighbours[j] == index) + { + // We store the bitwise complement since normal is opposite + var oppositeFaceIndex = neighbourTetrahedron->Faces[j]; + currentTetrahedron->Faces[i] = ~oppositeFaceIndex; + faces.Items[oppositeFaceIndex].BackTetrahedron = index; + faces.Items[oppositeFaceIndex].BackFace = (sbyte)i; + break; + } } } - } - else - { - // New face, let's create it - currentTetrahedron.Faces[i] = faces.Count; - - // Create face - currentFace.FrontTetrahedron = index; - currentFace.FrontFace = (sbyte)i; - currentFace.BackTetrahedron = -1; - currentFace.BackFace = -1; - currentFace.Vertices[0] = currentTetrahedron.Vertices[(i + 1) % 4]; // 1 2 3 0 - currentFace.Vertices[1] = currentTetrahedron.Vertices[3 - (i / 2) * 2]; // 3 3 1 1 - currentFace.Vertices[2] = currentTetrahedron.Vertices[(((i + 3) / 2) * 2) % 4]; // 2 0 0 2 - - // Compute normal - Vector3 edge1, edge2, faceNormal; - Vector3.Subtract(ref vertices[currentFace.Vertices[1]], ref vertices[currentFace.Vertices[0]], out edge1); - Vector3.Subtract(ref vertices[currentFace.Vertices[2]], ref vertices[currentFace.Vertices[0]], out edge2); - Vector3.Cross(ref edge1, ref edge2, out faceNormal); - faceNormal.Normalize(); - - currentFace.Normal = faceNormal; - - faces.Add(currentFace); - facesSpan = CollectionsMarshal.AsSpan(faces); + else + { + // New face, let's create it + currentTetrahedron->Faces[i] = faces.Count; + + // Create face + currentFace.FrontTetrahedron = index; + currentFace.FrontFace = (sbyte)i; + currentFace.BackTetrahedron = -1; + currentFace.BackFace = -1; + currentFace.Vertices[0] = currentTetrahedron->Vertices[(i + 1) % 4]; // 1 2 3 0 + currentFace.Vertices[1] = currentTetrahedron->Vertices[3 - (i / 2) * 2]; // 3 3 1 1 + currentFace.Vertices[2] = currentTetrahedron->Vertices[(((i + 3) / 2) * 2) % 4]; // 2 0 0 2 + + // Compute normal + Vector3 edge1, edge2, faceNormal; + Vector3.Subtract(ref vertices[currentFace.Vertices[1]], ref vertices[currentFace.Vertices[0]], out edge1); + Vector3.Subtract(ref vertices[currentFace.Vertices[2]], ref vertices[currentFace.Vertices[0]], out edge2); + Vector3.Cross(ref edge1, ref edge2, out faceNormal); + faceNormal.Normalize(); + + currentFace.Normal = faceNormal; + + faces.Add(currentFace); + } } } } @@ -298,51 +296,49 @@ private unsafe List GenerateFaces() /// private unsafe void CleanupUnusedTetrahedra() { - var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - - for (int index = 0; index < tetrahedralizationSpan.Length; index++) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) + for (int index = 0; index < tetrahedralization.Count; index++) { - // This is an unused tetrahedra, let's remove it - ref var currentTetrahedron = ref tetrahedralizationSpan[index]; - - // Swap-remove with latest tetrahedra (prevents RemoveAt shifting, only one tetrahedra is moving and needs its neighbour references updated) - int lastIndex = tetrahedralization.Count - 1; - if (index < lastIndex) + if (!IsTetrahedronAllocated(index)) { - if (IsTetrahedronAllocated(lastIndex, tetrahedralizationSpan)) - { - currentTetrahedron = tetrahedralizationSpan[lastIndex]; + // This is an unused tetrahedra, let's remove it + var currentTetrahedron = &tetrahedra[index]; - // We moved an allocated tretrahedra, we need to update neighbour indices pointing to this tetrahedra - // (neighbours pointing to lastIndex should now point to index) - for (int i = 0; i < 4; ++i) + // Swap-remove with latest tetrahedra (prevents RemoveAt shifting, only one tetrahedra is moving and needs its neighbour references updated) + int lastIndex = tetrahedralization.Count - 1; + if (index < lastIndex) + { + if (IsTetrahedronAllocated(lastIndex)) { - var neighbourTetrahedronIndex = currentTetrahedron.Neighbours[i]; - if (neighbourTetrahedronIndex == -1) - continue; + *currentTetrahedron = tetrahedra[lastIndex]; - ref var neighbourTetrahedron = ref tetrahedralizationSpan[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + // We moved an allocated tretrahedra, we need to update neighbour indices pointing to this tetrahedra + // (neighbours pointing to lastIndex should now point to index) + for (int i = 0; i < 4; ++i) { - if (neighbourTetrahedron.Neighbours[j] == lastIndex) - neighbourTetrahedron.Neighbours[j] = index; + var neighbourTetrahedronIndex = currentTetrahedron->Neighbours[i]; + if (neighbourTetrahedronIndex == -1) + continue; + + var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) + { + if (neighbourTetrahedron->Neighbours[j] == lastIndex) + neighbourTetrahedron->Neighbours[j] = index; + } } } + else + { + // Current tetrahedra is still not allocated. Go one step backward, so that next loop will also remove it. + index--; + } } - else - { - // Current tetrahedra is still not allocated. Go one step backward, so that next loop will also remove it. - index--; - } - } - tetrahedralization.RemoveAt(lastIndex); - // Reduce length of the span by one given that we just removed an item from the list the span originates from - tetrahedralizationSpan = tetrahedralizationSpan[..^1]; + tetrahedralization.RemoveAt(lastIndex); + } } - // We can clear the free list as well freeTetrahedra.Clear(); @@ -354,39 +350,41 @@ private unsafe void CleanupUnusedTetrahedra() /// private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) { - var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - for (int index = 0; index < tetrahedralizationSpan.Length; index++) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - continue; + for (int index = 0; index < tetrahedralization.Count; index++) + { + if (!IsTetrahedronAllocated(index)) + continue; - var tetrahedron = tetrahedralizationSpan[index]; + var tetrahedron = &tetrahedra[index]; - // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) - for (int i = 0; i < 4; ++i) - { - if (tetrahedron.Vertices[i] >= startVertex && tetrahedron.Vertices[i] < endVertex) + // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) + for (int i = 0; i < 4; ++i) { - FreeTetrahedron(index, tetrahedralizationSpan); - break; + if (tetrahedron->Vertices[i] >= startVertex && tetrahedron->Vertices[i] < endVertex) + { + FreeTetrahedron(index); + break; + } } } - } - // Remove invalid neighbour (pointing to nodes that were just deleted) - for (int index = 0; index < tetrahedralizationSpan.Length; index++) - { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - continue; + // Remove invalid neighbour (pointing to nodes that were just deleted) + for (int index = 0; index < tetrahedralization.Count; index++) + { + if (!IsTetrahedronAllocated(index)) + continue; - ref var tetrahedron = ref tetrahedralizationSpan[index]; + var tetrahedron = &tetrahedra[index]; - // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) - for (int i = 0; i < 4; ++i) - { - var neighbour = tetrahedron.Neighbours[i]; - if (neighbour != -1 && !IsTetrahedronAllocated(neighbour, tetrahedralizationSpan)) - tetrahedron.Neighbours[i] = -1; + // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) + for (int i = 0; i < 4; ++i) + { + var neighbour = tetrahedron->Neighbours[i]; + if (neighbour != -1 && !IsTetrahedronAllocated(neighbour)) + tetrahedron->Neighbours[i] = -1; + } } } } @@ -448,94 +446,96 @@ private unsafe void AddVertex(int vertexIndex) edges.Clear(); var vertex = vertices[vertexIndex]; - var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - - // First, find all the triangles that are no longer valid due to the insertion - // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)" - for (int index = 0; index < tetrahedralizationSpan.Length; index++) - { - if (IsTetrahedronAllocated(index, tetrahedralizationSpan) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedralizationSpan[index])) - badTetrahedra.Add(index); - } - // Find the boundary of the polygonal hole - foreach (var tetrahedronIndex in badTetrahedra) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - var tetrahedron = tetrahedralizationSpan[tetrahedronIndex]; - for (int i = 0; i < 4; ++i) + // First, find all the triangles that are no longer valid due to the insertion + // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)" + for (int index = 0; index < tetrahedralization.Count; index++) { - // If edge is not shared by any other bad tetrahedra, it means it's a boundary of our polygonal hole - var neighbourTetrahedronIndex = tetrahedron.Neighbours[i]; - if (badTetrahedra.BinarySearch(neighbourTetrahedronIndex) < 0) + if (IsTetrahedronAllocated(index) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedra[index])) + badTetrahedra.Add(index); + } + + // Find the boundary of the polygonal hole + foreach (var tetrahedronIndex in badTetrahedra) + { + var tetrahedron = &tetrahedra[tetrahedronIndex]; + for (int i = 0; i < 4; ++i) { - var neighbourTetrahedronSelfIndex = -1; - if (neighbourTetrahedronIndex != -1) + // If edge is not shared by any other bad tetrahedra, it means it's a boundary of our polygonal hole + var neighbourTetrahedronIndex = tetrahedron->Neighbours[i]; + if (badTetrahedra.BinarySearch(neighbourTetrahedronIndex) < 0) { - // Find the neighbour index of current tetrahedra in neighbourTetrahedra - ref var neighbourTetrahedron = ref tetrahedralizationSpan[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + var neighbourTetrahedronSelfIndex = -1; + if (neighbourTetrahedronIndex != -1) { - if (neighbourTetrahedron.Neighbours[j] == tetrahedronIndex) + // Find the neighbour index of current tetrahedra in neighbourTetrahedra + var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - neighbourTetrahedronSelfIndex = j; - break; + if (neighbourTetrahedron->Neighbours[j] == tetrahedronIndex) + { + neighbourTetrahedronSelfIndex = j; + break; + } } + if (neighbourTetrahedronSelfIndex == -1) + throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); } - if (neighbourTetrahedronSelfIndex == -1) - throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); - } - // Store edges information (to easily reconstruct neighbour after) - var vertex0 = tetrahedron.Vertices[(i + 1) % 4]; - var vertex1 = tetrahedron.Vertices[(i + 2) % 4]; - var vertex2 = tetrahedron.Vertices[(i + 3) % 4]; + // Store edges information (to easily reconstruct neighbour after) + var vertex0 = tetrahedron->Vertices[(i + 1) % 4]; + var vertex1 = tetrahedron->Vertices[(i + 2) % 4]; + var vertex2 = tetrahedron->Vertices[(i + 3) % 4]; - // If new vertex is at an odd position, it means that newly constructed tetrahedron would have a negative order, let's swap 2 vertices - //if (!IsTetrahedraPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) - if (i % 2 == 1) - { - var vertexTemp = vertex1; - vertex1 = vertex2; - vertex2 = vertexTemp; - } + // If new vertex is at an odd position, it means that newly constructed tetrahedron would have a negative order, let's swap 2 vertices + //if (!IsTetrahedraPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) + if (i % 2 == 1) + { + var vertexTemp = vertex1; + vertex1 = vertex2; + vertex2 = vertexTemp; + } - if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) - throw new InvalidOperationException(); + if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) + throw new InvalidOperationException(); - holeFaces.Add(new HoleFace(vertex0, vertex1, vertex2, neighbourTetrahedronIndex, neighbourTetrahedronSelfIndex)); + holeFaces.Add(new HoleFace(vertex0, vertex1, vertex2, neighbourTetrahedronIndex, neighbourTetrahedronSelfIndex)); + } } } - } - // Remove bad tetrahedra (by marking them invalid) - foreach (var tetrahedronIndex in badTetrahedra) - { - FreeTetrahedron(tetrahedronIndex, tetrahedralizationSpan); + // Remove bad tetrahedra (by marking them invalid) + foreach (var tetrahedronIndex in badTetrahedra) + { + FreeTetrahedron(tetrahedronIndex); + } } - var holeFacesSpan = CollectionsMarshal.AsSpan(holeFaces); // Allocate tetrahedron, and build edge list - for (int index = 0; index < holeFaces.Count; index++) + fixed (HoleFace* facesPointer = holeFaces.Items) { - ref var face = ref holeFacesSpan[index]; + for (int index = 0; index < holeFaces.Count; index++) + { + var face = &facesPointer[index]; - var tetrahedronIndex = AllocateTetrahedron(); - face.Tetrahedron = tetrahedronIndex; + var tetrahedronIndex = AllocateTetrahedron(); + face->Tetrahedron = tetrahedronIndex; - if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[face.Vertex0], ref vertices[face.Vertex1], ref vertices[face.Vertex2])) - throw new InvalidOperationException(); + if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[face->Vertex0], ref vertices[face->Vertex1], ref vertices[face->Vertex2])) + throw new InvalidOperationException(); - // Note: we use opposite direction for half-edge - edges.Add(new HoleEdge(face.Vertex0, face.Vertex1, tetrahedronIndex)); - edges.Add(new HoleEdge(face.Vertex1, face.Vertex2, tetrahedronIndex)); - edges.Add(new HoleEdge(face.Vertex2, face.Vertex0, tetrahedronIndex)); + // Note: we use opposite direction for half-edge + edges.Add(new HoleEdge(face->Vertex0, face->Vertex1, tetrahedronIndex)); + edges.Add(new HoleEdge(face->Vertex1, face->Vertex2, tetrahedronIndex)); + edges.Add(new HoleEdge(face->Vertex2, face->Vertex0, tetrahedronIndex)); + } } // Sort hole edges to be able to binary search them when reconstructing neighbour information edges.Sort(); - tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Fetch latest state of the list as a span - // Re-triangulate the polygonal hole foreach (var face in holeFaces) { @@ -543,70 +543,75 @@ private unsafe void AddVertex(int vertexIndex) // This should be outside of "fixed" statement since triangulation list might grow var tetrahedronIndex = face.Tetrahedron; - ref var newTetrahedron = ref tetrahedralizationSpan[tetrahedronIndex]; - - if (face.Neighbour != -1) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - // Update neighbour reference to self - ref var neighbourTetrahedron = ref tetrahedralizationSpan[face.Neighbour]; - neighbourTetrahedron.Neighbours[face.NeighbourFaceIndex] = tetrahedronIndex; - } + var newTetrahedron = &tetrahedra[tetrahedronIndex]; - newTetrahedron.Vertices[0] = vertexIndex; + if (face.Neighbour != -1) + { + // Update neighbour reference to self + var neighbourTetrahedron = &tetrahedra[face.Neighbour]; + neighbourTetrahedron->Neighbours[face.NeighbourFaceIndex] = tetrahedronIndex; + } - var tetrahedronAdjacentIndex0 = edges.BinarySearch(new HoleEdge(face.Vertex2, face.Vertex1)); - var tetrahedronAdjacentIndex1 = edges.BinarySearch(new HoleEdge(face.Vertex0, face.Vertex2)); - var tetrahedronAdjacentIndex2 = edges.BinarySearch(new HoleEdge(face.Vertex1, face.Vertex0)); + newTetrahedron->Vertices[0] = vertexIndex; - //if (tetrahedraAdjacentIndex0 < 0 || tetrahedraAdjacentIndex1 < 0 || tetrahedraAdjacentIndex2 < 0) - // throw new InvalidOperationException("Could not find adjacent tetrahedra."); + var tetrahedronAdjacentIndex0 = edges.BinarySearch(new HoleEdge(face.Vertex2, face.Vertex1)); + var tetrahedronAdjacentIndex1 = edges.BinarySearch(new HoleEdge(face.Vertex0, face.Vertex2)); + var tetrahedronAdjacentIndex2 = edges.BinarySearch(new HoleEdge(face.Vertex1, face.Vertex0)); - // Add the shared face, in opposite order (so that tetrahedron is still positive order) - newTetrahedron.Vertices[1] = face.Vertex0; - newTetrahedron.Vertices[2] = face.Vertex1; - newTetrahedron.Vertices[3] = face.Vertex2; + //if (tetrahedraAdjacentIndex0 < 0 || tetrahedraAdjacentIndex1 < 0 || tetrahedraAdjacentIndex2 < 0) + // throw new InvalidOperationException("Could not find adjacent tetrahedra."); - // Neighbour at boundary is always opposite of newly added vertex - newTetrahedron.Neighbours[0] = face.Neighbour; + // Add the shared face, in opposite order (so that tetrahedron is still positive order) + newTetrahedron->Vertices[1] = face.Vertex0; + newTetrahedron->Vertices[2] = face.Vertex1; + newTetrahedron->Vertices[3] = face.Vertex2; - newTetrahedron.Neighbours[1] = tetrahedronAdjacentIndex0 >= 0 ? edges[tetrahedronAdjacentIndex0].Neighboor : -1; - newTetrahedron.Neighbours[2] = tetrahedronAdjacentIndex1 >= 0 ? edges[tetrahedronAdjacentIndex1].Neighboor : -1; - newTetrahedron.Neighbours[3] = tetrahedronAdjacentIndex2 >= 0 ? edges[tetrahedronAdjacentIndex2].Neighboor : -1; + // Neighbour at boundary is always opposite of newly added vertex + newTetrahedron->Neighbours[0] = face.Neighbour; - if (!IsTetrahedronPositiveOrder(vertices, ref newTetrahedron)) - throw new InvalidOperationException("Tetrahedron not in positive order"); + newTetrahedron->Neighbours[1] = tetrahedronAdjacentIndex0 >= 0 ? edges[tetrahedronAdjacentIndex0].Neighboor : -1; + newTetrahedron->Neighbours[2] = tetrahedronAdjacentIndex1 >= 0 ? edges[tetrahedronAdjacentIndex1].Neighboor : -1; + newTetrahedron->Neighbours[3] = tetrahedronAdjacentIndex2 >= 0 ? edges[tetrahedronAdjacentIndex2].Neighboor : -1; + + if (!IsTetrahedronPositiveOrder(vertices, ref *newTetrahedron)) + throw new InvalidOperationException("Tetrahedron not in positive order"); + } } } private unsafe void CheckConnectivity() { - var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Check connectivity - for (int index = 0; index < tetrahedralizationSpan.Length; index++) + fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - continue; + for (int index = 0; index < tetrahedralization.Count; index++) + { + if (!IsTetrahedronAllocated(index)) + continue; - ref var tetrahedron = ref tetrahedralizationSpan[index]; + var tetrahedron = &tetrahedra[index]; - for (int i = 0; i < 4; ++i) - { - var neighbourTetrahedronIndex = tetrahedron.Neighbours[i]; - var neighbourTetrahedronSelfIndex = -1; - if (neighbourTetrahedronIndex != -1) + for (int i = 0; i < 4; ++i) { - // Find the neighbour index of current tetrahedron in neighbourTetrahedron - ref var neighbourTetrahedron = ref tetrahedralizationSpan[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + var neighbourTetrahedronIndex = tetrahedron->Neighbours[i]; + var neighbourTetrahedronSelfIndex = -1; + if (neighbourTetrahedronIndex != -1) { - if (neighbourTetrahedron.Neighbours[j] == index) + // Find the neighbour index of current tetrahedron in neighbourTetrahedron + var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - neighbourTetrahedronSelfIndex = j; - break; + if (neighbourTetrahedron->Neighbours[j] == index) + { + neighbourTetrahedronSelfIndex = j; + break; + } } + if (neighbourTetrahedronSelfIndex == -1) + throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); } - if (neighbourTetrahedronSelfIndex == -1) - throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); } } } @@ -628,18 +633,24 @@ private int AllocateTetrahedron() return tetrahedralization.Count - 1; } - private static unsafe bool IsTetrahedronAllocated(int index, Span tetrahedronSpan) + private unsafe bool IsTetrahedronAllocated(int index) { - return tetrahedronSpan[index].Vertices[0] != -1; + fixed (Tetrahedron* tetrahedron = &tetrahedralization.Items[index]) + { + return tetrahedron->Vertices[0] != -1; + } } - private unsafe void FreeTetrahedron(int index, Span tetrahedronSpan) + private unsafe void FreeTetrahedron(int index) { // Mark it as "unused" - tetrahedronSpan[index].Vertices[0] = -1; + fixed (Tetrahedron* tetrahedron = &tetrahedralization.Items[index]) + { + tetrahedron->Vertices[0] = -1; - // Add it to free list - freeTetrahedra.Add(index); + // Add it to free list + freeTetrahedra.Add(index); + } } /// diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs index c50c4a142b..c434de344d 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs @@ -31,8 +31,8 @@ public class LightProbeRuntimeData // Computed data public Vector3[] Vertices; public int UserVertexCount; - public List Tetrahedra; - public List Faces; + public FastList Tetrahedra; + public FastList Faces; // Data to upload to GPU public Color3[] Coefficients;