在使用Unity进行项目开发过程中,通过工业设计软件导出的模型一般都具有较多的零件,如果放入Unity中不进行处理,由于Batch数量的增加会严重降低帧率,影响应用的运行效率,如果不需要改变模型精细度(减面),可以将这些小零件合并成统一的整体,当需要对零件进行交互时,查看详细的单体零件,当看整体设备时,则可以将设备合并为一个整体,从而在一定程度上提高运行效率。
代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO;
using System.Text;
public class AutoMeshBakerEditor : Editor
{
///
/// 所有有材质的物体
///
public class AllMeshWithMaterial
{
///
/// 物体的Mesh
///
public Mesh mesh;
///
/// 物体的材质
///
public Material material;
///
///
///
public Matrix4x4 matrix4X4;
}
///
/// 有相同材质的Mesh
///
public class MeshesWithSameMaterial
{
public List WaitToMergeMeshes;
public List WaitToMergeMeshesMatrixList;
public Material SameMaterial;
public MeshesWithSameMaterial()
{
WaitToMergeMeshes = new List();
WaitToMergeMeshesMatrixList = new List();
}
}
static List subMeshesWithMaterialsSplitedList;
static string BaseSavedPath;
private static void CheckSelectionName(Transform trans)
{
trans.name = CheckFileNameLegality(trans.name, StringType.FileName);
}
[MenuItem("Tool/ModelOptimization/MergeWithLODSetting")]
public static void MergeWithLODSetting()
{
//Transform[] transs = Selection.transforms;
//Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.ExcludePrefab);
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel);
foreach (var item in transforms)
{
CheckSelectionName(item);
AutoMeshBakerEditorFunction(item);
LODSettings(item);
}
}
//[MenuItem("Tool/ModelOptimization/MergeWithoutLODSetting")]
//public static void MergeWithoutLODSetting()
//{
// CheckSelectionName(Selection.activeTransform);
// AutoMeshBakerEditorFunction(Selection.activeTransform);
//}
//[MenuItem("Tool/ModelOptimization/MergeObjectsWithLODByTag")]
//public static void MergeObjectsWithLODByTag()
//{
// GameObject[] objects = GameObject.FindGameObjectsWithTag("Merge");
// foreach (var item in objects)
// {
// CheckSelectionName(item.transform);
// AutoMeshBakerEditorFunction(item.transform);
// LODSettings(item.transform);
// }
//}
///
/// 调用Mesh合并方法的入口
///
///
public static void AutoMeshBakerEditorFunction(Transform selectionTrans)
{
GetSelectionMeshAndMaterials(selectionTrans);
List WaitToMergeMeshesWithMaterial = SplitMeshesWithMaterial();
MergedSubMeshResult(WaitToMergeMeshesWithMaterial, selectionTrans);
}
///
/// 获取选中物体的所有Mesh和Material
///
///
private static void GetSelectionMeshAndMaterials(Transform selectedObject)
{
MeshFilter[] meshFilters = selectedObject.GetComponentsInChildren();
List subMeshes = new List();
List subMeshWithMaterials = new List();
List subMatrix4X4 = new List();
for (int i = 0; i < meshFilters.Length; i++)
{
List meshes = GetAllSubMeshAsIsolatedMeshes(meshFilters[i].sharedMesh);
subMeshes.AddRange(meshes);
foreach (var item in meshes)
subMatrix4X4.Add(meshFilters[i].transform.localToWorldMatrix);
}
MeshRenderer[] meshRenderers = selectedObject.GetComponentsInChildren();
List subMaterials = new List();
foreach (var meshRenderer in meshRenderers)
{
subMaterials.AddRange(meshRenderer.sharedMaterials);
}
if (subMeshes.Count != subMaterials.Count || subMeshes.Count != subMatrix4X4.Count || subMatrix4X4.Count != subMaterials.Count)
{
return;
}
subMeshesWithMaterialsSplitedList = new List();
foreach (var item in subMeshes)
{
AllMeshWithMaterial allMeshWithMaterial = new AllMeshWithMaterial();
allMeshWithMaterial.mesh = item;
allMeshWithMaterial.material = subMaterials[subMeshes.IndexOf(item)];
allMeshWithMaterial.matrix4X4 = subMatrix4X4[subMeshes.IndexOf(item)];
subMeshesWithMaterialsSplitedList.Add(allMeshWithMaterial);
}
}
///
/// 将Mesh根据不同的Material分割开来
///
///
private static List SplitMeshesWithMaterial()
{
List meshesWithSameMaterialsSplitResult = new List();
if (subMeshesWithMaterialsSplitedList == null)
{
return null;
}
IEnumerable> groupResult = subMeshesWithMaterialsSplitedList.GroupBy(c => c.material);
foreach (IGrouping group in groupResult)
{
MeshesWithSameMaterial meshesWithSameMaterial = new MeshesWithSameMaterial();
List meshes = new List();
List matrix4X4s = new List();
foreach (var item in group)
{
meshes.Add(item.mesh);
matrix4X4s.Add(item.matrix4X4);
}
meshesWithSameMaterial.WaitToMergeMeshes = meshes;
meshesWithSameMaterial.SameMaterial = group.Key;
meshesWithSameMaterial.WaitToMergeMeshesMatrixList = matrix4X4s;
meshesWithSameMaterialsSplitResult.Add(meshesWithSameMaterial);
}
return meshesWithSameMaterialsSplitResult;
}
///
/// 合并Mesh
///
///
/// 当前选中的物体
private static void MergedSubMeshResult(List waitToMergeMeshesWithMaterial, Transform selectionTrans)
{
List mergedSubMeshWithMaterials = new List();
Material[] sharedMaterials = new Material[waitToMergeMeshesWithMaterial.Count];
for (int i = 0; i < sharedMaterials.Length; i++)
{
sharedMaterials[i] = waitToMergeMeshesWithMaterial[i].SameMaterial;
}
CombineInstance[] FinalCombines = new CombineInstance[waitToMergeMeshesWithMaterial.Count];
CombineInstance[] combine;
foreach (var item in waitToMergeMeshesWithMaterial)
{
combine = new CombineInstance[item.WaitToMergeMeshes.Count];
foreach (var mesh in item.WaitToMergeMeshes)
{
combine[item.WaitToMergeMeshes.IndexOf(mesh)].mesh = mesh;
combine[item.WaitToMergeMeshes.IndexOf(mesh)].transform = waitToMergeMeshesWithMaterial[waitToMergeMeshesWithMaterial.IndexOf(item)].WaitToMergeMeshesMatrixList[item.WaitToMergeMeshes.IndexOf(mesh)];
}
Mesh TempMesh = new Mesh();
TempMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
TempMesh.CombineMeshes(combine, true);
FinalCombines[waitToMergeMeshesWithMaterial.IndexOf(item)].mesh = TempMesh;
GameObject obj1 = new GameObject();
MeshFilter mf1 = obj1.AddComponent();
MeshRenderer mr1 = obj1.AddComponent();
mf1.sharedMesh = TempMesh;
FinalCombines[waitToMergeMeshesWithMaterial.IndexOf(item)].transform = mf1.transform.localToWorldMatrix;
DestroyImmediate(obj1);
}
//为合并后的Mesh创建物体
GameObject obj = new GameObject();
MeshFilter mf = obj.AddComponent();
MeshRenderer mr = obj.AddComponent();
Mesh FinalMesh = new Mesh();
FinalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
FinalMesh.CombineMeshes(FinalCombines, false);
FinalMesh.RecalculateBounds();
FinalMesh.RecalculateNormals();
FinalMesh.RecalculateTangents();
//检查文件名是否合法
string selectionTransName = CheckFileNameLegality(selectionTrans.name + "_LOD_1", StringType.FileName);
obj.name = selectionTransName;
string parentPath = GetCurrentMergedObjectParentPath(selectionTrans.gameObject);
//保存生成的Mesh到Asset中
SaveSourceMeshToAsset(FinalMesh, parentPath, obj.name, ".asset");
mf.sharedMesh = FinalMesh;
mr.sharedMaterials = sharedMaterials;
}
///
/// 文件名合法问题有待解决
///
///
///
private static string GetCurrentMergedObjectParentPath(GameObject obj)
{
string parentPath = "/";
Transform parent = obj.transform;
while (parent != null)
{
parentPath = "/" + CheckFileNameLegality(parent.name, StringType.FileName) + parentPath;
parent = parent.parent;
}
parentPath = CheckFileNameLegality(parentPath, StringType.PathName);
return parentPath;
}
private enum StringType
{
FileName,
PathName,
}
///
/// 检查文件名是否合法
///
///
///
///
private static string CheckFileNameLegality(string name, StringType stringType)
{
string result = name;
switch (stringType)
{
case StringType.FileName:
foreach (char rInvalidChar in Path.GetInvalidFileNameChars())
result = result.Replace(rInvalidChar, '+');
break;
case StringType.PathName:
foreach (char rInvalidChar in Path.GetInvalidPathChars())
result = result.Replace(rInvalidChar, '+');
while (result.IndexOf(" /") != -1)
{
result = result.Replace(" /", "/");
}
break;
}
return result;
}
///
/// 将Mesh数据保存到Asset中
///
///
///
///
///
private static void SaveSourceMeshToAsset(Mesh source, string parentPath, string fileName, string extentionName)
{
BaseSavedPath = Application.dataPath + "/" + "MergeResult";
string finalFileFullPath = FolderPathChecker(BaseSavedPath + parentPath, fileName, extentionName);
finalFileFullPath = finalFileFullPath.Replace(Application.dataPath, "Assets");
AssetDatabase.CreateAsset(source, finalFileFullPath);
AssetDatabase.Refresh();
}
///
/// 获取所有的子Mesh
///
///
///
private static List GetAllSubMeshAsIsolatedMeshes(Mesh mesh)
{
List meshesToReturn = new List();
if (!mesh)
{
Debug.LogError("No mesh passed into GetAllSubMeshAsIsolatedMeshes!");
return meshesToReturn;
}
int submeshCount = mesh.subMeshCount;
Mesh m1;
for (int i = 0; i < submeshCount; i++)
{
m1 = new Mesh();
//设置Mesh为32位,默认为16位,当Mesh面数的的时候会出错
m1.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
m1 = mesh.GetSubmesh(i);
meshesToReturn.Add(m1);
}
return meshesToReturn;
}
///
/// 生成LOD模型
///
///
private static void LODSettings(Transform trans)
{
GameObject obj = new GameObject(trans.name + "_Full");
if (trans.parent != null)
obj.transform.parent = trans.parent;
obj.transform.position = trans.position;
obj.transform.rotation = trans.rotation;
obj.transform.localScale = trans.localScale;
trans.SetParent(obj.transform);
Transform LODTrans = GameObject.Find(trans.name + "_LOD_1").transform;
LODTrans.SetParent(obj.transform);
LODGroup lODGroup = obj.AddComponent();
MeshRenderer[] lodObjectMRs;
lodObjectMRs = obj.transform.Find(trans.name + "_LOD_1").GetComponents();
//设置LOD参数
LOD[] lODs = new LOD[2] { new LOD(0.8f, trans.GetComponentsInChildren()), new LOD(0.25f, lodObjectMRs) };
lODGroup.SetLODs(lODs);
}
///
/// 检查文件路径
///
///
///
///
///
private static string FolderPathChecker(string path, string fileName, string extentionName)
{
CheckFolderExist(path);
string finalFileName = CheckFileExist(path + fileName, extentionName);
return finalFileName;
}
///
/// 判断文件路径是否存在
///
///
///
private static bool CheckFolderExist(string path)
{
if (!Directory.Exists(path))//如果不存在就创建file文件夹
Directory.CreateDirectory(path);//创建该文件夹
return true;
}
///
/// 判断文件是否存在
///
///
///
///
private static string CheckFileExist(string pathAndFileName, string extentionName)
{
string finalFileName = pathAndFileName + extentionName;
int count = 0;
while (File.Exists(finalFileName))
{
count++;
finalFileName = pathAndFileName + count.ToString("_0000") + extentionName;
}
return finalFileName;
}
}
///
/// Mesh相关参数
///
public static class MeshExtension
{
private class Vertices
{
///
/// 顶点
///
List verts = null;
List uv1 = null;
List uv2 = null;
List uv3 = null;
List uv4 = null;
///
/// 法线
///
List normals = null;
///
/// 切线
///
List tangents = null;
///
/// 颜色
///
List colors = null;
///
/// 骨骼权重
///
List boneWeights = null;
public Vertices()
{
verts = new List();
}
public Vertices(Mesh aMesh)
{
verts = CreateList(aMesh.vertices);
uv1 = CreateList(aMesh.uv);
uv2 = CreateList(aMesh.uv2);
uv3 = CreateList(aMesh.uv3);
uv4 = CreateList(aMesh.uv4);
normals = CreateList(aMesh.normals);
tangents = CreateList(aMesh.tangents);
colors = CreateList(aMesh.colors32);
boneWeights = CreateList(aMesh.boneWeights);
}
private List CreateList(T[] aSource)
{
if (aSource == null || aSource.Length == 0)
return null;
return new List(aSource);
}
private void Copy(ref List aDest, List aSource, int aIndex)
{
if (aSource == null)
return;
if (aDest == null)
aDest = new List();
aDest.Add(aSource[aIndex]);
}
public int Add(Vertices aOther, int aIndex)
{
int i = verts.Count;
Copy(ref verts, aOther.verts, aIndex);
Copy(ref uv1, aOther.uv1, aIndex);
Copy(ref uv2, aOther.uv2, aIndex);
Copy(ref uv3, aOther.uv3, aIndex);
Copy(ref uv4, aOther.uv4, aIndex);
Copy(ref normals, aOther.normals, aIndex);
Copy(ref tangents, aOther.tangents, aIndex);
Copy(ref colors, aOther.colors, aIndex);
Copy(ref boneWeights, aOther.boneWeights, aIndex);
return i;
}
public void AssignTo(Mesh aTarget)
{
aTarget.SetVertices(verts);
if (uv1 != null) aTarget.SetUVs(0, uv1);
if (uv2 != null) aTarget.SetUVs(1, uv2);
if (uv3 != null) aTarget.SetUVs(2, uv3);
if (uv4 != null) aTarget.SetUVs(3, uv4);
if (normals != null) aTarget.SetNormals(normals);
if (tangents != null) aTarget.SetTangents(tangents);
if (colors != null) aTarget.SetColors(colors);
if (boneWeights != null) aTarget.boneWeights = boneWeights.ToArray();
}
}
///
/// 获取Mesh相关属性
///
///
///
///
public static Mesh GetSubmesh(this Mesh aMesh, int aSubMeshIndex)
{
if (aSubMeshIndex < 0 || aSubMeshIndex >= aMesh.subMeshCount)
return null;
int[] indices = aMesh.GetTriangles(aSubMeshIndex);
Vertices source = new Vertices(aMesh);
Vertices dest = new Vertices();
Dictionary map = new Dictionary();
int[] newIndices = new int[indices.Length];
for (int i = 0; i < indices.Length; i++)
{
int o = indices[i];
int n;
if (!map.TryGetValue(o, out n))
{
n = dest.Add(source, o);
map.Add(o, n);
}
newIndices[i] = n;
}
Mesh m = new Mesh();
//提高顶点数
m.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
dest.AssignTo(m);
m.triangles = newIndices;
return m;
}
}
使用时,直接选中需要合并的物体,然后点击菜单中的“Tool/ModelOptimization/MergeWithLODSetting”即可实现合并和生成LOD的功能。
也可以直接用Mesh Baker插件来完成。