using System.Collections.Generic; using Module.Log; using UnityEngine; using UnityEditor; /// /// 用于将分散成多块的大模型,分解成小模型以减少顶点运算量 /// public static class LargeMeshSplit { public const float splitDistanceSqr = 1f * 1f; [MenuItem("GameObject/Split Mesh", false, 16)] public static void SplitMeshAndGameObject() { var gameObjects = Selection.gameObjects; if (gameObjects.Length <= 0) LogModule.ErrorLog("未选取GameObject!"); else { var targetList = new List(); for (var i = 0; i < gameObjects.Length; i++) { var list = EditorCommonUtility.GetGameObjects(gameObjects[i].transform); for (var j = 0; j < list.Count; j++) if (!targetList.Contains(list[j])) targetList.Add(list[j]); } for (var i = 0; i < targetList.Count; i++) { var meshFilter = targetList[i].GetComponent(); if (meshFilter != null) SplitForOneMeshRenderer(meshFilter); } } } public static void SplitForOneMeshRenderer(MeshFilter oldFilter) { var oldMesh = oldFilter.sharedMesh; if (oldMesh.subMeshCount > 1) LogModule.WarningLog(string.Format("{0}模型具有一个以上的SubMesh", oldFilter.gameObject.name)); else { var oldVerticles = oldMesh.vertices; var triangleList = new List(); var trianglePoints = oldMesh.triangles; for (var i = 0; i < trianglePoints.Length; i += 3) { var point0 = new PointRecord(trianglePoints[i], oldVerticles[trianglePoints[i]]); var point1 = new PointRecord(trianglePoints[i + 1], oldVerticles[trianglePoints[i + 1]]); var point2 = new PointRecord(trianglePoints[i + 2], oldVerticles[trianglePoints[i + 2]]); var triangle = new TriangleRecord(point0, point1, point2); triangleList.Add(triangle); } var meshList = new List(); var outerId = 0; while (outerId < triangleList.Count) { // 添加每个区域的第一个三角形 var pointBlock = new MeshPointBlock(); meshList.Add(pointBlock); pointBlock.triangleList.Add(triangleList[outerId]); outerId++; var innerId = outerId; // 扫描同第一个三角形连接的全部三角形 while (innerId < triangleList.Count) { var innerTriangle = triangleList[innerId]; // 如果连接成功,则将面板提升到outerId位置,然后innerId从outerId下一个位置开始扫描 if (pointBlock.IsTriangleLinked(innerTriangle)) { var temp = innerTriangle; triangleList[innerId] = triangleList[outerId]; triangleList[outerId] = temp; innerId = outerId; outerId++; pointBlock.triangleList.Add(innerTriangle); } innerId++; } } if (meshList.Count < 2) LogModule.DebugLog(string.Format("无需处理GameObject {0}", oldFilter.gameObject.name)); else { LogModule.WarningLog(string.Format("分解模型物体{0}为{1}份", oldFilter.gameObject.name, meshList.Count)); var oldUv = oldMesh.uv; var oldNormals = oldMesh.normals; var oldTangeant = oldMesh.tangents; var oldRenderer = oldFilter.GetComponent(); for (var i = 0; i < meshList.Count; i++) { var pointBlock = meshList[i]; var pointReflections = pointBlock.GetPointReflections(); var mesh = new Mesh(); var verticles = new Vector3[pointReflections.Count]; var uv = new Vector2[pointReflections.Count]; var normals = new Vector3[pointReflections.Count]; // 注:模型可能没有Tangeant参数,因此特别处理 var tangeants = new Vector4[oldTangeant.Length > 0 ? pointReflections.Count : 0]; var triangles = new int[pointBlock.triangleList.Count * 3]; for (var j = 0; j < verticles.Length; j++) { var index = pointReflections[j]; verticles[j] = oldVerticles[index]; uv[j] = oldUv[index]; normals[j] = oldNormals[index]; if (tangeants.Length > 0) tangeants[j] = oldTangeant[index]; } for (var j = 0; j < pointBlock.triangleList.Count; j++) { var triangle = pointBlock.triangleList[j]; var index = j * 3; triangles[index] = pointReflections.IndexOf(triangle.points[0].index); triangles[index + 1] = pointReflections.IndexOf(triangle.points[1].index); triangles[index + 2] = pointReflections.IndexOf(triangle.points[2].index); } mesh.vertices = verticles; mesh.uv = uv; mesh.normals = normals; mesh.tangents = tangeants; mesh.triangles = triangles; var clone = new GameObject(oldFilter.gameObject.name + string.Format("_{0}", i)); var meshFilter = clone.AddComponent(); meshFilter.mesh = mesh; var meshRenderer = clone.AddComponent(); meshRenderer.sharedMaterials = oldRenderer.sharedMaterials; //meshRenderer.sharedMaterials = new Material[oldRenderer.sharedMaterials.Length]; //for (var j = 0; j < oldRenderer.sharedMaterials.Length; j++) // meshRenderer.sharedMaterials[j] = oldRenderer.sharedMaterials[j]; meshRenderer.shadowCastingMode = oldRenderer.shadowCastingMode; meshRenderer.receiveShadows = oldRenderer.receiveShadows; clone.transform.SetParent(oldFilter.transform.parent, false); clone.transform.localPosition = oldFilter.transform.localPosition; clone.transform.localRotation = oldFilter.transform.localRotation; clone.transform.localScale = oldFilter.transform.localScale; } oldFilter.gameObject.SetActive(false); } } } public class MeshPointBlock { public List triangleList; public MeshPointBlock() { triangleList = new List(); } public bool IsTriangleLinked(TriangleRecord other) { var result = false; for (var i = 0; i < triangleList.Count; i++) { if (triangleList[i].IsTriangleLinked(other)) { result = true; break; } } return result; } public List GetPointReflections() { // 映射新的顶点列表index到旧的顶点列表中 var pointList = new List(); for (var i = 0; i < triangleList.Count; i++) { var triangle = triangleList[i]; for (var j = 0; j < triangle.points.Length; j++) { if (pointList.IndexOf(triangle.points[j].index) < 0) pointList.Add(triangle.points[j].index); } } return pointList; } } public class TriangleRecord { public readonly PointRecord[] points; public TriangleRecord(PointRecord point0, PointRecord point1, PointRecord point2) { points = new PointRecord[3]; points[0] = point0; points[1] = point1; points[2] = point2; } public bool IsTriangleLinked(TriangleRecord other) { var result = false; for (var i = 0; i < points.Length; i++) { for (var j = 0; j < other.points.Length; j++) { if (points[i].index == other.points[j].index || (points[i].position - other.points[j].position).sqrMagnitude < splitDistanceSqr) { result = true; break; } } if (result) break; } return result; } } public struct PointRecord { public readonly int index; public readonly Vector3 position; public PointRecord(int index, Vector3 position) { this.index = index; this.position = position; } } }