Files
JJBB/Assets/Project/Script/Common/Utilities/Editor/LargeMeshSplit.cs
2024-08-23 15:49:34 +08:00

232 lines
9.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}