Exemple #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<Vector3F> 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;
        }
Exemple #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;
              }
        }
Exemple #3
0
        public ConvexHullBuilder Clone()
        {
            var clone = new ConvexHullBuilder
              {
            _isPlanar = _isPlanar,
            _type = _type,
            _mesh = new DcelMesh(_mesh),
              };

              return clone;
        }