/// <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); }
static List <ProBuilderMesh> CombineToNewMeshes(IEnumerable <ProBuilderMesh> meshes) { if (meshes == null) { throw new ArgumentNullException("meshes"); } if (!meshes.Any() || meshes.Count() < 2) { return(null); } var vertices = new List <Vertex>(); var faces = new List <Face>(); var autoUvFaces = new List <Face>(); var sharedVertices = new List <SharedVertex>(); var sharedTextures = new List <SharedVertex>(); int offset = 0; var materialMap = new List <Material>(); AccumulateMeshesInfo( meshes, offset, ref vertices, ref faces, ref autoUvFaces, ref sharedVertices, ref sharedTextures, ref materialMap ); var res = SplitByMaxVertexCount(vertices, faces, sharedVertices, sharedTextures); var pivot = meshes.LastOrDefault().transform.position; foreach (var m in res) { m.renderer.sharedMaterials = materialMap.ToArray(); InternalMeshUtility.FilterUnusedSubmeshIndexes(m); m.SetPivot(pivot); UVEditing.SetAutoAndAlignUnwrapParamsToUVs(m, autoUvFaces); } return(res); }
/// <summary> /// Merge a collection of <see cref="ProBuilderMesh"/> objects to as few meshes as possible. This may result in /// more than one mesh due to a max vertex count limit of 65535. /// </summary> /// <param name="meshes">A collection of meshes to be merged.</param> /// <returns> /// A list of merged meshes. In most cases this will be a single mesh. 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) { if (meshes == null) { throw new ArgumentNullException("meshes"); } if (!meshes.Any() || meshes.Count() < 2) { return(null); } var vertices = new List <Vertex>(); var faces = new List <Face>(); var autoUvFaces = new List <Face>(); var sharedVertices = new List <SharedVertex>(); var sharedTextures = new List <SharedVertex>(); int offset = 0; var materialMap = new List <Material>(); foreach (var mesh in meshes) { var meshVertexCount = mesh.vertexCount; var transform = mesh.transform; var meshVertices = mesh.GetVertices(); var meshFaces = mesh.facesInternal; var meshSharedVertices = mesh.sharedVertices; var meshSharedTextures = mesh.sharedTextures; var materials = mesh.renderer.sharedMaterials; var materialCount = materials.Length; for (int i = 0; i < meshVertexCount; i++) { vertices.Add(transform.TransformVertex(meshVertices[i])); } foreach (var face in meshFaces) { var newFace = new Face(face); newFace.ShiftIndexes(offset); // prevents uvs from shifting when being converted from local coords to world space if (!newFace.manualUV && !newFace.uv.useWorldSpace) { newFace.manualUV = true; autoUvFaces.Add(newFace); } var material = materials[Math.Clamp(face.submeshIndex, 0, materialCount - 1)]; var submeshIndex = materialMap.IndexOf(material); if (submeshIndex > -1) { newFace.submeshIndex = submeshIndex; } else { if (material == null) { newFace.submeshIndex = 0; } else { newFace.submeshIndex = materialMap.Count; materialMap.Add(material); } } faces.Add(newFace); } foreach (var sv in meshSharedVertices) { var nsv = new SharedVertex(sv); nsv.ShiftIndexes(offset); sharedVertices.Add(nsv); } foreach (var st in meshSharedTextures) { var nst = new SharedVertex(st); nst.ShiftIndexes(offset); sharedTextures.Add(nst); } offset += meshVertexCount; } var res = SplitByMaxVertexCount(vertices, faces, sharedVertices, sharedTextures); var pivot = meshes.LastOrDefault().transform.position; foreach (var m in res) { m.renderer.sharedMaterials = materialMap.ToArray(); InternalMeshUtility.FilterUnusedSubmeshIndexes(m); m.SetPivot(pivot); UVEditing.SetAutoAndAlignUnwrapParamsToUVs(m, autoUvFaces); } 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.sharedVertices = sharedTextures != null?sharedTextures.ToArray() : null; meshTarget.renderer.sharedMaterials = materialMap.ToArray(); meshTarget.ToMesh(); meshTarget.Refresh(); UVEditing.SetAutoAndAlignUnwrapParamsToUVs(meshTarget, autoUvFaces); var returnedMesh = new List <ProBuilderMesh>() { meshTarget }; if (remainderMeshContributors.Count > 1) { var newMeshes = CombineToNewMeshes(remainderMeshContributors); foreach (var mesh in newMeshes) { returnedMesh.Add(mesh); } } else if (remainderMeshContributors.Count == 1) { returnedMesh.Add(remainderMeshContributors[0]); } return(returnedMesh); }