/// <summary> /// 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. /// </summary> /// <param name="mesh"></param> /// <param name="faces"></param> internal static void CollapseCoincidentVertices(ProBuilderMesh mesh, IEnumerable <Face> faces) { Dictionary <int, int> lookup = mesh.sharedVertexLookup; Dictionary <int, int> matches = new Dictionary <int, int>(); 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); }
public static int[] RemoveUnusedVertices(this ProBuilderMesh mesh) { List <int> removed = new List <int>(); MeshValidation.RemoveUnusedVertices(mesh, removed); return(removed.ToArray()); }
public static int[] RemoveDegenerateTriangles(this ProBuilderMesh mesh) { List <int> removed = new List <int>(); MeshValidation.RemoveDegenerateTriangles(mesh, removed); return(removed.ToArray()); }
/// <summary> /// Collapses all passed indexes to a single shared index. /// </summary> /// <remarks> /// Retains vertex normals. /// </remarks> /// <param name="mesh">Target mesh.</param> /// <param name="indexes">The indexes to merge to a single shared vertex.</param> /// <param name="collapseToFirst">If true, instead of merging all vertices to the average position, the vertices will be collapsed onto the first vertex position.</param> /// <returns>The first available local index created as a result of the merge. -1 if action is unsuccessfull.</returns> public static int MergeVertices(this ProBuilderMesh mesh, int[] indexes, bool collapseToFirst = false) { if (mesh == null) { throw new ArgumentNullException("mesh"); } if (indexes == null) { throw new ArgumentNullException("indexes"); } Vertex[] vertices = mesh.GetVertices(); Vertex cen = collapseToFirst ? vertices[indexes[0]] : Vertex.Average(vertices, indexes); mesh.SetVerticesCoincident(indexes); UVEditing.SplitUVs(mesh, indexes); int sharedVertexHandle = mesh.GetSharedVertexHandle(indexes.First()); mesh.SetSharedVertexValues(sharedVertexHandle, cen); SharedVertex merged = mesh.sharedVerticesInternal[sharedVertexHandle]; List <int> removedIndexes = new List <int>(); MeshValidation.RemoveDegenerateTriangles(mesh, removedIndexes); // get a non-deleted index to work with int ind = -1; for (int i = 0; i < merged.Count; i++) { if (!removedIndexes.Contains(merged[i])) { ind = merged[i]; } } int res = ind; for (int i = 0; i < removedIndexes.Count; i++) { if (ind > removedIndexes[i]) { res--; } } return(res); }
/// <summary> /// Inserts new edges on a face starting from the center of each edge to a new vertex in the center of the face. /// /// This is the equivalent of the [Connect Edges](../manual/Edge_Connect.html) action. /// </summary> /// <param name="mesh">Target mesh.</param> /// <param name="faces">The faces to affect.</param> /// <returns>The faces created as a result of inserting new edges.</returns> public static Face[] Connect(this ProBuilderMesh mesh, IEnumerable <Face> faces) { var split = MeshValidation.EnsureFacesAreComposedOfContiguousTriangles(mesh, faces); HashSet <Face> mask = new HashSet <Face>(faces); if (split.Count > 0) { foreach (var face in split) { mask.Add(face); } } IEnumerable <Edge> edges = mask.SelectMany(x => x.edgesInternal); Edge[] empty; Face[] res; Connect(mesh, edges, out res, out empty, true, false, mask); return(res); }
/// <summary> /// Merge a collection of <see cref="ProBuilderMesh"/> objects to as few meshes as possible. It will re-use the meshTarget object as the first /// destination for the first <see cref="ProBuilderMesh.maxVertexCount"/> -1 vertices. If the sum of vertices is above <see cref="ProBuilderMesh.maxVertexCount"/> - 1 /// it will generate new meshes unless there is a single mesh left in which case it will append it to the return list. /// </summary> /// <param name="meshes">A collection of meshes to be merged. Note: it is expected that meshes includes meshTarget.</param> /// <param name="meshTarget">A mesh which will be used as the starting point for merging and which will be kept as reference/target.</param> /// <returns> /// A list of merged meshes. In most cases this will be a single mesh corresponding to meshTarget. However it can be multiple in cases /// where the resulting vertex count exceeds the maximum allowable value. /// </returns> public static List <ProBuilderMesh> Combine(IEnumerable <ProBuilderMesh> meshes, ProBuilderMesh meshTarget) { if (meshes == null) { throw new ArgumentNullException("meshes"); } if (meshTarget == null) { throw new ArgumentNullException("meshTarget"); } if (!meshes.Any() || meshes.Count() < 2) { return(null); } if (!meshes.Contains(meshTarget)) { return(null); } var vertices = new List <Vertex>(meshTarget.GetVertices()); var faces = new List <Face>(meshTarget.facesInternal); var sharedVertices = new List <SharedVertex>(meshTarget.sharedVertices); var sharedTextures = new List <SharedVertex>(meshTarget.sharedTextures); int offset = meshTarget.vertexCount; var materialMap = new List <Material>(meshTarget.renderer.sharedMaterials); var targetTransform = meshTarget.transform; var firstMeshContributors = new List <ProBuilderMesh>(); var remainderMeshContributors = new List <ProBuilderMesh>(); var currentMeshVertexCount = offset; foreach (var mesh in meshes) { if (mesh != meshTarget) { if (currentMeshVertexCount + mesh.vertexCount < ProBuilderMesh.maxVertexCount) { currentMeshVertexCount += mesh.vertexCount; firstMeshContributors.Add(mesh); } else { remainderMeshContributors.Add(mesh); } } } var autoUvFaces = new List <Face>(); AccumulateMeshesInfo( firstMeshContributors, offset, ref vertices, ref faces, ref autoUvFaces, ref sharedVertices, ref sharedTextures, ref materialMap, targetTransform ); meshTarget.SetVertices(vertices); meshTarget.faces = faces; meshTarget.sharedVertices = sharedVertices; meshTarget.sharedTextures = sharedTextures != null?sharedTextures.ToArray() : null; meshTarget.renderer.sharedMaterials = materialMap.ToArray(); meshTarget.ToMesh(); meshTarget.Refresh(); UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(meshTarget, autoUvFaces); MeshValidation.EnsureMeshIsValid(meshTarget, out int removedVertices); var returnedMesh = new List <ProBuilderMesh>() { meshTarget }; if (remainderMeshContributors.Count > 1) { var newMeshes = CombineToNewMeshes(remainderMeshContributors); foreach (var mesh in newMeshes) { MeshValidation.EnsureMeshIsValid(mesh, out removedVertices); returnedMesh.Add(mesh); } } else if (remainderMeshContributors.Count == 1) { returnedMesh.Add(remainderMeshContributors[0]); } return(returnedMesh); }