/// <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); }
/// <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)); }