Example #1
0
        /// <summary>
        /// Insert a number of new points to each edge. Points are evenly spaced out along the edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="edges">The edges to split with points.</param>
        /// <param name="count">The number of new points to insert. Must be greater than 0.</param>
        /// <returns>The new edges created by inserting points.</returns>
        public static List <Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, IList <Edge> edges, int count)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (edges == null)
            {
                throw new ArgumentNullException("edges");
            }

            if (count < 1 || count > 512)
            {
                Log.Error("New edge vertex count is less than 1 or greater than 512.");
                return(null);
            }

            List <Vertex>         vertices        = new List <Vertex>(mesh.GetVertices());
            Dictionary <int, int> lookup          = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV        = mesh.sharedTextureLookup;
            List <int>            indexesToDelete = new List <int>();
            IEnumerable <Edge>    commonEdges     = EdgeUtility.GetSharedVertexHandleEdges(mesh, edges);
            List <Edge>           distinctEdges   = commonEdges.Distinct().ToList();

            Dictionary <Face, FaceRebuildData> modifiedFaces = new Dictionary <Face, FaceRebuildData>();

            int originalSharedIndexesCount = lookup.Count();
            int sharedIndexesCount         = originalSharedIndexesCount;

            foreach (Edge edge in distinctEdges)
            {
                Edge localEdge = EdgeUtility.GetEdgeWithSharedVertexHandles(mesh, edge);

                // Generate the new vertices that will be inserted on this edge
                List <Vertex> verticesToAppend = new List <Vertex>(count);

                for (int i = 0; i < count; i++)
                {
                    verticesToAppend.Add(Vertex.Mix(vertices[localEdge.a], vertices[localEdge.b],
                                                    (i + 1) / ((float)count + 1)));
                }

                List <SimpleTuple <Face, Edge> > adjacentFaces = ElementSelection.GetNeighborFaces(mesh, localEdge);
                Edge edgeLookUp = new Edge(lookup[localEdge.a], lookup[localEdge.b]);
                Edge e          = new Edge();

                // foreach face attached to common edge, append vertices
                foreach (SimpleTuple <Face, Edge> tup in adjacentFaces)
                {
                    Face face = tup.item1;

                    FaceRebuildData data;

                    if (!modifiedFaces.TryGetValue(face, out data))
                    {
                        data      = new FaceRebuildData();
                        data.face = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv),
                                             face.smoothingGroup, face.textureGroup, -1, face.manualUV);
                        data.vertices =
                            new List <Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
                        data.sharedIndexes   = new List <int>();
                        data.sharedIndexesUV = new List <int>();

                        foreach (int i in face.distinctIndexesInternal)
                        {
                            int shared;

                            if (lookup.TryGetValue(i, out shared))
                            {
                                data.sharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }
                        }

                        indexesToDelete.AddRange(face.distinctIndexesInternal);

                        modifiedFaces.Add(face, data);

                        //Ordering vertices in the new face
                        List <Vertex> orderedVertices        = new List <Vertex>();
                        List <int>    orderedSharedIndexes   = new List <int>();
                        List <int>    orderedSharedUVIndexes = new List <int>();
                        List <Edge>   peripheralEdges        = WingedEdge.SortEdgesByAdjacency(face);

                        for (int i = 0; i < peripheralEdges.Count; i++)
                        {
                            e.a = peripheralEdges[i].a;
                            e.b = peripheralEdges[i].b;

                            orderedVertices.Add(vertices[e.a]);

                            int shared;
                            if (lookup.TryGetValue(e.a, out shared))
                            {
                                orderedSharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }

                            if (edgeLookUp.a == lookup[e.a] && edgeLookUp.b == lookup[e.b])
                            {
                                for (int j = 0; j < count; j++)
                                {
                                    orderedVertices.Add(verticesToAppend[j]);
                                    orderedSharedIndexes.Add(sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                            else if (edgeLookUp.a == lookup[e.b] && edgeLookUp.b == lookup[e.a])
                            {
                                for (int j = count - 1; j >= 0; j--)
                                {
                                    orderedVertices.Add(verticesToAppend[j]);
                                    orderedSharedIndexes.Add(sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                        }

                        data.vertices        = orderedVertices;
                        data.sharedIndexes   = orderedSharedIndexes;
                        data.sharedIndexesUV = orderedSharedUVIndexes;
                    }
                    else
                    {
                        //Get ordered vertices in the existing face and add new ones
                        List <Vertex> orderedVertices        = data.vertices;
                        List <int>    orderedSharedIndexes   = data.sharedIndexes;
                        List <int>    orderedSharedUVIndexes = data.sharedIndexesUV;

                        for (int i = 0; i < orderedVertices.Count; i++)
                        {
                            Vertex edgeStart      = orderedVertices[i];
                            int    edgeStartIndex = vertices.IndexOf(edgeStart);

                            Vertex edgeEnd      = orderedVertices[(i + 1) % orderedVertices.Count];
                            int    edgeEndIndex = vertices.IndexOf(edgeEnd);

                            if (edgeStartIndex == -1 || edgeEndIndex == -1)
                            {
                                continue;
                            }

                            if (lookup[edgeStartIndex] == lookup[localEdge.a] &&
                                lookup[edgeEndIndex] == lookup[localEdge.b])
                            {
                                orderedVertices.InsertRange(i + 1, verticesToAppend);
                                for (int j = 0; j < count; j++)
                                {
                                    orderedSharedIndexes.Insert(i + j + 1, sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                            else if (lookup[edgeStartIndex] == lookup[localEdge.b] &&
                                     lookup[edgeEndIndex] == lookup[localEdge.a])
                            {
                                verticesToAppend.Reverse();
                                orderedVertices.InsertRange(i + 1, verticesToAppend);
                                for (int j = count - 1; j >= 0; j--)
                                {
                                    orderedSharedIndexes.Insert(i + 1, sharedIndexesCount + j);
                                    orderedSharedUVIndexes.Add(-1);
                                }
                            }
                        }

                        data.vertices        = orderedVertices;
                        data.sharedIndexes   = orderedSharedIndexes;
                        data.sharedIndexesUV = orderedSharedUVIndexes;
                    }
                }

                sharedIndexesCount += count;
            }

            // now apply the changes
            List <Face>            dic_face      = modifiedFaces.Keys.ToList();
            List <FaceRebuildData> dic_data      = modifiedFaces.Values.ToList();
            List <EdgeLookup>      appendedEdges = new List <EdgeLookup>();

            for (int i = 0; i < dic_face.Count; i++)
            {
                Face            face = dic_face[i];
                FaceRebuildData data = dic_data[i];

                int vertexCount = vertices.Count;
                // triangulate and set new face indexes to end of current vertex list
                List <int> triangles;

                if (Triangulation.TriangulateVertices(data.vertices, out triangles, false))
                {
                    data.face = new Face(triangles);
                }
                else
                {
                    continue;
                }

                data.face.ShiftIndexes(vertexCount);
                face.CopyFrom(data.face);

                for (int n = 0; n < data.vertices.Count; n++)
                {
                    lookup.Add(vertexCount + n, data.sharedIndexes[n]);
                }

                if (data.sharedIndexesUV.Count == data.vertices.Count)
                {
                    for (int n = 0; n < data.vertices.Count; n++)
                    {
                        lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
                    }
                }

                vertices.AddRange(data.vertices);

                foreach (Edge e in face.edgesInternal)
                {
                    EdgeLookup el = new EdgeLookup(new Edge(lookup[e.a], lookup[e.b]), e);

                    if (el.common.a >= originalSharedIndexesCount || el.common.b >= originalSharedIndexesCount)
                    {
                        appendedEdges.Add(el);
                    }
                }
            }

            indexesToDelete = indexesToDelete.Distinct().ToList();
            int delCount = indexesToDelete.Count;

            var newEdges = appendedEdges.Distinct().Select(x => x.local - delCount).ToList();

            mesh.SetVertices(vertices);
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);
            mesh.DeleteVertices(indexesToDelete);

            return(newEdges);
        }
Example #2
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="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>
        /// Add a set of points to a face and retriangulate. Points are added to the nearest edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="face">The face to append points to.</param>
        /// <param name="points">Points to added to the face.</param>
        /// <returns>The face created by appending the points.</returns>
        public static Face AppendVerticesToFace(this ProBuilderMesh mesh, Face face, Vector3[] points)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (face == null)
            {
                throw new ArgumentNullException("face");
            }

            if (points == null)
            {
                throw new ArgumentNullException("points");
            }

            List <Vertex>         vertices = mesh.GetVertices().ToList();
            List <Face>           faces    = new List <Face>(mesh.facesInternal);
            Dictionary <int, int> lookup   = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV = null;

            if (mesh.sharedTextures != null)
            {
                lookupUV = new Dictionary <int, int>();
                SharedVertex.GetSharedVertexLookup(mesh.sharedTextures, lookupUV);
            }

            List <Edge> wound = WingedEdge.SortEdgesByAdjacency(face);

            List <Vertex> n_vertices = new List <Vertex>();
            List <int>    n_shared   = new List <int>();
            List <int>    n_sharedUV = lookupUV != null ? new List <int>() : null;

            for (int i = 0; i < wound.Count; i++)
            {
                n_vertices.Add(vertices[wound[i].a]);
                n_shared.Add(lookup[wound[i].a]);

                if (lookupUV != null)
                {
                    int uv;

                    if (lookupUV.TryGetValue(wound[i].a, out uv))
                    {
                        n_sharedUV.Add(uv);
                    }
                    else
                    {
                        n_sharedUV.Add(-1);
                    }
                }
            }

            // now insert the new points on the nearest edge
            for (int i = 0; i < points.Length; i++)
            {
                int     index = -1;
                float   best  = Mathf.Infinity;
                Vector3 p     = points[i];
                int     vc    = n_vertices.Count;

                for (int n = 0; n < vc; n++)
                {
                    Vector3 v = n_vertices[n].position;
                    Vector3 w = n_vertices[(n + 1) % vc].position;

                    float dist = Math.DistancePointLineSegment(p, v, w);

                    if (dist < best)
                    {
                        best  = dist;
                        index = n;
                    }
                }

                Vertex left = n_vertices[index], right = n_vertices[(index + 1) % vc];

                float x = (p - left.position).sqrMagnitude;
                float y = (p - right.position).sqrMagnitude;

                Vertex insert = Vertex.Mix(left, right, x / (x + y));

                n_vertices.Insert((index + 1) % vc, insert);
                n_shared.Insert((index + 1) % vc, -1);
                if (n_sharedUV != null)
                {
                    n_sharedUV.Insert((index + 1) % vc, -1);
                }
            }

            List <int> triangles;

            try
            {
                Triangulation.TriangulateVertices(n_vertices, out triangles, false);
            }
            catch
            {
                Debug.Log("Failed triangulating face after appending vertices.");
                return(null);
            }

            FaceRebuildData data = new FaceRebuildData();

            data.face = new Face(triangles.ToArray(), face.submeshIndex, new AutoUnwrapSettings(face.uv),
                                 face.smoothingGroup, face.textureGroup, -1, face.manualUV);
            data.vertices        = n_vertices;
            data.sharedIndexes   = n_shared;
            data.sharedIndexesUV = n_sharedUV;

            FaceRebuildData.Apply(new List <FaceRebuildData>()
            {
                data
            },
                                  vertices,
                                  faces,
                                  lookup,
                                  lookupUV);

            var newFace = data.face;

            mesh.SetVertices(vertices);
            mesh.faces = faces;
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);

            // check old normal and make sure this new face is pointing the same direction
            Vector3 oldNrm = Math.Normal(mesh, face);
            Vector3 newNrm = Math.Normal(mesh, newFace);

            if (Vector3.Dot(oldNrm, newNrm) < 0)
            {
                newFace.Reverse();
            }

            mesh.DeleteFace(face);

            return(newFace);
        }
Example #4
0
        /// <summary>
        /// Insert a number of new points to each edge. Points are evenly spaced out along the edge.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="edges">The edges to split with points.</param>
        /// <param name="count">The number of new points to insert. Must be greater than 0.</param>
        /// <returns>The new edges created by inserting points.</returns>
        public static List <Edge> AppendVerticesToEdge(this ProBuilderMesh mesh, IList <Edge> edges, int count)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            if (edges == null)
            {
                throw new ArgumentNullException("edges");
            }

            if (count < 1 || count > 512)
            {
                Log.Error("New edge vertex count is less than 1 or greater than 512.");
                return(null);
            }

            List <Vertex>         vertices        = new List <Vertex>(mesh.GetVertices());
            Dictionary <int, int> lookup          = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV        = mesh.sharedTextureLookup;
            List <int>            indexesToDelete = new List <int>();
            IEnumerable <Edge>    commonEdges     = EdgeUtility.GetSharedVertexHandleEdges(mesh, edges);
            List <Edge>           distinctEdges   = commonEdges.Distinct().ToList();

            Dictionary <Face, FaceRebuildData> modifiedFaces = new Dictionary <Face, FaceRebuildData>();

            int originalSharedIndexesCount = lookup.Count();
            int sharedIndexesCount         = originalSharedIndexesCount;

            foreach (Edge edge in distinctEdges)
            {
                Edge localEdge = EdgeUtility.GetEdgeWithSharedVertexHandles(mesh, edge);

                // Generate the new vertices that will be inserted on this edge
                List <Vertex> verticesToAppend = new List <Vertex>(count);

                for (int i = 0; i < count; i++)
                {
                    verticesToAppend.Add(Vertex.Mix(vertices[localEdge.a], vertices[localEdge.b], (i + 1) / ((float)count + 1)));
                }

                List <SimpleTuple <Face, Edge> > adjacentFaces = ElementSelection.GetNeighborFaces(mesh, localEdge);

                // foreach face attached to common edge, append vertices
                foreach (SimpleTuple <Face, Edge> tup in adjacentFaces)
                {
                    Face face = tup.item1;

                    FaceRebuildData data;

                    if (!modifiedFaces.TryGetValue(face, out data))
                    {
                        data                 = new FaceRebuildData();
                        data.face            = new Face(new int[0], face.submeshIndex, new AutoUnwrapSettings(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV);
                        data.vertices        = new List <Vertex>(ArrayUtility.ValuesWithIndexes(vertices, face.distinctIndexesInternal));
                        data.sharedIndexes   = new List <int>();
                        data.sharedIndexesUV = new List <int>();

                        foreach (int i in face.distinctIndexesInternal)
                        {
                            int shared;

                            if (lookup.TryGetValue(i, out shared))
                            {
                                data.sharedIndexes.Add(shared);
                            }

                            if (lookupUV.TryGetValue(i, out shared))
                            {
                                data.sharedIndexesUV.Add(shared);
                            }
                        }

                        indexesToDelete.AddRange(face.distinctIndexesInternal);

                        modifiedFaces.Add(face, data);
                    }

                    data.vertices.AddRange(verticesToAppend);

                    for (int i = 0; i < count; i++)
                    {
                        data.sharedIndexes.Add(sharedIndexesCount + i);
                        data.sharedIndexesUV.Add(-1);
                    }
                }

                sharedIndexesCount += count;
            }

            // now apply the changes
            List <Face>            dic_face      = modifiedFaces.Keys.ToList();
            List <FaceRebuildData> dic_data      = modifiedFaces.Values.ToList();
            List <EdgeLookup>      appendedEdges = new List <EdgeLookup>();

            for (int i = 0; i < dic_face.Count; i++)
            {
                Face            face = dic_face[i];
                FaceRebuildData data = dic_data[i];

                Vector3   nrm        = Math.Normal(mesh, face);
                Vector2[] projection = Projection.PlanarProject(data.vertices.Select(x => x.position).ToArray(), null, nrm);

                int vertexCount = vertices.Count;

                // triangulate and set new face indexes to end of current vertex list
                List <int> indexes;

                if (Triangulation.SortAndTriangulate(projection, out indexes))
                {
                    data.face.indexesInternal = indexes.ToArray();
                }
                else
                {
                    continue;
                }

                data.face.ShiftIndexes(vertexCount);
                face.CopyFrom(data.face);

                for (int n = 0; n < data.vertices.Count; n++)
                {
                    lookup.Add(vertexCount + n, data.sharedIndexes[n]);
                }

                if (data.sharedIndexesUV.Count == data.vertices.Count)
                {
                    for (int n = 0; n < data.vertices.Count; n++)
                    {
                        lookupUV.Add(vertexCount + n, data.sharedIndexesUV[n]);
                    }
                }

                vertices.AddRange(data.vertices);

                foreach (Edge e in face.edgesInternal)
                {
                    EdgeLookup el = new EdgeLookup(new Edge(lookup[e.a], lookup[e.b]), e);

                    if (el.common.a >= originalSharedIndexesCount || el.common.b >= originalSharedIndexesCount)
                    {
                        appendedEdges.Add(el);
                    }
                }
            }

            indexesToDelete = indexesToDelete.Distinct().ToList();
            int delCount = indexesToDelete.Count;

            var newEdges = appendedEdges.Distinct().Select(x => x.local - delCount).ToList();

            mesh.SetVertices(vertices);
            mesh.SetSharedVertices(lookup);
            mesh.SetSharedTextures(lookupUV);
            mesh.DeleteVertices(indexesToDelete);

            return(newEdges);
        }