Exemple #1
0
 public bool Contains(Aabb aabb) => math.all((Min <= aabb.Min) & (Max >= aabb.Max));
Exemple #2
0
 public bool Overlaps(Aabb other)
 {
     return(math.all(Max >= other.Min & Min <= other.Max));
 }
Exemple #3
0
 public static Aabb Union(Aabb a, Aabb b)
 {
     a.Include(b);
     return(a);
 }
Exemple #4
0
 public void Include(Aabb aabb)
 {
     Min = math.min(Min, aabb.Min);
     Max = math.max(Max, aabb.Max);
 }
        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
 public Aabb ExpandAabb(Aabb aabb) => new Aabb
 {
     Max = math.max(aabb.Max, aabb.Max + Linear) + Uniform,
     Min = math.min(aabb.Min, aabb.Min + Linear) - Uniform
 };
Exemple #7
0
 public void Intersect(Aabb aabb)
 {
     Min = math.max(Min, aabb.Min);
     Max = math.min(Max, aabb.Max);
 }
        // 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;
            }
Exemple #9
0
        /// <summary>
        /// Generalized convex-convex distance.
        /// </summary>
        /// <param name="verticesA">Vertices of the first collider in local space</param>
        /// <param name="verticesB">Vertices of the second collider in local space</param>
        /// <param name="aFromB">Transform from the local space of B to the local space of A</param>
        /// <param name="penetrationHandling">How to compute penetration.</param>
        /// <returns></returns>
        public static unsafe Result ConvexConvex(
            float3 *verticesA, int numVerticesA, float3 *verticesB, int numVerticesB,
            MTransform aFromB, PenetrationHandling penetrationHandling)
        {
            const float epsTerminationSq = 1e-8f; // Main loop quits when it cannot find a point that improves the simplex by at least this much
            const float epsPenetrationSq = 1e-9f; // Epsilon used to check for penetration.  Should be smaller than shape cast ConvexConvex keepDistance^2.

            // Initialize simplex.
            Simplex simplex = new Simplex();

            simplex.NumVertices    = 1;
            simplex.A              = GetSupportingVertex(new float3(1, 0, 0), verticesA, numVerticesA, verticesB, numVerticesB, aFromB);
            simplex.Direction      = simplex.A.Xyz;
            simplex.ScaledDistance = math.lengthsq(simplex.A.Xyz);
            float scaleSq = simplex.ScaledDistance;

            // Iterate.
            int       iteration     = 0;
            bool      penetration   = false;
            const int maxIterations = 64;

            for (; iteration < maxIterations; ++iteration)
            {
                // Find a new support vertex
                SupportVertex newSv = GetSupportingVertex(-simplex.Direction, verticesA, numVerticesA, verticesB, numVerticesB, aFromB);

                // If the new vertex is not significantly closer to the origin, quit
                float scaledImprovement = math.dot(simplex.A.Xyz - newSv.Xyz, simplex.Direction);
                if (scaledImprovement * math.abs(scaledImprovement) < epsTerminationSq * scaleSq)
                {
                    break;
                }

                // Add the new vertex and reduce the simplex
                switch (simplex.NumVertices++)
                {
                case 1: simplex.B = newSv; break;

                case 2: simplex.C = newSv; break;

                default: simplex.D = newSv; break;
                }
                simplex.SolveDistance();

                // Check for penetration
                scaleSq = math.lengthsq(simplex.Direction);
                float scaledDistanceSq = simplex.ScaledDistance * simplex.ScaledDistance;
                if (simplex.NumVertices == 4 || scaledDistanceSq <= epsPenetrationSq * scaleSq)
                {
                    penetration = true;
                    break;
                }
            }

            // Finalize result.
            var ret = new Result {
                Iterations = iteration + 1
            };

            // Handle penetration.
            if (penetration && penetrationHandling != PenetrationHandling.DotNotCompute)
            {
                // Allocate a hull for EPA
                int verticesCapacity = 64;
                int triangleCapacity = 2 * verticesCapacity;
                ConvexHullBuilder.Vertex *  vertices  = stackalloc ConvexHullBuilder.Vertex[verticesCapacity];
                ConvexHullBuilder.Triangle *triangles = stackalloc ConvexHullBuilder.Triangle[triangleCapacity];
                Aabb        domain = GetSupportingAabb(verticesA, numVerticesA, verticesB, numVerticesB, aFromB);
                const float simplificationTolerance = 0.0f;
                var         hull = new ConvexHullBuilder(verticesCapacity, vertices, triangles, null,
                                                         domain, simplificationTolerance, ConvexHullBuilder.IntResolution.Low);

                // Add simplex vertices to the hull, remove any vertices from the simplex that do not increase the hull dimension
                hull.AddPoint(simplex.A.Xyz, simplex.A.Id);
                if (simplex.NumVertices > 1)
                {
                    hull.AddPoint(simplex.B.Xyz, simplex.B.Id);
                    if (simplex.NumVertices > 2)
                    {
                        int dimension = hull.Dimension;
                        hull.AddPoint(simplex.C.Xyz, simplex.C.Id);
                        if (dimension == 0 && hull.Dimension == 1)
                        {
                            simplex.B = simplex.C;
                        }
                        if (simplex.NumVertices > 3)
                        {
                            dimension = hull.Dimension;
                            hull.AddPoint(simplex.D.Xyz, simplex.D.Id);
                            if (dimension > hull.Dimension)
                            {
                                if (dimension == 0)
                                {
                                    simplex.B = simplex.D;
                                }
                                else if (dimension == 1)
                                {
                                    simplex.C = simplex.D;
                                }
                            }
                        }
                    }
                }
                simplex.NumVertices = (hull.Dimension + 1);

                // If the simplex is not 3D, try expanding the hull in all directions
                while (hull.Dimension < 3)
                {
                    // Choose expansion directions
                    float3 support0, support1, support2;
                    switch (simplex.NumVertices)
                    {
                    case 1:
                        support0 = new float3(1, 0, 0);
                        support1 = new float3(0, 1, 0);
                        support2 = new float3(0, 0, 1);
                        break;

                    case 2:
                        Math.CalculatePerpendicularNormalized(math.normalize(simplex.B.Xyz - simplex.A.Xyz), out support0, out support1);
                        support2 = float3.zero;
                        break;

                    default:
                        UnityEngine.Assertions.Assert.IsTrue(simplex.NumVertices == 3);
                        support0 = math.cross(simplex.B.Xyz - simplex.A.Xyz, simplex.C.Xyz - simplex.A.Xyz);
                        support1 = float3.zero;
                        support2 = float3.zero;
                        break;
                    }

                    // Try each one
                    int  numSupports = 4 - simplex.NumVertices;
                    bool success     = false;
                    for (int i = 0; i < numSupports; i++)
                    {
                        for (int j = 0; j < 2; j++) // +/- each direction
                        {
                            SupportVertex vertex = GetSupportingVertex(support0, verticesA, numVerticesA, verticesB, numVerticesB, aFromB);
                            hull.AddPoint(vertex.Xyz, vertex.Id);
                            if (hull.Dimension == simplex.NumVertices)
                            {
                                switch (simplex.NumVertices)
                                {
                                case 1: simplex.B = vertex; break;

                                case 2: simplex.C = vertex; break;

                                default: simplex.D = vertex; break;
                                }

                                // Next dimension
                                success = true;
                                simplex.NumVertices++;
                                i = numSupports;
                                break;
                            }
                            support0 = -support0;
                        }
                        support0 = support1;
                        support1 = support2;
                    }

                    if (!success)
                    {
                        break;
                    }
                }

                // We can still fail to build a tetrahedron if the minkowski difference is really flat.
                // In those cases just find the closest point to the origin on the infinite extension of the simplex (point / line / plane)
                if (hull.Dimension != 3)
                {
                    switch (simplex.NumVertices)
                    {
                    case 1:
                    {
                        ret.ClosestPoints.Distance  = math.length(simplex.A.Xyz);
                        ret.ClosestPoints.NormalInA = -math.normalizesafe(simplex.A.Xyz, new float3(1, 0, 0));
                        break;
                    }

                    case 2:
                    {
                        float3 edge      = math.normalize(simplex.B.Xyz - simplex.A.Xyz);
                        float3 direction = math.cross(math.cross(edge, simplex.A.Xyz), edge);
                        Math.CalculatePerpendicularNormalized(edge, out float3 safeNormal, out float3 unused);     // backup, take any direction perpendicular to the edge
                        float3 normal = math.normalizesafe(direction, safeNormal);
                        ret.ClosestPoints.Distance  = math.dot(normal, simplex.A.Xyz);
                        ret.ClosestPoints.NormalInA = -normal;
                        break;
                    }

                    default:
                    {
                        UnityEngine.Assertions.Assert.IsTrue(simplex.NumVertices == 3);
                        float3 cross         = math.cross(simplex.B.Xyz - simplex.A.Xyz, simplex.C.Xyz - simplex.A.Xyz);
                        float  crossLengthSq = math.lengthsq(cross);
                        if (crossLengthSq < 1e-8f)     // hull builder can accept extremely thin triangles for which we cannot compute an accurate normal
                        {
                            simplex.NumVertices = 2;
                            goto case 2;
                        }
                        float3 normal = cross * math.rsqrt(crossLengthSq);
                        float  dot    = math.dot(normal, simplex.A.Xyz);
                        ret.ClosestPoints.Distance  = math.abs(dot);
                        ret.ClosestPoints.NormalInA = math.select(-normal, normal, dot < 0);
                        break;
                    }
                    }
                }
                else
                {
                    int   closestTriangleIndex;
                    Plane closestPlane  = new Plane();
                    float stopThreshold = 1e-4f;
                    uint *uidsCache     = stackalloc uint[triangleCapacity];
                    for (int i = 0; i < triangleCapacity; i++)
                    {
                        uidsCache[i] = 0;
                    }
                    float *distancesCache = stackalloc float[triangleCapacity];
                    do
                    {
                        // Select closest triangle.
                        closestTriangleIndex = -1;
                        foreach (int triangleIndex in hull.Triangles.Indices)
                        {
                            if (hull.Triangles[triangleIndex].Uid != uidsCache[triangleIndex])
                            {
                                uidsCache[triangleIndex]      = hull.Triangles[triangleIndex].Uid;
                                distancesCache[triangleIndex] = hull.ComputePlane(triangleIndex).Distance;
                            }
                            if (closestTriangleIndex == -1 || distancesCache[closestTriangleIndex] < distancesCache[triangleIndex])
                            {
                                closestTriangleIndex = triangleIndex;
                            }
                        }
                        closestPlane = hull.ComputePlane(closestTriangleIndex);

                        // Add supporting vertex or exit.
                        SupportVertex sv  = GetSupportingVertex(closestPlane.Normal, verticesA, numVerticesA, verticesB, numVerticesB, aFromB);
                        float         d2P = math.dot(closestPlane.Normal, sv.Xyz) + closestPlane.Distance;
                        if (math.abs(d2P) > stopThreshold && hull.AddPoint(sv.Xyz, sv.Id))
                        {
                            stopThreshold *= 1.3f;
                        }
                        else
                        {
                            break;
                        }
                    } while (++iteration < maxIterations);

                    // There could be multiple triangles in the closest plane, pick the one that has the closest point to the origin on its face
                    foreach (int triangleIndex in hull.Triangles.Indices)
                    {
                        if (distancesCache[triangleIndex] >= closestPlane.Distance - 1e-4f)
                        {
                            ConvexHullBuilder.Triangle triangle = hull.Triangles[triangleIndex];
                            float3 a     = hull.Vertices[triangle.Vertex0].Position;
                            float3 b     = hull.Vertices[triangle.Vertex1].Position;
                            float3 c     = hull.Vertices[triangle.Vertex2].Position;
                            float3 cross = math.cross(b - a, c - a);
                            float3 dets  = new float3(
                                math.dot(math.cross(a - c, cross), a),
                                math.dot(math.cross(b - a, cross), b),
                                math.dot(math.cross(c - b, cross), c));
                            if (math.all(dets >= 0))
                            {
                                Plane plane = hull.ComputePlane(triangleIndex);
                                if (math.dot(plane.Normal, closestPlane.Normal) > (1 - 1e-4f))
                                {
                                    closestTriangleIndex = triangleIndex;
                                    closestPlane         = hull.ComputePlane(triangleIndex);
                                }
                                break;
                            }
                        }
                    }

                    // Generate simplex.
                    {
                        ConvexHullBuilder.Triangle triangle = hull.Triangles[closestTriangleIndex];
                        simplex.NumVertices    = 3;
                        simplex.A.Xyz          = hull.Vertices[triangle.Vertex0].Position; simplex.A.Id = hull.Vertices[triangle.Vertex0].UserData;
                        simplex.B.Xyz          = hull.Vertices[triangle.Vertex1].Position; simplex.B.Id = hull.Vertices[triangle.Vertex1].UserData;
                        simplex.C.Xyz          = hull.Vertices[triangle.Vertex2].Position; simplex.C.Id = hull.Vertices[triangle.Vertex2].UserData;
                        simplex.Direction      = -closestPlane.Normal;
                        simplex.ScaledDistance = closestPlane.Distance;

                        // Set normal and distance.
                        ret.ClosestPoints.NormalInA = -closestPlane.Normal;
                        ret.ClosestPoints.Distance  = closestPlane.Distance;
                    }
                }
            }
            else
            {
                // Compute distance and normal.
                float lengthSq    = math.lengthsq(simplex.Direction);
                float invLength   = math.rsqrt(lengthSq);
                bool  smallLength = lengthSq == 0;
                ret.ClosestPoints.Distance  = math.select(simplex.ScaledDistance * invLength, 0.0f, smallLength);
                ret.ClosestPoints.NormalInA = math.select(simplex.Direction * invLength, new float3(1, 0, 0), smallLength);

                // Make sure the normal is always valid.
                if (!math.all(math.isfinite(ret.ClosestPoints.NormalInA)))
                {
                    ret.ClosestPoints.NormalInA = new float3(1, 0, 0);
                }
            }

            // Compute position.
            float3 closestPoint = ret.ClosestPoints.NormalInA * ret.ClosestPoints.Distance;
            float4 coordinates  = simplex.ComputeBarycentricCoordinates(closestPoint);

            ret.ClosestPoints.PositionOnAinA =
                verticesA[simplex.A.IdA] * coordinates.x +
                verticesA[simplex.B.IdA] * coordinates.y +
                verticesA[simplex.C.IdA] * coordinates.z +
                verticesA[simplex.D.IdA] * coordinates.w;

            // Encode simplex.
            ret.Simplex.x = simplex.A.Id;
            ret.Simplex.y = simplex.NumVertices >= 2 ? simplex.B.Id : Result.InvalidSimplexVertex;
            ret.Simplex.z = simplex.NumVertices >= 3 ? simplex.C.Id : Result.InvalidSimplexVertex;

            // Done.
            UnityEngine.Assertions.Assert.IsTrue(math.isfinite(ret.ClosestPoints.Distance));
            UnityEngine.Assertions.Assert.IsTrue(math.abs(math.lengthsq(ret.ClosestPoints.NormalInA) - 1.0f) < 1e-5f);
            return(ret);
        }