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); }
/// <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 = InsertVertices(face, targetEdges, vertices); 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); } 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))); }