public static unsafe BlobAssetReference <Collider> Create(NativeArray <float3> vertices, NativeArray <int3> triangles, CollisionFilter filter, Material material) { // Copy vertices var tempVertices = new NativeArray <float3>(vertices, Allocator.Temp); // Triangle indices - needed for WeldVertices var tempIndices = new NativeArray <int>(triangles.Length * 3, Allocator.Temp); for (int iTriangle = 0; iTriangle < triangles.Length; iTriangle++) { if (triangles[iTriangle][0] >= 0 && triangles[iTriangle][0] < vertices.Length && triangles[iTriangle][1] >= 0 && triangles[iTriangle][1] < vertices.Length && triangles[iTriangle][2] >= 0 && triangles[iTriangle][2] < vertices.Length) { tempIndices[iTriangle * 3] = triangles[iTriangle][0]; tempIndices[iTriangle * 3 + 1] = triangles[iTriangle][1]; tempIndices[iTriangle * 3 + 2] = triangles[iTriangle][2]; } else { throw new ArgumentException("Tried to create a MeshCollider with indices referencing outside vertex array"); } } // Build connectivity and primitives NativeList <float3> uniqueVertices = MeshConnectivityBuilder.WeldVertices(tempIndices, tempVertices); var tempTriangleIndices = new NativeArray <int3>(triangles.Length, Allocator.Temp); UnsafeUtility.MemCpy(tempTriangleIndices.GetUnsafePtr(), tempIndices.GetUnsafePtr(), tempIndices.Length * UnsafeUtility.SizeOf <int>()); var connectivity = new MeshConnectivityBuilder(tempTriangleIndices, uniqueVertices); NativeList <MeshConnectivityBuilder.Primitive> primitives = connectivity.EnumerateQuadDominantGeometry(tempTriangleIndices, uniqueVertices); // Build bounding volume hierarchy int nodeCount = math.max(primitives.Length * 2 + 1, 2); // We need at least two nodes - an "invalid" node and a root node. var nodes = new NativeArray <BoundingVolumeHierarchy.Node>(nodeCount, Allocator.Temp); int numNodes = 0; { // Prepare data for BVH var points = new NativeList <BoundingVolumeHierarchy.PointAndIndex>(primitives.Length, Allocator.Temp); var aabbs = new NativeArray <Aabb>(primitives.Length, Allocator.Temp); for (int i = 0; i < primitives.Length; i++) { MeshConnectivityBuilder.Primitive p = primitives[i]; // Skip degenerate triangles if (MeshConnectivityBuilder.IsTriangleDegenerate(p.Vertices[0], p.Vertices[1], p.Vertices[2])) { continue; } aabbs[i] = Aabb.CreateFromPoints(p.Vertices); points.Add(new BoundingVolumeHierarchy.PointAndIndex { Position = aabbs[i].Center, Index = i }); } var bvh = new BoundingVolumeHierarchy(nodes); bvh.Build(points.AsArray(), aabbs, out numNodes, useSah: true); } // Build mesh sections BoundingVolumeHierarchy.Node *nodesPtr = (BoundingVolumeHierarchy.Node *)nodes.GetUnsafePtr(); MeshBuilder.TempSection sections = MeshBuilder.BuildSections(nodesPtr, numNodes, primitives); // Allocate collider int meshDataSize = Mesh.CalculateMeshDataSize(numNodes, sections.Ranges); int totalColliderSize = Math.NextMultipleOf(sizeof(MeshCollider), 16) + meshDataSize; MeshCollider *meshCollider = (MeshCollider *)UnsafeUtility.Malloc(totalColliderSize, 16, Allocator.Temp); // Initialize it { UnsafeUtility.MemClear(meshCollider, totalColliderSize); meshCollider->MemorySize = totalColliderSize; meshCollider->m_Header.Type = ColliderType.Mesh; meshCollider->m_Header.CollisionType = CollisionType.Composite; meshCollider->m_Header.Version += 1; meshCollider->m_Header.Magic = 0xff; ref var mesh = ref meshCollider->Mesh; mesh.Init(nodesPtr, numNodes, sections, filter, material); // Calculate combined filter meshCollider->m_Header.Filter = mesh.Sections.Length > 0 ? mesh.Sections[0].Filters[0] : CollisionFilter.Default; for (int i = 0; i < mesh.Sections.Length; ++i) { for (var j = 0; j < mesh.Sections[i].Filters.Length; ++j) { var f = mesh.Sections[i].Filters[j]; meshCollider->m_Header.Filter = CollisionFilter.CreateUnion(meshCollider->m_Header.Filter, f); } } meshCollider->m_Aabb = meshCollider->Mesh.BoundingVolumeHierarchy.Domain; meshCollider->NumColliderKeyBits = meshCollider->Mesh.NumColliderKeyBits; }
internal static unsafe List <TempSection> BuildSections(BoundingVolumeHierarchy.Node *nodes, int nodeCount, List <MeshConnectivityBuilder.Primitive> primitives) { var tempSections = new List <TempSection>(); // Traverse the tree and break out geometry into sections int *nodesIndexStack = stackalloc int[BoundingVolumeHierarchy.Constants.UnaryStackSize]; int stackSize = 1; nodesIndexStack[0] = 1; const float uniqueVerticesPerPrimitiveFactor = 1.5f; int[] primitivesCountInSubTree = ProducedPrimitivesCountPerSubTree(nodes, nodeCount); do { int nodeIndex = nodesIndexStack[--stackSize]; int subTreeVertexCountEstimate = (int)(uniqueVerticesPerPrimitiveFactor * primitivesCountInSubTree[nodeIndex]); var subTreeIndices = new List <int>(); if (subTreeVertexCountEstimate < Mesh.Section.MaxNumVertices) { subTreeIndices.Add(nodeIndex); } else { // Sub tree is too big, break it up. BoundingVolumeHierarchy.Node node = nodes[nodeIndex]; for (int i = 0; i < 4; i++) { if (node.IsChildValid(i)) { int childNodeIndex = node.Data[i]; int nodeSubTreeVertexCount = (int)(uniqueVerticesPerPrimitiveFactor * primitivesCountInSubTree[childNodeIndex]); if (nodeSubTreeVertexCount < Mesh.Section.MaxNumVertices) { subTreeIndices.Add(childNodeIndex); } else { nodesIndexStack[stackSize++] = childNodeIndex; } } } } float tempUniqueVertexPrimitiveFactor = 1.0f; const float factorStepIncrement = 0.25f; while (subTreeIndices.Any()) { // Try to combine sub trees if multiple sub trees can fit into one section. var nodeIndices = new List <int>(); int vertexCountEstimate = 0; foreach (int subTreeNodeIndex in subTreeIndices) { int nodeIndexCount = (int)(tempUniqueVertexPrimitiveFactor * primitivesCountInSubTree[subTreeNodeIndex]); if (vertexCountEstimate + nodeIndexCount < Mesh.Section.MaxNumVertices) { vertexCountEstimate += nodeIndexCount; nodeIndices.Add(subTreeNodeIndex); } } if (!nodeIndices.Any()) { // We failed to fit any sub tree into sections. // Split up nodes and push them to stack. foreach (int subTreeNodeIndex in subTreeIndices) { BoundingVolumeHierarchy.Node nodeToSplit = nodes[subTreeNodeIndex]; for (int i = 0; i < 4; i++) { if (nodeToSplit.IsChildValid(i)) { nodesIndexStack[stackSize++] = nodeToSplit.Data[i]; } } } subTreeIndices.Clear(); continue; } // Collect vertices from all sub trees. var tmpVertices = new List <float3>(); foreach (int subTreeNodeIndex in nodeIndices) { tmpVertices.AddRange(CollectAllVerticesFromSubTree(nodes, subTreeNodeIndex, primitives)); } float3[] allVertices = tmpVertices.ToArray(); int[] vertexIndices = new int[allVertices.Length]; for (int i = 0; i < vertexIndices.Length; i++) { vertexIndices[i] = i; } MeshConnectivityBuilder.WeldVertices(vertexIndices, ref allVertices); if (allVertices.Length < Mesh.Section.MaxNumVertices) { tempSections.Add(BuildSectionGeometry(tempSections.Count, primitives, nodeIndices, nodes, allVertices)); // Remove used indices foreach (int nodeTreeIndex in nodeIndices) { subTreeIndices.Remove(nodeTreeIndex); } } else { // Estimate of num vertices per primitives was wrong. // Increase the tempUniqueVertexPrimitiveFactor. tempUniqueVertexPrimitiveFactor += factorStepIncrement; } } }while (stackSize > 0); return(tempSections); }
internal static unsafe TempSection BuildSections(BoundingVolumeHierarchy.Node *nodes, int nodeCount, NativeList <MeshConnectivityBuilder.Primitive> primitives) { var tempSections = new TempSection() { PrimitivesFlags = new NativeList <Mesh.PrimitiveFlags>(Allocator.Temp), Primitives = new NativeList <Mesh.PrimitiveVertexIndices>(Allocator.Temp), Vertices = new NativeList <float3>(Allocator.Temp), Ranges = new NativeList <TempSectionRanges>(Allocator.Temp) }; if (primitives.Length == 0) { // Early-out in the case of no input primitives return(tempSections); } // Traverse the tree and break out geometry into sections int *nodesIndexStack = stackalloc int[BoundingVolumeHierarchy.Constants.UnaryStackSize]; int stackSize = 1; nodesIndexStack[0] = 1; const float uniqueVerticesPerPrimitiveFactor = 1.5f; var primitivesCountInSubTree = ProducedPrimitivesCountPerSubTree(nodes, nodeCount); var subTreeIndices = new NativeList <int>(Allocator.Temp); var nodeIndices = new NativeList <int>(Allocator.Temp); var tmpVertices = new NativeList <float3>(Allocator.Temp); do { int nodeIndex = nodesIndexStack[--stackSize]; int subTreeVertexCountEstimate = (int)(uniqueVerticesPerPrimitiveFactor * primitivesCountInSubTree[nodeIndex]); subTreeIndices.Clear(); if (subTreeVertexCountEstimate < Mesh.Section.MaxNumVertices) { subTreeIndices.Add(nodeIndex); } else { // Sub tree is too big, break it up. BoundingVolumeHierarchy.Node node = nodes[nodeIndex]; for (int i = 0; i < 4; i++) { if (node.IsChildValid(i)) { int childNodeIndex = node.Data[i]; int nodeSubTreeVertexCount = (int)(uniqueVerticesPerPrimitiveFactor * primitivesCountInSubTree[childNodeIndex]); if (nodeSubTreeVertexCount < Mesh.Section.MaxNumVertices) { subTreeIndices.Add(childNodeIndex); } else { nodesIndexStack[stackSize++] = childNodeIndex; } } } } float tempUniqueVertexPrimitiveFactor = 1.0f; const float factorStepIncrement = 0.25f; while (subTreeIndices.Length > 0) { // Try to combine sub trees if multiple sub trees can fit into one section. nodeIndices.Clear(); int vertexCountEstimate = 0; for (var i = 0; i < subTreeIndices.Length; ++i) { var subTreeNodeIndex = subTreeIndices[i]; int nodeIndexCount = (int)(tempUniqueVertexPrimitiveFactor * primitivesCountInSubTree[subTreeNodeIndex]); if (vertexCountEstimate + nodeIndexCount < Mesh.Section.MaxNumVertices) { vertexCountEstimate += nodeIndexCount; nodeIndices.Add(subTreeNodeIndex); } } if (nodeIndices.Length == 0) { // We failed to fit any sub tree into sections. // Split up nodes and push them to stack. for (var index = 0; index < subTreeIndices.Length; ++index) { var subTreeNodeIndex = subTreeIndices[index]; BoundingVolumeHierarchy.Node nodeToSplit = nodes[subTreeNodeIndex]; for (int i = 0; i < 4; i++) { if (nodeToSplit.IsChildValid(i)) { nodesIndexStack[stackSize++] = nodeToSplit.Data[i]; } } } subTreeIndices.Clear(); continue; } // Collect vertices from all sub trees. tmpVertices.Clear(); for (var i = 0; i < nodeIndices.Length; ++i) { var subTreeNodeIndex = nodeIndices[i]; CollectAllVerticesFromSubTree(nodes, subTreeNodeIndex, primitives, tmpVertices); } var vertexIndices = new NativeArray <int>(tmpVertices.Length, Allocator.Temp); for (int i = 0; i < vertexIndices.Length; i++) { vertexIndices[i] = i; } NativeList <float3> uniqueVertices = MeshConnectivityBuilder.WeldVertices(vertexIndices, new NativeArray <float3>(tmpVertices, Allocator.Temp)); if (uniqueVertices.Length < Mesh.Section.MaxNumVertices) { BuildSectionGeometry(tempSections, primitives, nodeIndices, nodes, new NativeArray <float3>(uniqueVertices, Allocator.Temp)); // Remove used indices for (var i = 0; i < nodeIndices.Length; ++i) { var nodeTreeIndex = nodeIndices[i]; subTreeIndices.RemoveAtSwapBack(subTreeIndices.IndexOf(nodeTreeIndex)); } } else { // Estimate of num vertices per primitives was wrong. // Increase the tempUniqueVertexPrimitiveFactor. tempUniqueVertexPrimitiveFactor += factorStepIncrement; } } }while (stackSize > 0); return(tempSections); }
// followed by variable sized mesh data #region Construction // Create a mesh collider asset from a set of triangles public static unsafe BlobAssetReference <Collider> Create(float3[] vertices, int[] indices, CollisionFilter?filter = null, Material?material = null) { int numVertices = vertices.Length; int numIndices = indices.Length; int numTriangles = numIndices / 3; // Copy vertices float3[] tempVertices = new float3[numVertices]; Array.Copy(vertices, tempVertices, numVertices); // Copy indices int[] tempIndices = new int[numIndices]; for (int iTriangle = 0; iTriangle < numTriangles; iTriangle++) { int iIndex0 = iTriangle * 3; int iIndex1 = iIndex0 + 1; int iIndex2 = iIndex0 + 2; tempIndices[iIndex0] = indices[iIndex0]; tempIndices[iIndex1] = indices[iIndex1]; tempIndices[iIndex2] = indices[iIndex2]; } // Build connectivity and primitives List <MeshConnectivityBuilder.Primitive> primitives = null; { MeshConnectivityBuilder.WeldVertices(tempIndices, ref tempVertices); var connectivity = new MeshConnectivityBuilder(tempIndices, tempVertices); primitives = connectivity.EnumerateQuadDominantGeometry(tempIndices, tempVertices); } // Build bounding volume hierarchy var nodes = new NativeArray <BoundingVolumeHierarchy.Node>(primitives.Count * 2 + 1, Allocator.Temp); int numNodes = 0; { // Prepare data for BVH var points = new NativeArray <BoundingVolumeHierarchy.PointAndIndex>(primitives.Count, Allocator.Temp); var aabbs = new NativeArray <Aabb>(primitives.Count, Allocator.Temp); for (int i = 0; i < primitives.Count; i++) { MeshConnectivityBuilder.Primitive p = primitives[i]; // Skip degenerate triangles if (MeshConnectivityBuilder.IsTriangleDegenerate(p.Vertices[0], p.Vertices[1], p.Vertices[2])) { continue; } aabbs[i] = Aabb.CreateFromPoints(p.Vertices); points[i] = new BoundingVolumeHierarchy.PointAndIndex { Position = aabbs[i].Center, Index = i }; } var bvh = new BoundingVolumeHierarchy(nodes); bvh.Build(points, aabbs, out numNodes, useSah: true); points.Dispose(); aabbs.Dispose(); } // Build mesh sections BoundingVolumeHierarchy.Node * nodesPtr = (BoundingVolumeHierarchy.Node *)nodes.GetUnsafePtr(); List <MeshBuilder.TempSection> sections = MeshBuilder.BuildSections(nodesPtr, numNodes, primitives); // Allocate collider int meshDataSize = Mesh.CalculateMeshDataSize(numNodes, sections); int totalColliderSize = Math.NextMultipleOf(sizeof(MeshCollider), 16) + meshDataSize; MeshCollider *meshCollider = (MeshCollider *)UnsafeUtility.Malloc(totalColliderSize, 16, Allocator.Temp); // Initialize it { UnsafeUtility.MemClear(meshCollider, totalColliderSize); meshCollider->MemorySize = totalColliderSize; meshCollider->m_Header.Type = ColliderType.Mesh; meshCollider->m_Header.CollisionType = CollisionType.Composite; meshCollider->m_Header.Version += 1; meshCollider->m_Header.Magic = 0xff; ref var mesh = ref meshCollider->Mesh; mesh.Init(nodesPtr, numNodes, sections, filter ?? CollisionFilter.Default, material ?? Material.Default); // Calculate combined filter meshCollider->m_Header.Filter = mesh.Sections[0].Filters[0]; for (int i = 0; i < mesh.Sections.Length; ++i) { foreach (CollisionFilter f in mesh.Sections[i].Filters) { meshCollider->m_Header.Filter = CollisionFilter.CreateUnion(meshCollider->m_Header.Filter, f); } } meshCollider->m_Aabb = meshCollider->Mesh.BoundingVolumeHierarchy.Domain; meshCollider->NumColliderKeyBits = meshCollider->Mesh.NumColliderKeyBits; }