一个网格合并(weld)小工具
在日常开发中会有需求合并多个Mesh网格,并且它们重合处的顶点也要合并,而并非合并成两个subMesh。
而近期刚好在学习Geomipmap的细分,需要把多个mesh块进行合并,于是写了这个脚本
(简单的情况下用Mesh.CombineMeshes也可以)。
见下图,多对象合并前后对比:
使用时传入多个MeshFilter,会返回对应Mesh,但是没做UV、顶点色这些,需要自己扩展:
namespace Hont { using System.Collections; using System.Collections.Generic; using UnityEngine; public static class MeshCombinerUtil { public static Mesh Combine(MeshFilter[] meshFilterArary, float weldDistance = 0.01f) { const int MESH_VERT_GAP = 10_0000; Dictionary<int, int> newTrianglesDict = new Dictionary<int, int>(); List<int> newTrianglesList = new List<int>(); List<Vector3> newVerticesList = new List<Vector3>(); List<KeyValuePair<Vector3, int>> meshSlotList = new List<KeyValuePair<Vector3, int>>(); int counter = 0; for (int i = 0; i < meshFilterArary.Length; i++) { MeshFilter meshFilter = meshFilterArary[i]; Mesh mesh = meshFilter.sharedMesh; Vector3[] verticesArray = mesh.vertices; int[] trianglesArray = mesh.triangles; for (int j = 0; j < trianglesArray.Length; j++) { int triangleIndex = trianglesArray[j]; int triangleIndexContainOffset = triangleIndex + i * MESH_VERT_GAP; Vector3 vertex = verticesArray[triangleIndex]; vertex = meshFilter.transform.localToWorldMatrix.MultiplyPoint3x4(vertex); if (!newTrianglesDict.ContainsKey(triangleIndexContainOffset)) { KeyValuePair<Vector3, int> slotInfo = new KeyValuePair<Vector3, int>(Vector3.zero, -1); for (int k = 0; k < meshSlotList.Count; k++) { KeyValuePair<Vector3, int> meshSlot = meshSlotList[k]; if (Vector3.Distance(meshSlot.Key, vertex) <= weldDistance) { slotInfo = meshSlot; break; } } //检索网格交错列表 if (slotInfo.Value > -1) //如果网格交错列表里有 { newTrianglesDict.Add(triangleIndexContainOffset, slotInfo.Value); } else //如果网格交错列表里没有 { meshSlotList.Add(new KeyValuePair<Vector3, int>(vertex, counter)); newTrianglesDict.Add(triangleIndexContainOffset, counter); ++counter; newVerticesList.Add(vertex); } } //如果这个原始三角形索引字典里没有则初始化 newTrianglesList.Add(newTrianglesDict[triangleIndexContainOffset]); } } Mesh newMesh = new Mesh(); newMesh.SetVertices(newVerticesList); newMesh.SetTriangles(newTrianglesList, 0); return newMesh; } } }
2022/02/13补充,该脚本提供多个MeshFilter的合并,但并不会将硬边转软边,刚好这几天遇到一个需求要进行这一步操作。补充Mesh硬边转软边脚本,搭配使用:
public static Mesh HardEdgeCombine(Mesh mesh, float weldDistance = 0.01f) { int[] triangles = mesh.triangles; Vector3[] vertices = mesh.vertices; List<int> newTriangleList = new List<int>(triangles.Length); List<int> newVertexTriangleIndexList = new List<int>(vertices.Length); List<Vector3> newVertexList = new List<Vector3>(vertices.Length); int trianglesCounter = 0; for (int i = 0; i < triangles.Length; i++) { int triangleIndex = triangles[i]; Vector3 vertex = vertices[triangleIndex]; int dstTriangleIndex = 0; bool combineFlag = false; for (int j = 0; j < newVertexList.Count; j++) { Vector3 compareVertex = newVertexList[j]; if (Vector3.Distance(vertex, compareVertex) <= weldDistance) { dstTriangleIndex = newVertexTriangleIndexList[j]; combineFlag = true; break; } } if (!combineFlag) { dstTriangleIndex = trianglesCounter; newTriangleList.Add(dstTriangleIndex); newVertexList.Add(vertex); newVertexTriangleIndexList.Add(dstTriangleIndex); trianglesCounter++; } else { newTriangleList.Add(dstTriangleIndex); } } Mesh newMesh = new Mesh(); newMesh.SetVertices(newVertexList); newMesh.SetTriangles(newTriangleList, 0); newMesh.RecalculateBounds(); newMesh.RecalculateNormals(); newMesh.RecalculateTangents(); return newMesh; }