Ejemplo n.º 1
0
        /// <summary>
        /// Given a path of vertices, inserts a new vertex in the center inserts triangles along the path.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        internal static List <FaceRebuildData> TentCapWithVertices(List <Vertex> path)
        {
            int    count  = path.Count;
            Vertex center = Vertex.Average(path);
            List <FaceRebuildData> faces = new List <FaceRebuildData>();

            for (int i = 0; i < count; i++)
            {
                List <Vertex> vertices = new List <Vertex>()
                {
                    path[i],
                    center,
                    path[(i + 1) % count]
                };

                FaceRebuildData data = new FaceRebuildData();
                data.vertices = vertices;
                data.face     = new Face(new int[] { 0, 1, 2 });

                faces.Add(data);
            }

            return(faces);
        }
Ejemplo n.º 2
0
        static bool InsertVertices(Face face, List <WingedEdge> edges, List <Vertex> vertices, out ConnectFaceRebuildData data)
        {
            List <Edge>    perimeter        = WingedEdge.SortEdgesByAdjacency(face);
            List <Vertex>  n_vertices       = new List <Vertex>();
            List <int>     newVertexIndexes = new List <int>();
            HashSet <Edge> affected         = new HashSet <Edge>(edges.Select(x => x.edge.local));

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

                if (affected.Contains(perimeter[i]))
                {
                    newVertexIndexes.Add(n_vertices.Count);
                    n_vertices.Add(Vertex.Mix(vertices[perimeter[i].a], vertices[perimeter[i].b], .5f));
                }
            }

            FaceRebuildData res = AppendElements.FaceWithVertices(n_vertices, false);

            if (res != null)
            {
                res.face.textureGroup   = face.textureGroup;
                res.face.uv             = new AutoUnwrapSettings(face.uv);
                res.face.smoothingGroup = face.smoothingGroup;
                res.face.manualUV       = face.manualUV;
                res.face.submeshIndex   = face.submeshIndex;

                data = new ConnectFaceRebuildData(res, newVertexIndexes);
                return(true);
            }

            data = null;

            return(false);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Apply a bevel to a set of edges.
        /// </summary>
        /// <param name="mesh">Target mesh.</param>
        /// <param name="edges">A set of edges to apply bevelling to.</param>
        /// <param name="amount">A value from 0 (bevel not at all) to 1 (bevel entire face).</param>
        /// <returns>The new faces created to form the bevel.</returns>
        public static List <Face> BevelEdges(ProBuilderMesh mesh, IList <Edge> edges, float amount)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            Dictionary <int, int>  lookup      = mesh.sharedVertexLookup;
            List <Vertex>          vertices    = new List <Vertex>(mesh.GetVertices());
            List <EdgeLookup>      m_edges     = EdgeLookup.GetEdgeLookup(edges, lookup).Distinct().ToList();
            List <WingedEdge>      wings       = WingedEdge.GetWingedEdges(mesh);
            List <FaceRebuildData> appendFaces = new List <FaceRebuildData>();

            Dictionary <Face, List <int> > ignore = new Dictionary <Face, List <int> >();
            HashSet <int> slide   = new HashSet <int>();
            int           beveled = 0;

            Dictionary <int, List <SimpleTuple <FaceRebuildData, List <int> > > > holes = new Dictionary <int, List <SimpleTuple <FaceRebuildData, List <int> > > >();

            // test every edge that will be moved along to make sure the bevel distance is appropriate.  if it's not, adjust the max bevel amount
            // to suit.
            Dictionary <int, List <WingedEdge> > spokes = WingedEdge.GetSpokes(wings);
            HashSet <int> tested_common = new HashSet <int>();

            foreach (EdgeLookup e in m_edges)
            {
                if (tested_common.Add(e.common.a))
                {
                    foreach (WingedEdge w in spokes[e.common.a])
                    {
                        Edge le = w.edge.local;
                        amount = Mathf.Min(Vector3.Distance(vertices[le.a].position, vertices[le.b].position) - .001f, amount);
                    }
                }

                if (tested_common.Add(e.common.b))
                {
                    foreach (WingedEdge w in spokes[e.common.b])
                    {
                        Edge le = w.edge.local;
                        amount = Mathf.Min(Vector3.Distance(vertices[le.a].position, vertices[le.b].position) - .001f, amount);
                    }
                }
            }

            if (amount < .001f)
            {
                Log.Info("Bevel Distance > Available Surface");
                return(null);
            }

            // iterate selected edges and move each leading edge back along it's direction
            // storing information about adjacent faces in the process
            foreach (EdgeLookup lup in m_edges)
            {
                WingedEdge we = wings.FirstOrDefault(x => x.edge.Equals(lup));

                if (we == null || we.opposite == null)
                {
                    continue;
                }

                beveled++;

                ignore.AddOrAppend(we.face, we.edge.common.a);
                ignore.AddOrAppend(we.face, we.edge.common.b);
                ignore.AddOrAppend(we.opposite.face, we.edge.common.a);
                ignore.AddOrAppend(we.opposite.face, we.edge.common.b);

                // after initial slides go back and split indirect triangles at the intersecting index into two vertices
                slide.Add(we.edge.common.a);
                slide.Add(we.edge.common.b);

                SlideEdge(vertices, we, amount);
                SlideEdge(vertices, we.opposite, amount);

                appendFaces.AddRange(GetBridgeFaces(vertices, we, we.opposite, holes));
            }

            if (beveled < 1)
            {
                Log.Info("Cannot Bevel Open Edges");
                return(null);
            }

            // grab the "createdFaces" array now so that the selection returned is just the bridged faces
            // then add holes later
            var createdFaces = new List <Face>(appendFaces.Select(x => x.face));

            Dictionary <Face, List <SimpleTuple <WingedEdge, int> > > sorted = new Dictionary <Face, List <SimpleTuple <WingedEdge, int> > >();

            // sort the adjacent but affected faces into winged edge groups where each group contains a set of
            // unique winged edges pointing to the same face
            foreach (int c in slide)
            {
                IEnumerable <WingedEdge> matches = wings.Where(x => x.edge.common.Contains(c) && !(ignore.ContainsKey(x.face) && ignore[x.face].Contains(c)));

                HashSet <Face> used = new HashSet <Face>();

                foreach (WingedEdge match in matches)
                {
                    if (!used.Add(match.face))
                    {
                        continue;
                    }

                    sorted.AddOrAppend(match.face, new SimpleTuple <WingedEdge, int>(match, c));
                }
            }

            // now go through those sorted faces and apply the vertex exploding, keeping track of any holes created
            foreach (KeyValuePair <Face, List <SimpleTuple <WingedEdge, int> > > kvp in sorted)
            {
                // common index & list of vertices it was split into
                Dictionary <int, List <int> > appended;

                FaceRebuildData f = VertexEditing.ExplodeVertex(vertices, kvp.Value, amount, out appended);

                if (f == null)
                {
                    continue;
                }

                appendFaces.Add(f);

                foreach (var apv in appended)
                {
                    // organize holes by new face so that later we can compare the winding of the new face to the hole face
                    // holes are sorted by key: common index value: face, vertex list
                    holes.AddOrAppend(apv.Key, new SimpleTuple <FaceRebuildData, List <int> >(f, apv.Value));
                }
            }

            FaceRebuildData.Apply(appendFaces, mesh, vertices);
            int removed = mesh.DeleteFaces(sorted.Keys).Length;

            mesh.sharedTextures = new SharedVertex[0];
            mesh.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(mesh.positionsInternal);

            // @todo don't rebuild indexes, keep 'em cached
            SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
            lookup = mesh.sharedVertexLookup;
            List <HashSet <int> > holesCommonIndexes = new List <HashSet <int> >();

            // offset the indexes of holes and cull any potential holes that are less than 3 indexes (not a hole :)
            foreach (KeyValuePair <int, List <SimpleTuple <FaceRebuildData, List <int> > > > hole in holes)
            {
                // less than 3 indexes in hole path; ain't a hole
                if (hole.Value.Sum(x => x.item2.Count) < 3)
                {
                    continue;
                }

                HashSet <int> holeCommon = new HashSet <int>();

                foreach (SimpleTuple <FaceRebuildData, List <int> > path in hole.Value)
                {
                    int offset = path.item1.Offset() - removed;

                    for (int i = 0; i < path.item2.Count; i++)
                    {
                        holeCommon.Add(lookup[path.item2[i] + offset]);
                    }
                }

                holesCommonIndexes.Add(holeCommon);
            }

            List <WingedEdge> modified = WingedEdge.GetWingedEdges(mesh, appendFaces.Select(x => x.face));

            // now go through the holes and create faces for them
            vertices = new List <Vertex>(mesh.GetVertices());

            List <FaceRebuildData> holeFaces = new List <FaceRebuildData>();

            foreach (HashSet <int> h in holesCommonIndexes)
            {
                // even if a set of hole indexes made it past the initial culling, the distinct part
                // may have reduced the index count
                if (h.Count < 3)
                {
                    continue;
                }
                // skip sorting the path if it's just a triangle
                if (h.Count < 4)
                {
                    List <Vertex> v = new List <Vertex>(mesh.GetVertices(h.Select(x => sharedIndexes[x][0]).ToList()));
                    holeFaces.Add(AppendElements.FaceWithVertices(v));
                }
                // if this hole has > 3 indexes, it needs a tent pole triangulation, which requires sorting into the perimeter order
                else
                {
                    List <int>    holePath = WingedEdge.SortCommonIndexesByAdjacency(modified, h);
                    List <Vertex> v        = new List <Vertex>(mesh.GetVertices(holePath.Select(x => sharedIndexes[x][0]).ToList()));
                    holeFaces.AddRange(AppendElements.TentCapWithVertices(v));
                }
            }

            FaceRebuildData.Apply(holeFaces, mesh, vertices);
            mesh.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(mesh.positionsInternal);

            // go through new faces and conform hole normals
            // get a hash of just the adjacent and bridge faces
            // HashSet<pb_Face> adjacent = new HashSet<pb_Face>(appendFaces.Select(x => x.face));
            // and also just the filled holes
            HashSet <Face> newHoles = new HashSet <Face>(holeFaces.Select(x => x.face));

            // now append filled holes to the full list of added faces
            appendFaces.AddRange(holeFaces);

            List <WingedEdge> allNewFaceEdges = WingedEdge.GetWingedEdges(mesh, appendFaces.Select(x => x.face));

            for (int i = 0; i < allNewFaceEdges.Count && newHoles.Count > 0; i++)
            {
                WingedEdge wing = allNewFaceEdges[i];

                if (newHoles.Contains(wing.face))
                {
                    newHoles.Remove(wing.face);

                    // find first edge whose opposite face isn't a filled hole* then
                    // conform normal by that.
                    // *or is a filled hole but has already been conformed
                    using (var it = new WingedEdgeEnumerator(wing))
                    {
                        while (it.MoveNext())
                        {
                            var w = it.Current;

                            if (!newHoles.Contains(w.opposite.face))
                            {
                                w.face.submeshIndex = w.opposite.face.submeshIndex;
                                w.face.uv           = new AutoUnwrapSettings(w.opposite.face.uv);
                                SurfaceTopology.ConformOppositeNormal(w.opposite);
                                break;
                            }
                        }
                    }
                }
            }

            mesh.ToMesh();

            return(createdFaces);
        }
        /// <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);
        }
        /// <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);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Inserts edges connecting a list of indices.
        ///
        /// This is the equivalent of the [Connect Edges](../manual/Edge_Connect.html) action.
        /// </summary>
        /// <param name="mesh">The target mesh.</param>
        /// <param name="indexes">A list of indices (corresponding to the <see cref="ProBuilderMesh.positions"/> array) to connect to the new edges.</param>
        /// <returns>A new array containing the indices of the newly connected positions. This method rebuilds the `indexes` array because it might modify the ordering of the original array.</returns>
        public static int[] Connect(this ProBuilderMesh mesh, IList <int> indexes)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

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

            int sharedIndexOffset        = mesh.sharedVerticesInternal.Length;
            Dictionary <int, int> lookup = mesh.sharedVertexLookup;

            HashSet <int> distinct = new HashSet <int>(indexes.Select(x => lookup[x]));
            HashSet <int> affected = new HashSet <int>();

            foreach (int i in distinct)
            {
                affected.UnionWith(mesh.sharedVerticesInternal[i].arrayInternal);
            }

            Dictionary <Face, List <int> > splits = new Dictionary <Face, List <int> >();
            List <Vertex> vertices = new List <Vertex>(mesh.GetVertices());

            foreach (Face face in mesh.facesInternal)
            {
                int[] f = face.distinctIndexesInternal;

                for (int i = 0; i < f.Length; i++)
                {
                    if (affected.Contains(f[i]))
                    {
                        splits.AddOrAppend(face, f[i]);
                    }
                }
            }

            List <ConnectFaceRebuildData> appendFaces = new List <ConnectFaceRebuildData>();
            List <Face>   successfulSplits            = new List <Face>();
            HashSet <int> usedTextureGroups           = new HashSet <int>(mesh.facesInternal.Select(x => x.textureGroup));
            int           newTextureGroupIndex        = 1;

            foreach (KeyValuePair <Face, List <int> > split in splits)
            {
                Face face = split.Key;

                List <ConnectFaceRebuildData> res = split.Value.Count == 2 ?
                                                    ConnectIndexesPerFace(face, split.Value[0], split.Value[1], vertices, lookup) :
                                                    ConnectIndexesPerFace(face, split.Value, vertices, lookup, sharedIndexOffset++);

                if (res == null)
                {
                    continue;
                }

                if (face.textureGroup < 0)
                {
                    while (usedTextureGroups.Contains(newTextureGroupIndex))
                    {
                        newTextureGroupIndex++;
                    }

                    usedTextureGroups.Add(newTextureGroupIndex);
                }

                foreach (ConnectFaceRebuildData c in res)
                {
                    c.faceRebuildData.face.textureGroup   = face.textureGroup < 0 ? newTextureGroupIndex : face.textureGroup;
                    c.faceRebuildData.face.uv             = new AutoUnwrapSettings(face.uv);
                    c.faceRebuildData.face.smoothingGroup = face.smoothingGroup;
                    c.faceRebuildData.face.manualUV       = face.manualUV;
                    c.faceRebuildData.face.submeshIndex   = face.submeshIndex;
                }

                successfulSplits.Add(face);
                appendFaces.AddRange(res);
            }

            FaceRebuildData.Apply(appendFaces.Select(x => x.faceRebuildData), mesh, vertices, null);
            int removedVertexCount = mesh.DeleteFaces(successfulSplits).Length;

            lookup = mesh.sharedVertexLookup;

            HashSet <int> newVertexIndexes = new HashSet <int>();

            for (int i = 0; i < appendFaces.Count; i++)
            {
                for (int n = 0; n < appendFaces[i].newVertexIndexes.Count; n++)
                {
                    newVertexIndexes.Add(lookup[appendFaces[i].newVertexIndexes[n] + (appendFaces[i].faceRebuildData.Offset() - removedVertexCount)]);
                }
            }

            mesh.ToMesh();

            return(newVertexIndexes.Select(x => mesh.sharedVerticesInternal[x][0]).ToArray());
        }
Ejemplo n.º 7
0
        static List <ConnectFaceRebuildData> ConnectIndexesPerFace(
            Face face,
            List <int> indexes,
            List <Vertex> vertices,
            Dictionary <int, int> lookup,
            int sharedIndexOffset)
        {
            if (indexes.Count < 3)
            {
                return(null);
            }

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

            int splitCount = indexes.Count;

            List <List <Vertex> > n_vertices      = ArrayUtility.Fill <List <Vertex> >(x => { return(new List <Vertex>()); }, splitCount);
            List <List <int> >    n_sharedIndexes = ArrayUtility.Fill <List <int> >(x => { return(new List <int>()); }, splitCount);
            List <List <int> >    n_indexes       = ArrayUtility.Fill <List <int> >(x => { return(new List <int>()); }, splitCount);

            Vertex  center = Vertex.Average(vertices, indexes);
            Vector3 nrm    = Math.Normal(vertices, face.indexesInternal);

            int index = 0;

            for (int i = 0; i < perimeter.Count; i++)
            {
                int cur = perimeter[i].a;

                n_vertices[index].Add(vertices[cur]);
                n_sharedIndexes[index].Add(lookup[cur]);

                if (indexes.Contains(cur))
                {
                    n_indexes[index].Add(n_vertices[index].Count);
                    n_vertices[index].Add(center);
                    n_sharedIndexes[index].Add(sharedIndexOffset);

                    index = (index + 1) % splitCount;

                    n_indexes[index].Add(n_vertices[index].Count);
                    n_vertices[index].Add(vertices[cur]);
                    n_sharedIndexes[index].Add(lookup[cur]);
                }
            }

            List <ConnectFaceRebuildData> faces = new List <ConnectFaceRebuildData>();

            for (int i = 0; i < n_vertices.Count; i++)
            {
                if (n_vertices[i].Count < 3)
                {
                    continue;
                }

                FaceRebuildData f = AppendElements.FaceWithVertices(n_vertices[i], false);
                f.sharedIndexes = n_sharedIndexes[i];

                Vector3 fn = Math.Normal(n_vertices[i], f.face.indexesInternal);

                if (Vector3.Dot(nrm, fn) < 0)
                {
                    f.face.Reverse();
                }

                faces.Add(new ConnectFaceRebuildData(f, n_indexes[i]));
            }

            return(faces);
        }
Ejemplo n.º 8
0
        static List <ConnectFaceRebuildData> ConnectIndexesPerFace(
            Face face,
            int a,
            int b,
            List <Vertex> vertices,
            Dictionary <int, int> lookup)
        {
            List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face);

            List <Vertex>[] n_vertices = new List <Vertex>[] {
                new List <Vertex>(),
                new List <Vertex>()
            };

            List <int>[] n_sharedIndexes = new List <int>[] {
                new List <int>(),
                new List <int>()
            };

            List <int>[] n_indexes = new List <int>[] {
                new List <int>(),
                new List <int>()
            };

            int index = 0;

            for (int i = 0; i < perimeter.Count; i++)
            {
                // trying to connect two vertices that are already connected
                if (perimeter[i].Contains(a) && perimeter[i].Contains(b))
                {
                    return(null);
                }

                int cur = perimeter[i].a;

                n_vertices[index].Add(vertices[cur]);
                n_sharedIndexes[index].Add(lookup[cur]);

                if (cur == a || cur == b)
                {
                    index = (index + 1) % 2;

                    n_indexes[index].Add(n_vertices[index].Count);
                    n_vertices[index].Add(vertices[cur]);
                    n_sharedIndexes[index].Add(lookup[cur]);
                }
            }

            List <ConnectFaceRebuildData> faces = new List <ConnectFaceRebuildData>();
            Vector3 nrm = Math.Normal(vertices, face.indexesInternal);

            for (int i = 0; i < n_vertices.Length; i++)
            {
                FaceRebuildData f = AppendElements.FaceWithVertices(n_vertices[i], false);
                f.sharedIndexes = n_sharedIndexes[i];

                Vector3 fn = Math.Normal(n_vertices[i], f.face.indexesInternal);

                if (Vector3.Dot(nrm, fn) < 0)
                {
                    f.face.Reverse();
                }

                faces.Add(new ConnectFaceRebuildData(f, n_indexes[i]));
            }

            return(faces);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Inserts new edges connecting the passed edges, optionally restricting new edge insertion to faces in faceMask.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="edges"></param>
        /// <param name="addedFaces"></param>
        /// <param name="connections"></param>
        /// <param name="returnFaces"></param>
        /// <param name="returnEdges"></param>
        /// <param name="faceMask"></param>
        /// <returns></returns>
        internal static ActionResult Connect(
            this ProBuilderMesh mesh,
            IEnumerable <Edge> edges,
            out Face[] addedFaces,
            out Edge[] connections,
            bool returnFaces        = false,
            bool returnEdges        = false,
            HashSet <Face> faceMask = null)
        {
            Dictionary <int, int> lookup        = mesh.sharedVertexLookup;
            Dictionary <int, int> lookupUV      = mesh.sharedTextureLookup;
            HashSet <EdgeLookup>  distinctEdges = new HashSet <EdgeLookup>(EdgeLookup.GetEdgeLookup(edges, lookup));
            List <WingedEdge>     wings         = WingedEdge.GetWingedEdges(mesh);

            // map each edge to a face so that we have a list of all touched faces with their to-be-subdivided edges
            Dictionary <Face, List <WingedEdge> > touched = new Dictionary <Face, List <WingedEdge> >();

            foreach (WingedEdge wing in wings)
            {
                if (distinctEdges.Contains(wing.edge))
                {
                    List <WingedEdge> faceEdges;
                    if (touched.TryGetValue(wing.face, out faceEdges))
                    {
                        faceEdges.Add(wing);
                    }
                    else
                    {
                        touched.Add(wing.face, new List <WingedEdge>()
                        {
                            wing
                        });
                    }
                }
            }

            Dictionary <Face, List <WingedEdge> > affected = new Dictionary <Face, List <WingedEdge> >();

            // weed out edges that won't actually connect to other edges (if you don't play ya' can't stay)
            foreach (KeyValuePair <Face, List <WingedEdge> > kvp in touched)
            {
                if (kvp.Value.Count <= 1)
                {
                    WingedEdge opp = kvp.Value[0].opposite;

                    if (opp == null)
                    {
                        continue;
                    }

                    List <WingedEdge> opp_list;

                    if (!touched.TryGetValue(opp.face, out opp_list))
                    {
                        continue;
                    }

                    if (opp_list.Count <= 1)
                    {
                        continue;
                    }
                }

                affected.Add(kvp.Key, kvp.Value);
            }

            List <Vertex> vertices = new List <Vertex>(mesh.GetVertices());
            List <ConnectFaceRebuildData> results = new List <ConnectFaceRebuildData>();
            // just the faces that where connected with > 1 edge
            List <Face> connectedFaces = new List <Face>();

            HashSet <int> usedTextureGroups    = new HashSet <int>(mesh.facesInternal.Select(x => x.textureGroup));
            int           newTextureGroupIndex = 1;

            // do the splits
            foreach (KeyValuePair <Face, List <WingedEdge> > split in affected)
            {
                Face face = split.Key;
                List <WingedEdge> targetEdges = split.Value;
                int     inserts = targetEdges.Count;
                Vector3 nrm     = Math.Normal(vertices, face.indexesInternal);

                if (inserts == 1 || (faceMask != null && !faceMask.Contains(face)))
                {
                    ConnectFaceRebuildData c;

                    if (InsertVertices(face, targetEdges, vertices, out c))
                    {
                        Vector3 fn = Math.Normal(c.faceRebuildData.vertices, c.faceRebuildData.face.indexesInternal);

                        if (Vector3.Dot(nrm, fn) < 0)
                        {
                            c.faceRebuildData.face.Reverse();
                        }

                        results.Add(c);
                    }
                }
                else if (inserts > 1)
                {
                    List <ConnectFaceRebuildData> res = inserts == 2 ?
                                                        ConnectEdgesInFace(face, targetEdges[0], targetEdges[1], vertices) :
                                                        ConnectEdgesInFace(face, targetEdges, vertices);

                    if (face.textureGroup < 0)
                    {
                        while (usedTextureGroups.Contains(newTextureGroupIndex))
                        {
                            newTextureGroupIndex++;
                        }

                        usedTextureGroups.Add(newTextureGroupIndex);
                    }


                    if (res == null)
                    {
                        connections = null;
                        addedFaces  = null;
                        return(new ActionResult(ActionResult.Status.Failure, "Unable to connect faces"));
                    }
                    else
                    {
                        foreach (ConnectFaceRebuildData c in res)
                        {
                            connectedFaces.Add(c.faceRebuildData.face);

                            Vector3 fn = Math.Normal(c.faceRebuildData.vertices,
                                                     c.faceRebuildData.face.indexesInternal);

                            if (Vector3.Dot(nrm, fn) < 0)
                            {
                                c.faceRebuildData.face.Reverse();
                            }

                            c.faceRebuildData.face.textureGroup =
                                face.textureGroup < 0 ? newTextureGroupIndex : face.textureGroup;
                            c.faceRebuildData.face.uv             = new AutoUnwrapSettings(face.uv);
                            c.faceRebuildData.face.submeshIndex   = face.submeshIndex;
                            c.faceRebuildData.face.smoothingGroup = face.smoothingGroup;
                            c.faceRebuildData.face.manualUV       = face.manualUV;
                        }

                        results.AddRange(res);
                    }
                }
            }

            FaceRebuildData.Apply(results.Select(x => x.faceRebuildData), mesh, vertices, null);

            mesh.sharedTextures = new SharedVertex[0];
            int removedVertexCount = mesh.DeleteFaces(affected.Keys).Length;

            mesh.sharedVertices = SharedVertex.GetSharedVerticesWithPositions(mesh.positionsInternal);
            mesh.ToMesh();

            // figure out where the new edges where inserted
            if (returnEdges)
            {
                // offset the newVertexIndexes by whatever the FaceRebuildData did so we can search for the new edges by index
                var appended = new HashSet <int>();

                for (int n = 0; n < results.Count; n++)
                {
                    for (int i = 0; i < results[n].newVertexIndexes.Count; i++)
                    {
                        appended.Add((results[n].newVertexIndexes[i] + results[n].faceRebuildData.Offset()) - removedVertexCount);
                    }
                }

                Dictionary <int, int>    lup          = mesh.sharedVertexLookup;
                IEnumerable <Edge>       newEdges     = results.SelectMany(x => x.faceRebuildData.face.edgesInternal).Where(x => appended.Contains(x.a) && appended.Contains(x.b));
                IEnumerable <EdgeLookup> distNewEdges = EdgeLookup.GetEdgeLookup(newEdges, lup);

                connections = distNewEdges.Distinct().Select(x => x.local).ToArray();
            }
            else
            {
                connections = null;
            }

            if (returnFaces)
            {
                addedFaces = connectedFaces.ToArray();
            }
            else
            {
                addedFaces = null;
            }

            return(new ActionResult(ActionResult.Status.Success, string.Format("Connected {0} Edges", results.Count / 2)));
        }
Ejemplo n.º 10
0
 public ConnectFaceRebuildData(FaceRebuildData faceRebuildData, List <int> newVertexIndexes)
 {
     this.faceRebuildData  = faceRebuildData;
     this.newVertexIndexes = newVertexIndexes;
 }
        /// <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);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Split a common index on a face into two vertices and slide each vertex backwards along it's feeding edge by distance.
        /// This method does not perform any input validation, so make sure edgeAndCommonIndex is distinct and all winged edges belong
        /// to the same face.
        ///<pre>
        /// `appendedVertices` is common index and a list of the new face indexes it was split into.
        ///
        /// _ _ _ _          _ _ _
        /// |              /
        /// |         ->   |
        /// |              |
        /// </pre>
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="edgeAndCommonIndex"></param>
        /// <param name="distance"></param>
        /// <param name="appendedVertices"></param>
        /// <returns></returns>
        internal static FaceRebuildData ExplodeVertex(
            IList <Vertex> vertices,
            IList <SimpleTuple <WingedEdge, int> > edgeAndCommonIndex,
            float distance,
            out Dictionary <int, List <int> > appendedVertices)
        {
            Face        face      = edgeAndCommonIndex.FirstOrDefault().item1.face;
            List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face);

            appendedVertices = new Dictionary <int, List <int> >();
            Vector3 oldNormal = Math.Normal(vertices, face.indexesInternal);

            // store local and common index of split points
            Dictionary <int, int> toSplit = new Dictionary <int, int>();

            foreach (SimpleTuple <WingedEdge, int> v in edgeAndCommonIndex)
            {
                if (v.item2 == v.item1.edge.common.a)
                {
                    toSplit.Add(v.item1.edge.local.a, v.item2);
                }
                else
                {
                    toSplit.Add(v.item1.edge.local.b, v.item2);
                }
            }

            int           pc         = perimeter.Count;
            List <Vertex> n_vertices = new List <Vertex>();

            for (int i = 0; i < pc; i++)
            {
                int index = perimeter[i].b;

                // split this index into two
                if (toSplit.ContainsKey(index))
                {
                    // a --- b --- c
                    Vertex a = vertices[perimeter[i].a];
                    Vertex b = vertices[perimeter[i].b];
                    Vertex c = vertices[perimeter[(i + 1) % pc].b];

                    Vertex leading_dir   = a - b;
                    Vertex following_dir = c - b;
                    leading_dir.Normalize();
                    following_dir.Normalize();

                    Vertex leading_insert   = vertices[index] + leading_dir * distance;
                    Vertex following_insert = vertices[index] + following_dir * distance;

                    appendedVertices.AddOrAppend(toSplit[index], n_vertices.Count);
                    n_vertices.Add(leading_insert);

                    appendedVertices.AddOrAppend(toSplit[index], n_vertices.Count);
                    n_vertices.Add(following_insert);
                }
                else
                {
                    n_vertices.Add(vertices[index]);
                }
            }

            List <int> triangles;

            if (Triangulation.TriangulateVertices(n_vertices, out triangles, false))
            {
                FaceRebuildData data = new FaceRebuildData();
                data.vertices = n_vertices;
                data.face     = new Face(face);

                Vector3 newNormal = Math.Normal(n_vertices, triangles);

                if (Vector3.Dot(oldNormal, newNormal) < 0f)
                {
                    triangles.Reverse();
                }

                data.face.indexesInternal = triangles.ToArray();

                return(data);
            }

            return(null);
        }