Ejemplo n.º 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);

                // 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.º 2
0
        /// <summary>
        /// Insert a face between two edges.
        /// </summary>
        /// <param name="mesh">The source mesh.</param>
        /// <param name="a">First edge.</param>
        /// <param name="b">Second edge</param>
        /// <param name="allowNonManifoldGeometry">If true, this function will allow edges to be bridged that create overlapping (non-manifold) faces.</param>
        /// <returns>The new face, or null of the action failed.</returns>
        public static Face Bridge(this ProBuilderMesh mesh, Edge a, Edge b, bool allowNonManifoldGeometry = false)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            SharedVertex[]        sharedVertices = mesh.sharedVerticesInternal;
            Dictionary <int, int> lookup         = mesh.sharedVertexLookup;

            // Check to see if a face already exists
            if (!allowNonManifoldGeometry)
            {
                if (ElementSelection.GetNeighborFaces(mesh, a).Count > 1 || ElementSelection.GetNeighborFaces(mesh, b).Count > 1)
                {
                    return(null);
                }
            }

            foreach (Face face in mesh.facesInternal)
            {
                if (mesh.IndexOf(face.edgesInternal, a) >= 0 && mesh.IndexOf(face.edgesInternal, b) >= 0)
                {
                    Log.Warning("Face already exists between these two edges!");
                    return(null);
                }
            }

            Vector3[] positions = mesh.positionsInternal;
            bool      hasColors = mesh.HasArrays(MeshArrays.Color);

            Color[] colors = hasColors ? mesh.colorsInternal : null;

            Vector3[]          v;
            Color[]            c;
            int[]              s;
            AutoUnwrapSettings uvs = AutoUnwrapSettings.tile;
            int submeshIndex       = 0;

            // Get material and UV stuff from the first edge face
            SimpleTuple <Face, Edge> faceAndEdge;

            if (EdgeUtility.ValidateEdge(mesh, a, out faceAndEdge) || EdgeUtility.ValidateEdge(mesh, b, out faceAndEdge))
            {
                uvs          = new AutoUnwrapSettings(faceAndEdge.item1.uv);
                submeshIndex = faceAndEdge.item1.submeshIndex;
            }

            // Bridge will form a triangle
            if (a.Contains(b.a, lookup) || a.Contains(b.b, lookup))
            {
                v = new Vector3[3];
                c = new Color[3];
                s = new int[3];

                bool axbx = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.a)].arrayInternal, b.a) > -1;
                bool axby = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.a)].arrayInternal, b.b) > -1;

                bool aybx = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.b)].arrayInternal, b.a) > -1;
                bool ayby = Array.IndexOf(sharedVertices[mesh.GetSharedVertexHandle(a.b)].arrayInternal, b.b) > -1;

                if (axbx)
                {
                    v[0] = positions[a.a];
                    if (hasColors)
                    {
                        c[0] = colors[a.a];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.a);
                    v[1] = positions[a.b];
                    if (hasColors)
                    {
                        c[1] = colors[a.b];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.b);
                    v[2] = positions[b.b];
                    if (hasColors)
                    {
                        c[2] = colors[b.b];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.b);
                }
                else if (axby)
                {
                    v[0] = positions[a.a];
                    if (hasColors)
                    {
                        c[0] = colors[a.a];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.a);
                    v[1] = positions[a.b];
                    if (hasColors)
                    {
                        c[1] = colors[a.b];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.b);
                    v[2] = positions[b.a];
                    if (hasColors)
                    {
                        c[2] = colors[b.a];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.a);
                }
                else if (aybx)
                {
                    v[0] = positions[a.b];
                    if (hasColors)
                    {
                        c[0] = colors[a.b];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.b);
                    v[1] = positions[a.a];
                    if (hasColors)
                    {
                        c[1] = colors[a.a];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.a);
                    v[2] = positions[b.b];
                    if (hasColors)
                    {
                        c[2] = colors[b.b];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.b);
                }
                else if (ayby)
                {
                    v[0] = positions[a.b];
                    if (hasColors)
                    {
                        c[0] = colors[a.b];
                    }
                    s[0] = mesh.GetSharedVertexHandle(a.b);
                    v[1] = positions[a.a];
                    if (hasColors)
                    {
                        c[1] = colors[a.a];
                    }
                    s[1] = mesh.GetSharedVertexHandle(a.a);
                    v[2] = positions[b.a];
                    if (hasColors)
                    {
                        c[2] = colors[b.a];
                    }
                    s[2] = mesh.GetSharedVertexHandle(b.a);
                }

                return(mesh.AppendFace(
                           v,
                           hasColors ? c : null,
                           new Vector2[v.Length],
                           new Face(axbx || axby ? new int[3] {
                    2, 1, 0
                } : new int[3] {
                    0, 1, 2
                }, submeshIndex, uvs, 0, -1, -1, false),
                           s));
            }

            // Else, bridge will form a quad

            v = new Vector3[4];
            c = new Color[4];
            s = new int[4]; // shared indexes index to add to

            v[0] = positions[a.a];
            if (hasColors)
            {
                c[0] = mesh.colorsInternal[a.a];
            }
            s[0] = mesh.GetSharedVertexHandle(a.a);
            v[1] = positions[a.b];
            if (hasColors)
            {
                c[1] = mesh.colorsInternal[a.b];
            }
            s[1] = mesh.GetSharedVertexHandle(a.b);

            Vector3 nrm = Vector3.Cross(positions[b.a] - positions[a.a], positions[a.b] - positions[a.a]).normalized;

            Vector2[] planed = Projection.PlanarProject(new Vector3[4] {
                positions[a.a], positions[a.b], positions[b.a], positions[b.b]
            }, null, nrm);

            Vector2 ipoint     = Vector2.zero;
            bool    intersects = Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);

            if (!intersects)
            {
                v[2] = positions[b.a];
                if (hasColors)
                {
                    c[2] = mesh.colorsInternal[b.a];
                }
                s[2] = mesh.GetSharedVertexHandle(b.a);
                v[3] = positions[b.b];
                if (hasColors)
                {
                    c[3] = mesh.colorsInternal[b.b];
                }
                s[3] = mesh.GetSharedVertexHandle(b.b);
            }
            else
            {
                v[2] = positions[b.b];
                if (hasColors)
                {
                    c[2] = mesh.colorsInternal[b.b];
                }
                s[2] = mesh.GetSharedVertexHandle(b.b);
                v[3] = positions[b.a];
                if (hasColors)
                {
                    c[3] = mesh.colorsInternal[b.a];
                }
                s[3] = mesh.GetSharedVertexHandle(b.a);
            }

            return(mesh.AppendFace(
                       v,
                       hasColors ? c : null,
                       new Vector2[v.Length],
                       new Face(new int[6] {
                2, 1, 0, 2, 3, 1
            }, submeshIndex, uvs, 0, -1, -1, false),
                       s));
        }