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