using UnityEngine; using System.Collections.Generic; using System.Linq; using UnityEngine.ProBuilder; namespace UnityEngine.ProBuilder.MeshOperations { /// /// Methods for merging multiple faces of a to a single face. /// public static class MergeElements { /// /// Merge each pair of faces to a single face. Indexes are combined, but otherwise the properties of the first face in the pair take precedence. Returns a list of the new faces created. /// /// /// /// /// public static List MergePairs(ProBuilderMesh target, IEnumerable> pairs, bool collapseCoincidentVertices = true) { HashSet remove = new HashSet(); List add = new List(); foreach (SimpleTuple pair in pairs) { Face left = pair.item1; Face right = pair.item2; int leftLength = left.indexesInternal.Length; int rightLength = right.indexesInternal.Length; int[] indexes = new int[leftLength + rightLength]; System.Array.Copy(left.indexesInternal, 0, indexes, 0, leftLength); System.Array.Copy(right.indexesInternal, 0, indexes, leftLength, rightLength); add.Add(new Face(indexes, left.submeshIndex, left.uv, left.smoothingGroup, left.textureGroup, left.elementGroup, left.manualUV)); remove.Add(left); remove.Add(right); } List faces = target.facesInternal.Where(x => !remove.Contains(x)).ToList(); faces.AddRange(add); target.faces = faces; if (collapseCoincidentVertices) CollapseCoincidentVertices(target, add); return add; } /// /// Merge a collection of faces to a single face. This function does not /// perform any sanity checks, it just merges faces. It's the caller's /// responsibility to make sure that the input is valid. /// In addition to merging faces this method also removes duplicate vertices /// created as a result of merging previously common vertices. /// /// /// /// public static Face Merge(ProBuilderMesh target, IEnumerable faces) { int mergedCount = faces != null ? faces.Count() : 0; if (mergedCount < 1) return null; Face first = faces.First(); Face mergedFace = new Face(faces.SelectMany(x => x.indexesInternal).ToArray(), first.submeshIndex, first.uv, first.smoothingGroup, first.textureGroup, first.elementGroup, first.manualUV); Face[] rebuiltFaces = new Face[target.facesInternal.Length - mergedCount + 1]; int n = 0; HashSet skip = new HashSet(faces); foreach (Face f in target.facesInternal) { if (!skip.Contains(f)) rebuiltFaces[n++] = f; } rebuiltFaces[n] = mergedFace; target.faces = rebuiltFaces; CollapseCoincidentVertices(target, new Face[] { mergedFace }); return mergedFace; } /// /// Condense co-incident vertex positions per-face. vertices must already be marked as shared in the sharedIndexes /// array to be considered. This method is really only useful after merging faces. /// /// /// internal static void CollapseCoincidentVertices(ProBuilderMesh mesh, IEnumerable faces) { Dictionary lookup = mesh.sharedVertexLookup; Dictionary matches = new Dictionary(); foreach (Face face in faces) { matches.Clear(); for (int i = 0; i < face.indexesInternal.Length; i++) { int common = lookup[face.indexesInternal[i]]; if (matches.ContainsKey(common)) face.indexesInternal[i] = matches[common]; else matches.Add(common, face.indexesInternal[i]); } face.InvalidateCache(); } MeshValidation.RemoveUnusedVertices(mesh); } } }