Пример #1
0
        /// <summary>
        /// Creates a convex hull mesh for a set of points.
        /// </summary>
        /// <param name="points">The points.</param>
        /// <param name="vertexLimit">
        /// The vertex limit. Must be greater than 0. Common values are 32 or 64.
        /// </param>
        /// <param name="skinWidth">
        /// The skin width. Common values are 0.01 or 0.001.
        /// </param>
        /// <returns>
        /// The mesh of the convex hull or <see langword="null"/> if the point list is
        /// <see langword="null"/> or empty.
        /// </returns>
        /// <remarks>
        /// <para>
        /// The returned mesh describes the convex hull. All faces are convex polygons.
        /// </para>
        /// <para>
        /// If the created convex hull has more vertices than <paramref name="vertexLimit"/>, the hull
        /// will be simplified. The simplified hull is conservative, which means it contains all given
        /// <paramref name="points"/> and is less "tight" than the exact hull. It is possible that the
        /// simplified hull contains slightly more vertices than <paramref name="vertexLimit"/> (e.g. it
        /// is possible that for a vertex limit of 32 a hull with 34 vertices is returned).
        /// </para>
        /// <para>
        /// All planes of the convex hull are extruded by the <paramref name="skinWidth"/>. This can be
        /// used to increase or decrease the size of the convex hull.
        /// </para>
        /// </remarks>
        public static DcelMesh CreateConvexHull(IEnumerable <Vector3> points, int vertexLimit, float skinWidth)
        {
            // Nothing to do for empty input.
            if (points == null)
            {
                return(null);
            }

            ConvexHullBuilder builder = new ConvexHullBuilder();

            builder.Grow(points, vertexLimit, skinWidth);

            if (builder.Type == ConvexHullType.Empty)
            {
                return(null);
            }

            return(builder.Mesh);
        }
Пример #2
0
        private float GetConcavity(int vertexLimit, bool sampleVertices, bool sampleCenters)
        {
            // Initially we assume that the new vertices are simply the union of the islands' vertices.
            Vertices = IslandA.Vertices.Union(IslandB.Vertices).ToArray();

            try
            {
                // Create hull mesh.

                // Incremental hull building.
                // Note: Commented out because this building the hull this way is less stable.
                //bool rebuild = true;
                //try
                //{
                //  if (IslandA.ConvexHullBuilder != null)
                //  {
                //    if (IslandB.ConvexHullBuilder == null || IslandA.Vertices.Length > IslandB.Vertices.Length)
                //    {
                //      ConvexHullBuilder = IslandA.ConvexHullBuilder.Clone();
                //      ConvexHullBuilder.Grow(IslandB.Vertices, vertexLimit, 0);
                //    }
                //    else
                //    {
                //      ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone();
                //      ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0);
                //    }
                //    rebuild = false;
                //  }
                //  else if (IslandB.ConvexHullBuilder != null)
                //  {
                //    ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone();
                //    ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0);
                //    rebuild = false;
                //  }
                //}
                //catch (GeometryException)
                //{
                //  rebuild = true;
                //}

                //if (rebuild)
                {
                    try
                    {
                        ConvexHullBuilder = new ConvexHullBuilder();
                        ConvexHullBuilder.Grow(Vertices, vertexLimit, 0);
                    }
                    catch (GeometryException)
                    {
                        // Hull building failed. Try again with a randomized order.
                        var random = new Random(1234567);
                        // Fisher-Yates shuffle:
                        for (int i = Vertices.Length - 1; i >= 1; i--)
                        {
                            var v = Vertices[i];
                            var j = random.NextInteger(0, i);
                            Vertices[i] = Vertices[j];
                            Vertices[j] = v;
                        }
                    }
                }

                var hullMesh = ConvexHullBuilder.Mesh.ToTriangleMesh();

                // Now, we have a reduced set of vertices.
                Vertices = hullMesh.Vertices.ToArray();

                // For larger meshes we create an AabbTree as acceleration structure.
                AabbTree <int> partition = null;
                if (hullMesh.NumberOfTriangles > 12)
                {
                    partition = new AabbTree <int>
                    {
                        GetAabbForItem         = i => hullMesh.GetTriangle(i).Aabb,
                        BottomUpBuildThreshold = 0,
                    };
                    for (int i = 0; i < hullMesh.NumberOfTriangles; i++)
                    {
                        partition.Add(i);
                    }

                    partition.Update(true);
                }

                Aabb  aabb       = Aabb;
                float aabbExtent = aabb.Extent.Length;

                // Note: For a speed-up we could skip some ray tests in the next loop and only sample
                // a few vertices if there would be a lot of tests.

                // The next loop performs ray casts against the hull mesh to determine the maximum
                // concavity. We ensure that we make only one ray cast per vertex even if a vertex
                // is shared by many triangles.
                float maxConcavity = 0;
                foreach (var triangle in IslandA.Triangles.Union(IslandB.Triangles))
                {
                    if (sampleVertices)
                    {
                        for (int i = 0; i < triangle.Vertices.Length; i++)
                        {
                            // Each vertex can be shared by several triangles of the current islands.
                            // Therefore, we check the edges that contain this vertex. If an edge neighbor
                            // is in the same island, we make sure that the vertex concavity is computed only once
                            // in the triangle with the smallest Id.
                            var neighbor0 = triangle.Neighbors[(i + 1) % 3];
                            var neighbor1 = triangle.Neighbors[(i + 2) % 3];

                            if (neighbor0 != null &&
                                (neighbor0.Island == IslandA || neighbor0.Island == IslandB) &&
                                triangle.Id > neighbor0.Id)
                            {
                                // No need to test: The neighbor is in the same islands and this triangle Id is larger.
                                continue;
                            }

                            if (neighbor1 != null &&
                                (neighbor1.Island == IslandA || neighbor1.Island == IslandB) &&
                                triangle.Id > neighbor1.Id)
                            {
                                // No need to test: The neighbor is in the same islands and this triangle Id is larger.
                                continue;
                            }

                            var position = triangle.Vertices[i];
                            var normal   = triangle.VertexNormals[i];

                            // Degenerate triangles are ignored.
                            if (normal.IsNumericallyZero)
                            {
                                continue;
                            }

                            // Shoot a ray from outside the hull mesh to the vertex.
                            float    hitDistance;
                            Vector3F rayOrigin = position + normal * aabbExtent;
                            float    rayLength = (position - rayOrigin).Length;
                            var      ray       = new Ray(rayOrigin, -normal, rayLength);
                            if (partition != null)
                            {
                                // Use AABB tree for better performance.
                                foreach (var triangleIndex in partition.GetOverlaps(ray))
                                {
                                    var candidateTriangle = hullMesh.GetTriangle(triangleIndex);
                                    var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance);
                                    if (hit)
                                    {
                                        // The concavity is the distance from the hull to the vertex.
                                        float concavity = rayLength - hitDistance;
                                        maxConcavity = Math.Max(maxConcavity, concavity);
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                // No AABB tree.
                                var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance);
                                if (hit)
                                {
                                    float concavity = rayLength - hitDistance;
                                    maxConcavity = Math.Max(maxConcavity, concavity);
                                }
                            }
                        }
                    }

                    if (sampleCenters)
                    {
                        // Test: Also shoot from the triangle centers.
                        var center = (triangle.Vertices[0] + triangle.Vertices[1] + triangle.Vertices[2]) / 3;
                        var normal = triangle.Normal;

                        // Degenerate triangles are ignored.
                        if (normal.IsNumericallyZero)
                        {
                            continue;
                        }

                        // Shoot a ray from outside the hull mesh to the vertex.
                        float    hitDistance;
                        Vector3F rayOrigin = center + normal * aabbExtent;
                        float    rayLength = (center - rayOrigin).Length;
                        var      ray       = new Ray(rayOrigin, -normal, rayLength);
                        if (partition != null)
                        {
                            // Use AABBTree for better performance.
                            foreach (var triangleIndex in partition.GetOverlaps(ray))
                            {
                                var candidateTriangle = hullMesh.GetTriangle(triangleIndex);
                                var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance);
                                if (hit)
                                {
                                    // The concavity is the distance from the hull to the vertex.
                                    float concavity = rayLength - hitDistance;
                                    maxConcavity = Math.Max(maxConcavity, concavity);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            // No AABBTree.
                            var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance);
                            if (hit)
                            {
                                float concavity = rayLength - hitDistance;
                                maxConcavity = Math.Max(maxConcavity, concavity);
                            }
                        }
                    }
                }

                return(maxConcavity);
            }
            catch (GeometryException)
            {
                // Ouch, the convex hull generation failed. This can happen for degenerate inputs
                // and numerical problems in the convex hull builder.
                ConvexHullBuilder = null;
                return(0);
            }
        }