Unity合并mesh,【CombineMeshes】将多个物体合并成一个物体或层级关系的物体
Unity3D将多个物体合并一个物体或层级关系的物体
一、三种合并工具
1.MergeMesh1:
(效率低,适应性高)模型点超过65535自动分模型,一个mesh上有多个材质会自动分出来成为子集部分,父节点要有mesh则 fatherMesh = true;
2.MergeMesh2:
(效率快、适应性低)模型点不能超过65535,超过会报错,且相同材质才会合并,若一个mesh上有多个材质会少一些材质(也就是丢失了一部分模型)
3.MergeMesh3:
(适应性极低)模型点不能超过65535,超过会报错 ,材质相同也不会合并(一般外部不常调用,MergeMesh1、2函数更好)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace CombineMeshes
{
///<summary>
///注意:MergeMesh2、3只能针对于mesh上只有一个材质
/// </summary>
public static class Combinemeshes
{
/// <summary>
/// (效率低,适应性高)模型点超过65535自动分模型,一个mesh上有多个材质会分出来成为子集部分,父节点要有mesh则 fatherMesh = true;
/// </summary>
public static GameObject MergeMesh1(GameObject parent, bool fatherMesh = true)
{
//获取原始坐标
Vector3 initialPos = parent.transform.position;
Quaternion initialRot = parent.transform.rotation;
//将模型坐标归零
parent.transform.position = Vector3.zero;
parent.transform.rotation = Quaternion.Euler(Vector3.zero);
List<GameObject> list = new List<GameObject>();
int verts = 0;
//存放要合并的父物体
Dictionary<int, GameObject> NewParent = new Dictionary<int, GameObject>();
//获取所有网格过滤器
MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();
//通过游戏对象的总点数/65536,进行分组
for (int i = 0; i < meshFilters.Length; i++)
{
verts += meshFilters[i].mesh.vertexCount;
}
for (int i = 1; i <= (verts / 65536) + 1; i++)
{
GameObject wx = new GameObject("child" + i);
wx.transform.parent = parent.transform;
NewParent.Add(i, wx);
}
verts = 0;
int key = 1;
//给超过65535点的游戏对象进行分组
for (int i = 0; i < meshFilters.Length; i++)
{
verts += meshFilters[i].mesh.vertexCount;
if (verts >= 65535)
{
key++;
verts = 0;
verts += meshFilters[i].mesh.vertexCount;
}
//key= (verts / 65536) + 1;
if (NewParent.ContainsKey(key))
{
meshFilters[i].transform.parent = NewParent[key].transform;
}
//else
// Debug.Log("错误");
}
//处理多材质(一个mesh上有多个材质)
if (meshFilters[0].GetComponent<MeshRenderer>().sharedMaterials.Length > 1)
{
list.Add(GameObject.Instantiate(meshFilters[0].gameObject));
}
//
foreach (var item in NewParent)
{
list.Add(MergeMesh3(item.Value, false));
}
//处理多材质
for (int i = 1; i < meshFilters.Length; i++)
{
if (meshFilters[0].GetComponent<MeshRenderer>().sharedMaterials.Length > 1)
{
for (int j = 0; j < list[0].transform.childCount; j++)
{
GameObject.Destroy(list[0].transform.GetChild(j).gameObject);
}
GameObject.Destroy(meshFilters[0].gameObject);
}
if (meshFilters[i].GetComponent<MeshRenderer>().sharedMaterials.Length > 1)
{
list.Add(GameObject.Instantiate(meshFilters[i].gameObject));
GameObject.Destroy(meshFilters[i].gameObject);
}
}
//
GameObject tar_Obj = null;
//是否父节点上有mesh
if (!fatherMesh)
{
tar_Obj = new GameObject();
tar_Obj.name = "clone_F";
for (int i = 0; i < list.Count; i++)
{
list[i].gameObject.transform.parent = tar_Obj.transform;
}
//还原坐标
tar_Obj.transform.position = initialPos;
tar_Obj.transform.rotation = initialRot;
}
//父节点上无mesh
else
{
for (int i = 1; i < list.Count; i++)
{
list[i].gameObject.transform.parent = list[0].gameObject.transform;
list[i].gameObject.name = "child" + i;
}
//还原坐标
list[0].gameObject.transform.position = initialPos;
list[0].gameObject.transform.rotation = initialRot;
}
GameObject.Destroy(parent);
return fatherMesh ? list[0] : tar_Obj;
}
/// <summary>
///(效率快、适用性低)模型点不能超过65535,且相同材质会合并
/// </summary>
public static GameObject MergeMesh2(GameObject parent, bool mergeAll = true)
{
//获取原始坐标
Vector3 initialPos = parent.transform.position;
Quaternion initialRot = parent.transform.rotation;
//将坐标归零
parent.transform.position = Vector3.zero;
parent.transform.rotation = Quaternion.Euler(Vector3.zero);
//获取所有网格过滤器
MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();
//存放不同的材质球,相同的就存一个
Dictionary<string, Material> materials = new Dictionary<string, Material>();
//存放要合并的网格对象
Dictionary<string, List<CombineInstance>> combines = new Dictionary<string, List<CombineInstance>>();
for (int i = 0; i < meshFilters.Length; i++)
{
//构造一个网格合并结构体
CombineInstance combine = new CombineInstance();
//给结构体的mesh赋值
combine.mesh = meshFilters[i].sharedMesh;
combine.transform = meshFilters[i].transform.localToWorldMatrix;
MeshRenderer renderer = meshFilters[i].GetComponent<MeshRenderer>();
if (renderer == null)
{
continue;
}
Material mat = renderer.sharedMaterial;
//将相同材质记录一次,再将拥有相同材质的mesh放到Dictionary中
if (!materials.ContainsKey(mat.name))
{
materials.Add(mat.name, mat);
}
if (combines.ContainsKey(mat.name))
{
combines[mat.name].Add(combine);
}
else
{
List<CombineInstance> coms = new List<CombineInstance>();
coms.Add(combine);
combines[mat.name] = coms;
}
}
GameObject combineObj = new GameObject(parent.name);
foreach (KeyValuePair<string, Material> mater in materials)
{
GameObject obj = new GameObject(mater.Key);
obj.transform.parent = combineObj.transform;
MeshFilter combineMeshFilter = obj.AddComponent<MeshFilter>();
combineMeshFilter.mesh = new Mesh();
//将引用相同材质球的网格合并
combineMeshFilter.sharedMesh.CombineMeshes(combines[mater.Key].ToArray(), true, true);
//Debug.LogError("网格定点数" + combineMeshFilter.sharedMesh.vertices);
MeshRenderer rend = obj.AddComponent<MeshRenderer>();
//指定材质球
rend.sharedMaterial = mater.Value;
//需要设置请自行打开
//rend.shadowCastingMode = ShadowCastingMode.Off;
//rend.receiveShadows = true;
}
GameObject tar_Obj = null;
if (mergeAll)
{
tar_Obj = MergeMesh3(combineObj);
}
//还原坐标
tar_Obj.transform.position = initialPos;
tar_Obj.transform.rotation = initialRot;
GameObject.Destroy(parent);
return tar_Obj;
}
/// <summary>
/// 模型点不能超过65535,材质相同也不会合并(一般外部不常调用,MergeMesh1、2函数更好)
/// </summary>
public static GameObject MergeMesh3(GameObject parent, bool mergeSubMeshes = false)
{
MeshRenderer[] meshRenderers = parent.GetComponentsInChildren<MeshRenderer>();
Material[] materials = new Material[meshRenderers.Length];
for (int i = 0; i < meshRenderers.Length; i++)
{
materials[i] = meshRenderers[i].sharedMaterial;
}
MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();
CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
for (int i = 0; i < meshFilters.Length; i++)
{
combineInstances[i].mesh = meshFilters[i].sharedMesh;
combineInstances[i].transform = meshFilters[i].transform.localToWorldMatrix;
}
GameObject mesh_obj = new GameObject(parent.name);
MeshFilter meshFilter = mesh_obj.AddComponent<MeshFilter>();
meshFilter.mesh.CombineMeshes(combineInstances, mergeSubMeshes);
MeshRenderer meshRenderer = mesh_obj.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterials = materials;
GameObject.Destroy(parent);
return mesh_obj;
}
}
}
二、调用合并工具
using System.Collections.Generic;
using UnityEngine;
using CombineMeshes;
public class UsingMergeMesh : MonoBehaviour
{
[Header("示例1:AllMergeMesh")]
public GameObject MergeMeshGameObject;
[Header("示例2:PartMergeMesh请给父节点放入4个模型(若没有4个游戏对象,程序会报错)")]
public GameObject MergeMeshGameObject2;//
void Update()
{
//按下Enter键才会执行
if (Input.GetKeyDown(KeyCode.Return))
{
AllMergeMesh(MergeMeshGameObject);
}
//按下A键才会执行
if (Input.GetKeyDown(KeyCode.A))
{
PartMergeMesh(MergeMeshGameObject2);
}
}
/// <summary>
/// 模型的整体合并网格
/// </summary>
/// <param name="source_Obj">父节点</param>
/// <returns></returns>
GameObject AllMergeMesh(GameObject source_Obj)
{
Vector3 initialPos = source_Obj.transform.position;
Quaternion initialRot = source_Obj.transform.rotation;
GameObject clone_Obj = Instantiate(source_Obj);
GameObject cloneParents = new GameObject();
cloneParents.transform.position = initialPos;
cloneParents.transform.rotation = initialRot;
clone_Obj.transform.parent = cloneParents.transform;
#region<根据实际要求自行更改>
///<summary>
///自动分总点数超过65535点的模型,超过65535的点会有子集关系,且相同材质会合并
///</summary>
GameObject tar_Obj = Combinemeshes.MergeMesh1(cloneParents, true);//父节点有mesh
//tar_Obj = Combinemeshes.MergeMesh1(clone_F,false);//父节点无mesh
///<summary>
///模型总点数不能超过65535,且相同材质会合并,超过会报错:合并游戏对象的总点数超过65535
///</summary>
//tar_Obj = Combinemeshes.MergeMesh2(clone_F);
///<summary>
///模型总点数不能超过65535,材质相同也不会合并(一般不调用,MergeMesh2函数更好)
///</summary>
//tar_Obj = Combinemeshes.MergeMesh3(clone_F);
#endregion
tar_Obj.name = "AllMergeMesh";
return tar_Obj;
}
/// <summary>
/// 模型的子集关系合并(这里做个示例,请给父节点放入4个模型)
/// </summary>
/// <param name="source_Obj">父节点</param>
/// <returns></returns>
GameObject PartMergeMesh(GameObject source_Obj)
{
//获取父节点中的子物体
List<GameObject> source_ObjChild = new List<GameObject>();
for (int i = 0; i < source_Obj.transform.childCount; i++)
{
source_ObjChild.Add(source_Obj.transform.GetChild(i).gameObject);
}
GameObject tar_Obj = new GameObject(source_Obj.name);
GameObject gameObjects;//将gameObj实例化的对象,放入gameObjects子集中
List<GameObject> gameObj = new List<GameObject>();//实例化对象
//合并一
gameObjects = new GameObject(source_Obj.name);
gameObjects.transform.position = source_ObjChild[0].transform.localPosition;
gameObj.Add(Instantiate(source_ObjChild[0]));
gameObj.Add(Instantiate(source_ObjChild[1]));
for (int i = 0; i < gameObj.Count; i++)
{
gameObj[i].transform.parent = gameObjects.transform;
}
//这里我用的是MergeMesh1,可自行根据实际更改MergeMesh2、MergeMesh3
gameObjects = Combinemeshes.MergeMesh1(gameObjects);
gameObj.Clear();
gameObjects.transform.parent = tar_Obj.transform;
//合并二
gameObjects = new GameObject("joint");
gameObjects.transform.position = source_ObjChild[2].transform.localPosition;
gameObj.Add(Instantiate(source_ObjChild[2]));
gameObj.Add(Instantiate(source_ObjChild[3]));
for (int i = 0; i < gameObj.Count; i++)
{
gameObj[i].transform.parent = gameObjects.transform;
}
//这里我用的是MergeMesh1,可自行根据实际更改MergeMesh2、MergeMesh3
gameObjects = Combinemeshes.MergeMesh1(gameObjects);
gameObj.Clear();
gameObjects.transform.parent = tar_Obj.transform;
Vector3 initialPos = source_Obj.transform.position;
Quaternion initialRot = source_Obj.transform.rotation;
tar_Obj.transform.position = initialPos;
tar_Obj.transform.rotation = initialRot;
//设置父子关系
for (int i = 0; i < tar_Obj.transform.childCount; i++)
{
gameObj.Add(tar_Obj.transform.GetChild(i).gameObject);
}
for (int i = 1; i < gameObj.Count; i++)
{
gameObj[i].transform.parent = gameObj[i - 1].transform;
}
#region<根据需要请自行更改>
///<summary>
///父节点有mesh
/// </summary>
gameObj[0].transform.parent = tar_Obj.transform.parent;
gameObj[0].name = "PartMergeMesh";
Destroy(tar_Obj);
return gameObj[0];
///<summary>
///父节点无mesh
/// </summary>
//return tar_Obj;
#endregion
}
}
三、使用方法
1.创建如下测试示例模型,拖入框中
2.创建好的模型如下
3.点击Enter和A键就会得到如下合并后的模型,合并后模型与原模型位置是重合的,为了方便大家看清合并的模型,我将它们移了出来。
参考文章:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!