Exemple #1
0
        private static unsafe List <float3> CollectAllVerticesFromSubTree(BoundingVolumeHierarchy.Node *nodes, int subTreeNodeIndex, List <MeshConnectivityBuilder.Primitive> primitives)
        {
            var vertices = new List <float3>();

            int *nodesIndexStack = stackalloc int[BoundingVolumeHierarchy.Constants.UnaryStackSize];
            int  stackSize       = 1;

            nodesIndexStack[0] = subTreeNodeIndex;

            do
            {
                BoundingVolumeHierarchy.Node node = nodes[nodesIndexStack[--stackSize]];

                if (node.IsLeaf)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        if (node.IsChildValid(i))
                        {
                            MeshConnectivityBuilder.Primitive p = primitives[node.Data[i]];
                            vertices.Add(p.Vertices[0]);
                            vertices.Add(p.Vertices[1]);
                            vertices.Add(p.Vertices[2]);

                            if (p.Flags.HasFlag(MeshConnectivityBuilder.PrimitiveFlags.DefaultTrianglePairFlags))
                            {
                                vertices.Add(p.Vertices[3]);
                            }
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < 4; i++)
                    {
                        if (node.IsChildValid(i))
                        {
                            nodesIndexStack[stackSize++] = node.Data[i];
                        }
                    }
                }
            } while (stackSize > 0);

            return(vertices);
        }
Exemple #2
0
        private static unsafe NativeArray <int> ProducedPrimitivesCountPerSubTree(BoundingVolumeHierarchy.Node *nodes, int nodeCount)
        {
            var primitivesPerNode = new NativeArray <int>(nodeCount, Allocator.Temp);

            for (int nodeIndex = nodeCount - 1; nodeIndex >= 0; nodeIndex--)
            {
                BoundingVolumeHierarchy.Node node = nodes[nodeIndex];

                if (node.IsLeaf)
                {
                    primitivesPerNode[nodeIndex] = node.NumValidChildren();
                }
                else
                {
                    primitivesPerNode[nodeIndex] =
                        primitivesPerNode[node.Data[0]] + primitivesPerNode[node.Data[1]] +
                        primitivesPerNode[node.Data[2]] + primitivesPerNode[node.Data[3]];
                }
            }

            return(primitivesPerNode);
        }
Exemple #3
0
        private static unsafe int[] ProducedPrimitivesCountPerSubTree(BoundingVolumeHierarchy.Node *nodes, int nodeCount)
        {
            int[] primitivesPerNode = Enumerable.Repeat(0, nodeCount).ToArray();

            for (int nodeIndex = nodeCount - 1; nodeIndex >= 0; nodeIndex--)
            {
                BoundingVolumeHierarchy.Node node = nodes[nodeIndex];

                if (node.IsLeaf)
                {
                    primitivesPerNode[nodeIndex] = node.NumValidChildren();
                }
                else
                {
                    primitivesPerNode[nodeIndex] =
                        primitivesPerNode[node.Data[0]] + primitivesPerNode[node.Data[1]] +
                        primitivesPerNode[node.Data[2]] + primitivesPerNode[node.Data[3]];
                }
            }

            return(primitivesPerNode);
        }
Exemple #4
0
        public unsafe void BuildTreeAndOverlap([Values(2, 10, 33, 100)] int elementCount)
        {
            elementCount *= 2;
            int numNodes = elementCount / 3 * 2 + 4;
            var points   = new NativeArray <BoundingVolumeHierarchy.PointAndIndex>(elementCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var aabbs    = new NativeArray <Aabb>(elementCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var filters  = new NativeArray <CollisionFilter>(elementCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            InitializeInputWithCopyArrays(points, aabbs, filters);

            var nodes = new NativeArray <BoundingVolumeHierarchy.Node>(numNodes, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            var bvh = new BoundingVolumeHierarchy(nodes);

            bvh.Build(points, aabbs, out int numNodesOut);
            bvh.CheckIntegrity();

            var buffer = new PairBuffer {
                Pairs = new NativeList <PhysicsBody.IndexPair>(0, Allocator.Temp)
            };

            buffer.MaxId = elementCount - 1;

            BoundingVolumeHierarchy.Node *nodesPtr = (BoundingVolumeHierarchy.Node *)nodes.GetUnsafePtr();
            BoundingVolumeHierarchy.TreeOverlap(ref buffer, nodesPtr, nodesPtr);

            int numCollidingPairs = buffer.Pairs.Length;

            Assert.AreEqual(elementCount / 2, numCollidingPairs);

            buffer.Dispose();
            filters.Dispose();
            points.Dispose();
            aabbs.Dispose();
            nodes.Dispose();
        }
        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;
            }
Exemple #6
0
        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);
        }
Exemple #7
0
        private static unsafe void BuildSectionGeometry(TempSection sections, NativeList <MeshConnectivityBuilder.Primitive> primitives, NativeList <int> subTreeNodeIndices, BoundingVolumeHierarchy.Node *nodes, NativeArray <float3> vertices)
        {
            var sectionIndex = sections.Ranges.Length;

            var newSectionRange = new TempSectionRanges
            {
                VerticesMin        = sections.Vertices.Length,
                PrimitivesFlagsMin = sections.PrimitivesFlags.Length,
                PrimitivesMin      = sections.Primitives.Length
            };

            sections.Vertices.AddRange(vertices);

            int *nodesIndexStack = stackalloc int[BoundingVolumeHierarchy.Constants.UnaryStackSize];

            for (var rootIndex = 0; rootIndex < subTreeNodeIndices.Length; ++rootIndex)
            {
                var root      = subTreeNodeIndices[rootIndex];
                int stackSize = 1;
                nodesIndexStack[0] = root;

                do
                {
                    int nodeIndex = nodesIndexStack[--stackSize];
                    ref BoundingVolumeHierarchy.Node node = ref nodes[nodeIndex];

                    if (node.IsLeaf)
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            if (node.IsChildValid(i))
                            {
                                MeshConnectivityBuilder.Primitive p = primitives[node.Data[i]];
                                sections.PrimitivesFlags.Add(ConvertPrimitiveFlags(p.Flags));

                                int vertexCount = (p.Flags & MeshConnectivityBuilder.PrimitiveFlags.IsTrianglePair) != 0 ? 4 : 3;

                                Mesh.PrimitiveVertexIndices sectionPrimitive = new Mesh.PrimitiveVertexIndices();
                                byte *vertexIndices = &sectionPrimitive.A;

                                for (int v = 0; v < vertexCount; v++)
                                {
                                    vertexIndices[v] = (byte)vertices.IndexOf(p.Vertices[v]);
                                }

                                if (vertexCount == 3)
                                {
                                    sectionPrimitive.D = sectionPrimitive.C;
                                }

                                sections.Primitives.Add(sectionPrimitive);

                                int primitiveSectionIndex = sections.Primitives.Length - newSectionRange.PrimitivesMin - 1;

                                // Update primitive index in the BVH.
                                node.Data[i] = (sectionIndex << 8) | primitiveSectionIndex;
                            }
                        }
                    }
                    else
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            if (node.IsChildValid(i))
                            {
                                nodesIndexStack[stackSize++] = node.Data[i];
                            }
                        }
                    }
                } while (stackSize > 0);
            }
Exemple #8
0
        private static unsafe TempSection BuildSectionGeometry(
            int sectionIndex, List <MeshConnectivityBuilder.Primitive> primitives, List <int> subTreeNodeIndices, BoundingVolumeHierarchy.Node *nodes, float3[] vertices)
        {
            var section = new TempSection();

            section.Vertices        = vertices.ToList();
            section.Primitives      = new List <Mesh.PrimitiveVertexIndices>();
            section.PrimitivesFlags = new List <Mesh.PrimitiveFlags>();

            foreach (int root in subTreeNodeIndices)
            {
                int *nodesIndexStack = stackalloc int[BoundingVolumeHierarchy.Constants.UnaryStackSize];
                int  stackSize       = 1;
                nodesIndexStack[0] = root;

                do
                {
                    int nodeIndex = nodesIndexStack[--stackSize];
                    ref BoundingVolumeHierarchy.Node node = ref nodes[nodeIndex];

                    if (node.IsLeaf)
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            if (node.IsChildValid(i))
                            {
                                MeshConnectivityBuilder.Primitive p = primitives[node.Data[i]];
                                section.PrimitivesFlags.Add(ConvertPrimitiveFlags(p.Flags));

                                int vertexCount = p.Flags.HasFlag(MeshConnectivityBuilder.PrimitiveFlags.IsTrianglePair) ? 4 : 3;

                                Mesh.PrimitiveVertexIndices sectionPrimitive = new Mesh.PrimitiveVertexIndices();
                                byte *vertexIndices = &sectionPrimitive.A;

                                for (int v = 0; v < vertexCount; v++)
                                {
                                    vertexIndices[v] = (byte)Array.IndexOf(vertices, p.Vertices[v]);
                                }

                                if (vertexCount == 3)
                                {
                                    sectionPrimitive.D = sectionPrimitive.C;
                                }

                                section.Primitives.Add(sectionPrimitive);

                                int primitiveSectionIndex = section.Primitives.Count - 1;

                                // Update primitive index in the BVH.
                                node.Data[i] = (sectionIndex << 8) | primitiveSectionIndex;
                            }
                        }
                    }
                    else
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            if (node.IsChildValid(i))
                            {
                                nodesIndexStack[stackSize++] = node.Data[i];
                            }
                        }
                    }
                } while (stackSize > 0);
            }
Exemple #9
0
        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);
        }
        // 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;
            }