232 lines
9.4 KiB
C#
232 lines
9.4 KiB
C#
using System.Collections.Generic;
|
||
using Module.Log;
|
||
using UnityEngine;
|
||
using UnityEditor;
|
||
/// <summary>
|
||
/// 用于将分散成多块的大模型,分解成小模型以减少顶点运算量
|
||
/// </summary>
|
||
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<GameObject>();
|
||
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<MeshFilter>();
|
||
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<TriangleRecord>();
|
||
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<MeshPointBlock>();
|
||
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<MeshRenderer>();
|
||
|
||
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>();
|
||
meshFilter.mesh = mesh;
|
||
|
||
var meshRenderer = clone.AddComponent<MeshRenderer>();
|
||
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<TriangleRecord> triangleList;
|
||
|
||
public MeshPointBlock()
|
||
{
|
||
triangleList = new List<TriangleRecord>();
|
||
}
|
||
|
||
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<int> GetPointReflections()
|
||
{
|
||
// 映射新的顶点列表index到旧的顶点列表中
|
||
var pointList = new List<int>();
|
||
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;
|
||
}
|
||
}
|
||
} |