/// <summary>
        /// Rebuild a mesh from an ordered set of points.
        /// </summary>
        /// <param name="mesh">The target mesh. The mesh values will be cleared and repopulated with the shape extruded from points.</param>
        /// <param name="points">A path of points to triangulate and extrude.</param>
        /// <param name="extrude">The distance to extrude.</param>
        /// <param name="flipNormals">If true the faces will be inverted at creation.</param>
        /// <returns>An ActionResult with the status of the operation.</returns>
        public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList <Vector3> points, float extrude, bool flipNormals)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (points == null || points.Count < 3)
            {
                mesh.Clear();
                mesh.ToMesh();
                mesh.Refresh();
                return(new ActionResult(ActionResult.Status.NoChange, "Too Few Points"));
            }

            Vector3[]  vertices = points.ToArray();
            List <int> triangles;

            Log.PushLogLevel(LogLevel.Error);

            if (Triangulation.TriangulateVertices(vertices, out triangles, false))
            {
                int[] indexes = triangles.ToArray();

                if (Math.PolygonArea(vertices, indexes) < Mathf.Epsilon)
                {
                    mesh.Clear();
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Polygon Area < Epsilon"));
                }

                mesh.Clear();

                mesh.positionsInternal      = vertices;
                mesh.facesInternal          = new[] { new Face(indexes) };
                mesh.sharedVerticesInternal = SharedVertex.GetSharedVerticesWithPositions(vertices);
                mesh.InvalidateCaches();

                Vector3 nrm = Math.Normal(mesh, mesh.facesInternal[0]);

                if (Vector3.Dot(Vector3.up, nrm) > 0f)
                {
                    mesh.facesInternal[0].Reverse();
                }

                mesh.DuplicateAndFlip(mesh.facesInternal);

                mesh.Extrude(new Face[] { mesh.facesInternal[1] }, ExtrudeMethod.IndividualFaces, extrude);

                if ((extrude < 0f && !flipNormals) || (extrude > 0f && flipNormals))
                {
                    foreach (var face in mesh.facesInternal)
                    {
                        face.Reverse();
                    }
                }

                mesh.ToMesh();
                mesh.Refresh();
            }
            else
            {
                Log.PopLogLevel();
                return(new ActionResult(ActionResult.Status.Failure, "Failed Triangulating Points"));
            }

            Log.PopLogLevel();

            return(new ActionResult(ActionResult.Status.Success, "Create Polygon Shape"));
        }
        /// <summary>
        /// Rebuild a mesh from an ordered set of points.
        /// </summary>
        /// <param name="mesh">The target mesh. The mesh values will be cleared and repopulated with the shape extruded from points.</param>
        /// <param name="points">A path of points to triangulate and extrude.</param>
        /// <param name="extrude">The distance to extrude.</param>
        /// <param name="flipNormals">If true the faces will be inverted at creation.</param>
        /// <param name="holePoints">Holes in the polygon. If null this will be ignored.</param>
        /// <returns>An ActionResult with the status of the operation.</returns>
        public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList <Vector3> points,
                                                          float extrude, bool flipNormals, IList <IList <Vector3> > holePoints)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (points == null || points.Count < 3)
            {
                ClearAndRefreshMesh(mesh);
                return(new ActionResult(ActionResult.Status.NoChange, "Too Few Points"));
            }

            Vector3[] vertices = points.ToArray();

            Vector3[][] holeVertices = null;
            if (holePoints != null && holePoints.Count > 0)
            {
                holeVertices = new Vector3[holePoints.Count][];
                for (int i = 0; i < holePoints.Count; i++)
                {
                    if (holePoints[i] == null || holePoints[i].Count < 3)
                    {
                        ClearAndRefreshMesh(mesh);
                        return(new ActionResult(ActionResult.Status.NoChange, "Too Few Points in hole " + i));
                    }

                    holeVertices[i] = holePoints[i].ToArray();
                }
            }

            List <int> triangles;

            Log.PushLogLevel(LogLevel.Error);

            if (Triangulation.TriangulateVertices(vertices, out triangles, holeVertices))
            {
                Vector3[] combinedVertices = null;
                if (holeVertices != null)
                {
                    combinedVertices = new Vector3[vertices.Length + holeVertices.Sum(arr => arr.Length)];
                    Array.Copy(vertices, combinedVertices, vertices.Length);
                    int destinationIndex = vertices.Length;
                    foreach (var hole in holeVertices)
                    {
                        Array.ConstrainedCopy(hole, 0, combinedVertices, destinationIndex, hole.Length);
                        destinationIndex += hole.Length;
                    }
                }
                else
                {
                    combinedVertices = vertices;
                }

                int[] indexes = triangles.ToArray();

                if (Math.PolygonArea(combinedVertices, indexes) < Mathf.Epsilon)
                {
                    ClearAndRefreshMesh(mesh);
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Polygon Area < Epsilon"));
                }

                mesh.Clear();

                mesh.positionsInternal = combinedVertices;
                var newFace = new Face(indexes);
                mesh.facesInternal          = new[] { newFace };
                mesh.sharedVerticesInternal = SharedVertex.GetSharedVerticesWithPositions(combinedVertices);
                mesh.InvalidateCaches();

                // check that all points are represented in the triangulation
                if (newFace.distinctIndexesInternal.Length != combinedVertices.Length)
                {
                    ClearAndRefreshMesh(mesh);
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Triangulation missing points"));
                }

                Vector3 nrm = Math.Normal(mesh, mesh.facesInternal[0]);
                nrm = mesh.gameObject.transform.TransformDirection(nrm);
                if ((flipNormals
                    ? Vector3.Dot(mesh.gameObject.transform.up, nrm) > 0f
                    : Vector3.Dot(mesh.gameObject.transform.up, nrm) < 0f))
                {
                    mesh.facesInternal[0].Reverse();
                }

                if (extrude != 0.0f)
                {
                    mesh.DuplicateAndFlip(mesh.facesInternal);

                    mesh.Extrude(new Face[] { (flipNormals ? mesh.facesInternal[1] : mesh.facesInternal[0]) },
                                 ExtrudeMethod.IndividualFaces, extrude);

                    if ((extrude < 0f && !flipNormals) || (extrude > 0f && flipNormals))
                    {
                        foreach (var face in mesh.facesInternal)
                        {
                            face.Reverse();
                        }
                    }
                }

                mesh.ToMesh();
                mesh.Refresh();
            }
            else
            {
                // clear mesh instead of showing an invalid one
                ClearAndRefreshMesh(mesh);
                Log.PopLogLevel();
                return(new ActionResult(ActionResult.Status.Failure, "Failed Triangulating Points"));
            }

            Log.PopLogLevel();

            return(new ActionResult(ActionResult.Status.Success, "Create Polygon Shape"));
        }
Example #3
0
        /// <summary>
        /// Rebuild a mesh from an ordered set of points.
        /// </summary>
        /// <param name="mesh">The target mesh. The mesh values will be cleared and repopulated with the shape extruded from points.</param>
        /// <param name="points">A path of points to triangulate and extrude.</param>
        /// <param name="extrude">The distance to extrude.</param>
        /// <param name="flipNormals">If true the faces will be inverted at creation.</param>
        /// <param name="cameraLookAt">If the normal of the polygon of the first face is facing in the same direction of the camera lookat it will be inverted at creation, so it is facing the camera.</param>
        /// <returns>An ActionResult with the status of the operation.</returns>
        public static ActionResult CreateShapeFromPolygon(this ProBuilderMesh mesh, IList <Vector3> points, float extrude, bool flipNormals, Vector3 cameraLookAt)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (points == null || points.Count < 3)
            {
                ClearAndRefreshMesh(mesh);
                return(new ActionResult(ActionResult.Status.NoChange, "Too Few Points"));
            }

            Vector3[]  vertices = points.ToArray();
            List <int> triangles;

            Log.PushLogLevel(LogLevel.Error);

            if (Triangulation.TriangulateVertices(vertices, out triangles, false))
            {
                int[] indexes = triangles.ToArray();

                if (Math.PolygonArea(vertices, indexes) < Mathf.Epsilon)
                {
                    ClearAndRefreshMesh(mesh);
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Polygon Area < Epsilon"));
                }

                mesh.Clear();

                mesh.positionsInternal = vertices;
                var newFace = new Face(indexes);
                mesh.facesInternal          = new[] { newFace };
                mesh.sharedVerticesInternal = SharedVertex.GetSharedVerticesWithPositions(vertices);
                mesh.InvalidateCaches();

                // check that all points are represented in the triangulation
                if (newFace.distinctIndexesInternal.Length != vertices.Length)
                {
                    ClearAndRefreshMesh(mesh);
                    Log.PopLogLevel();
                    return(new ActionResult(ActionResult.Status.Failure, "Triangulation missing points"));
                }

                Vector3 nrm = Math.Normal(mesh, mesh.facesInternal[0]);
                cameraLookAt.Normalize();
                if ((flipNormals ? Vector3.Dot(cameraLookAt, nrm) < 0f : Vector3.Dot(cameraLookAt, nrm) > 0f))
                {
                    mesh.facesInternal[0].Reverse();
                }

                if (extrude != 0.0f)
                {
                    mesh.DuplicateAndFlip(mesh.facesInternal);

                    mesh.Extrude(new Face[] { (flipNormals ? mesh.facesInternal[1] : mesh.facesInternal[0]) }, ExtrudeMethod.IndividualFaces, extrude);

                    if ((extrude < 0f && !flipNormals) || (extrude > 0f && flipNormals))
                    {
                        foreach (var face in mesh.facesInternal)
                        {
                            face.Reverse();
                        }
                    }
                }

                mesh.ToMesh();
                mesh.Refresh();
            }
            else
            {
                // clear mesh instead of showing an invalid one
                ClearAndRefreshMesh(mesh);
                Log.PopLogLevel();
                return(new ActionResult(ActionResult.Status.Failure, "Failed Triangulating Points"));
            }

            Log.PopLogLevel();

            return(new ActionResult(ActionResult.Status.Success, "Create Polygon Shape"));
        }