[Unity学习教程] Unity合并mesh,【CombineMeshes】将多个物体合并成一个物体或层级关系的物

[复制链接]
查看1086 | 回复0 | 2023-8-23 12:13:24 | 显示全部楼层 |阅读模式 来自 中国北京
 Unity3D将多个物体合并一个物体或层级关系的物体

一、三种合并工具

1.MergeMesh1:

(服从低,顺应性高)模子点超过65535主动分模子,一个mesh上有多个材质会主动分出来成为子集部分,父节点要有mesh则 fatherMesh = true;
2.MergeMesh2:

(服从快、顺应性低)模子点不能超过65535,超过会报错,且雷同材质才会集并,若一个mesh上有多个材质会少一些材质(也就是丢失了一部分模子)
3.MergeMesh3:

(顺应性极低)模子点不能超过65535,超过会报错 ,材质雷同也不会集并(一般外部不常调用,MergeMesh1、2函数更好)
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace CombineMeshes
  5. {
  6.     ///<summary>
  7.     ///注意:MergeMesh2、3只能针对于mesh上只有一个材质
  8.     /// </summary>
  9.     public static class Combinemeshes
  10.     {
  11.         /// <summary>
  12.         /// (效率低,适应性高)模型点超过65535自动分模型,一个mesh上有多个材质会分出来成为子集部分,父节点要有mesh则 fatherMesh = true;
  13.         /// </summary>
  14.         public static GameObject MergeMesh1(GameObject parent, bool fatherMesh = true)
  15.         {
  16.             //获取原始坐标
  17.             Vector3 initialPos = parent.transform.position;
  18.             Quaternion initialRot = parent.transform.rotation;
  19.             //将模型坐标归零
  20.             parent.transform.position = Vector3.zero;
  21.             parent.transform.rotation = Quaternion.Euler(Vector3.zero);
  22.             List<GameObject> list = new List<GameObject>();
  23.             int verts = 0;
  24.             //存放要合并的父物体  
  25.             Dictionary<int, GameObject> NewParent = new Dictionary<int, GameObject>();
  26.             //获取所有网格过滤器
  27.             MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();
  28.             //通过游戏对象的总点数/65536,进行分组
  29.             for (int i = 0; i < meshFilters.Length; i++)
  30.             {
  31.                 verts += meshFilters[i].mesh.vertexCount;
  32.             }
  33.             for (int i = 1; i <= (verts / 65536) + 1; i++)
  34.             {
  35.                 GameObject wx = new GameObject("child" + i);
  36.                 wx.transform.parent = parent.transform;
  37.                 NewParent.Add(i, wx);
  38.             }
  39.             verts = 0;
  40.             int key = 1;
  41.             //给超过65535点的游戏对象进行分组
  42.             for (int i = 0; i < meshFilters.Length; i++)
  43.             {
  44.                 verts += meshFilters[i].mesh.vertexCount;
  45.                 if (verts >= 65535)
  46.                 {
  47.                     key++;
  48.                     verts = 0;
  49.                     verts += meshFilters[i].mesh.vertexCount;
  50.                 }
  51.                 //key= (verts / 65536) + 1;
  52.                 if (NewParent.ContainsKey(key))
  53.                 {
  54.                     meshFilters[i].transform.parent = NewParent[key].transform;
  55.                 }
  56.                 //else
  57.                 //    Debug.Log("错误");
  58.             }
  59.             //处理多材质(一个mesh上有多个材质)
  60.             if (meshFilters[0].GetComponent<MeshRenderer>().sharedMaterials.Length > 1)
  61.             {
  62.                 list.Add(GameObject.Instantiate(meshFilters[0].gameObject));
  63.             }
  64.             //
  65.             foreach (var item in NewParent)
  66.             {
  67.                 list.Add(MergeMesh3(item.Value, false));
  68.             }
  69.             //处理多材质
  70.             for (int i = 1; i < meshFilters.Length; i++)
  71.             {
  72.                 if (meshFilters[0].GetComponent<MeshRenderer>().sharedMaterials.Length > 1)
  73.                 {
  74.                     for (int j = 0; j < list[0].transform.childCount; j++)
  75.                     {
  76.                         GameObject.Destroy(list[0].transform.GetChild(j).gameObject);
  77.                     }
  78.                     GameObject.Destroy(meshFilters[0].gameObject);
  79.                 }
  80.                 if (meshFilters[i].GetComponent<MeshRenderer>().sharedMaterials.Length > 1)
  81.                 {
  82.                     list.Add(GameObject.Instantiate(meshFilters[i].gameObject));
  83.                     GameObject.Destroy(meshFilters[i].gameObject);
  84.                 }
  85.             }
  86.             //
  87.             GameObject tar_Obj = null;
  88.             //是否父节点上有mesh
  89.             if (!fatherMesh)
  90.             {
  91.                 tar_Obj = new GameObject();
  92.                 tar_Obj.name = "clone_F";
  93.                 for (int i = 0; i < list.Count; i++)
  94.                 {
  95.                     list[i].gameObject.transform.parent = tar_Obj.transform;
  96.                 }
  97.                 //还原坐标
  98.                 tar_Obj.transform.position = initialPos;
  99.                 tar_Obj.transform.rotation = initialRot;
  100.             }
  101.             //父节点上无mesh
  102.             else
  103.             {
  104.                 for (int i = 1; i < list.Count; i++)
  105.                 {
  106.                     list[i].gameObject.transform.parent = list[0].gameObject.transform;
  107.                     list[i].gameObject.name = "child" + i;
  108.                 }
  109.                 //还原坐标
  110.                 list[0].gameObject.transform.position = initialPos;
  111.                 list[0].gameObject.transform.rotation = initialRot;
  112.             }
  113.             GameObject.Destroy(parent);
  114.             return fatherMesh ? list[0] : tar_Obj;
  115.         }
  116.         /// <summary>
  117.         ///(效率快、适用性低)模型点不能超过65535,且相同材质会合并
  118.         /// </summary>
  119.         public static GameObject MergeMesh2(GameObject parent, bool mergeAll = true)
  120.         {
  121.             //获取原始坐标
  122.             Vector3 initialPos = parent.transform.position;
  123.             Quaternion initialRot = parent.transform.rotation;
  124.             //将坐标归零
  125.             parent.transform.position = Vector3.zero;
  126.             parent.transform.rotation = Quaternion.Euler(Vector3.zero);
  127.             //获取所有网格过滤器
  128.             MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();
  129.             //存放不同的材质球,相同的就存一个
  130.             Dictionary<string, Material> materials = new Dictionary<string, Material>();
  131.             //存放要合并的网格对象
  132.             Dictionary<string, List<CombineInstance>> combines = new Dictionary<string, List<CombineInstance>>();
  133.             for (int i = 0; i < meshFilters.Length; i++)
  134.             {
  135.                 //构造一个网格合并结构体
  136.                 CombineInstance combine = new CombineInstance();
  137.                 //给结构体的mesh赋值
  138.                 combine.mesh = meshFilters[i].sharedMesh;
  139.                 combine.transform = meshFilters[i].transform.localToWorldMatrix;
  140.                 MeshRenderer renderer = meshFilters[i].GetComponent<MeshRenderer>();
  141.                 if (renderer == null)
  142.                 {
  143.                     continue;
  144.                 }
  145.                 Material mat = renderer.sharedMaterial;
  146.                 //将相同材质记录一次,再将拥有相同材质的mesh放到Dictionary中
  147.                 if (!materials.ContainsKey(mat.name))
  148.                 {
  149.                     materials.Add(mat.name, mat);
  150.                 }
  151.                 if (combines.ContainsKey(mat.name))
  152.                 {
  153.                     combines[mat.name].Add(combine);
  154.                 }
  155.                 else
  156.                 {
  157.                     List<CombineInstance> coms = new List<CombineInstance>();
  158.                     coms.Add(combine);
  159.                     combines[mat.name] = coms;
  160.                 }
  161.             }
  162.             GameObject combineObj = new GameObject(parent.name);
  163.             foreach (KeyValuePair<string, Material> mater in materials)
  164.             {
  165.                 GameObject obj = new GameObject(mater.Key);
  166.                 obj.transform.parent = combineObj.transform;
  167.                 MeshFilter combineMeshFilter = obj.AddComponent<MeshFilter>();
  168.                 combineMeshFilter.mesh = new Mesh();
  169.                 //将引用相同材质球的网格合并
  170.                 combineMeshFilter.sharedMesh.CombineMeshes(combines[mater.Key].ToArray(), true, true);
  171.                 //Debug.LogError("网格定点数" + combineMeshFilter.sharedMesh.vertices);
  172.                 MeshRenderer rend = obj.AddComponent<MeshRenderer>();
  173.                 //指定材质球
  174.                 rend.sharedMaterial = mater.Value;
  175.                 //需要设置请自行打开
  176.                 //rend.shadowCastingMode = ShadowCastingMode.Off;
  177.                 //rend.receiveShadows = true;
  178.             }
  179.             GameObject tar_Obj = null;
  180.             if (mergeAll)
  181.             {
  182.                 tar_Obj = MergeMesh3(combineObj);
  183.             }
  184.             //还原坐标
  185.             tar_Obj.transform.position = initialPos;
  186.             tar_Obj.transform.rotation = initialRot;
  187.             GameObject.Destroy(parent);
  188.             return tar_Obj;
  189.         }
  190.         /// <summary>
  191.         /// 模型点不能超过65535,材质相同也不会合并(一般外部不常调用,MergeMesh1、2函数更好)
  192.         /// </summary>
  193.         public static GameObject MergeMesh3(GameObject parent, bool mergeSubMeshes = false)
  194.         {
  195.             MeshRenderer[] meshRenderers = parent.GetComponentsInChildren<MeshRenderer>();
  196.             Material[] materials = new Material[meshRenderers.Length];
  197.             for (int i = 0; i < meshRenderers.Length; i++)
  198.             {
  199.                 materials[i] = meshRenderers[i].sharedMaterial;
  200.             }
  201.             MeshFilter[] meshFilters = parent.GetComponentsInChildren<MeshFilter>();
  202.             CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
  203.             for (int i = 0; i < meshFilters.Length; i++)
  204.             {
  205.                 combineInstances[i].mesh = meshFilters[i].sharedMesh;
  206.                 combineInstances[i].transform = meshFilters[i].transform.localToWorldMatrix;
  207.             }
  208.             GameObject mesh_obj = new GameObject(parent.name);
  209.             MeshFilter meshFilter = mesh_obj.AddComponent<MeshFilter>();
  210.             meshFilter.mesh.CombineMeshes(combineInstances, mergeSubMeshes);
  211.             MeshRenderer meshRenderer = mesh_obj.AddComponent<MeshRenderer>();
  212.             meshRenderer.sharedMaterials = materials;
  213.             GameObject.Destroy(parent);
  214.             return mesh_obj;
  215.         }
  216.     }
  217. }
复制代码
 
二、调用合并工具 

  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using CombineMeshes;
  4. public class UsingMergeMesh : MonoBehaviour
  5. {
  6.     [Header("示例1:AllMergeMesh")]
  7.     public GameObject MergeMeshGameObject;
  8.     [Header("示例2:PartMergeMesh请给父节点放入4个模型(若没有4个游戏对象,程序会报错)")]
  9.     public GameObject MergeMeshGameObject2;//
  10.     void Update()
  11.     {
  12.         //按下Enter键才会执行
  13.         if (Input.GetKeyDown(KeyCode.Return))
  14.         {
  15.             AllMergeMesh(MergeMeshGameObject);
  16.         }
  17.         //按下A键才会执行
  18.         if (Input.GetKeyDown(KeyCode.A))
  19.         {
  20.             PartMergeMesh(MergeMeshGameObject2);
  21.         }
  22.     }
  23.     /// <summary>
  24.     /// 模型的整体合并网格
  25.     /// </summary>
  26.     /// <param name="source_Obj">父节点</param>
  27.     /// <returns></returns>
  28.     GameObject AllMergeMesh(GameObject source_Obj)
  29.     {
  30.         Vector3 initialPos = source_Obj.transform.position;
  31.         Quaternion initialRot = source_Obj.transform.rotation;
  32.         GameObject clone_Obj = Instantiate(source_Obj);
  33.         GameObject cloneParents = new GameObject();
  34.         cloneParents.transform.position = initialPos;
  35.         cloneParents.transform.rotation = initialRot;
  36.         clone_Obj.transform.parent = cloneParents.transform;
  37.         #region<根据实际要求自行更改>
  38.         ///<summary>
  39.         ///自动分总点数超过65535点的模型,超过65535的点会有子集关系,且相同材质会合并
  40.         ///</summary>
  41.         GameObject tar_Obj = Combinemeshes.MergeMesh1(cloneParents, true);//父节点有mesh
  42.         //tar_Obj = Combinemeshes.MergeMesh1(clone_F,false);//父节点无mesh
  43.         ///<summary>
  44.         ///模型总点数不能超过65535,且相同材质会合并,超过会报错:合并游戏对象的总点数超过65535
  45.         ///</summary>
  46.         //tar_Obj = Combinemeshes.MergeMesh2(clone_F);
  47.         ///<summary>
  48.         ///模型总点数不能超过65535,材质相同也不会合并(一般不调用,MergeMesh2函数更好)
  49.         ///</summary>
  50.         //tar_Obj = Combinemeshes.MergeMesh3(clone_F);
  51.         #endregion
  52.         tar_Obj.name = "AllMergeMesh";
  53.         return tar_Obj;
  54.     }
  55.     /// <summary>
  56.     /// 模型的子集关系合并(这里做个示例,请给父节点放入4个模型)
  57.     /// </summary>
  58.     /// <param name="source_Obj">父节点</param>
  59.     /// <returns></returns>
  60.     GameObject PartMergeMesh(GameObject source_Obj)
  61.     {
  62.         //获取父节点中的子物体
  63.         List<GameObject> source_ObjChild = new List<GameObject>();
  64.         for (int i = 0; i < source_Obj.transform.childCount; i++)
  65.         {
  66.             source_ObjChild.Add(source_Obj.transform.GetChild(i).gameObject);
  67.         }
  68.         GameObject tar_Obj = new GameObject(source_Obj.name);
  69.         GameObject gameObjects;//将gameObj实例化的对象,放入gameObjects子集中
  70.         List<GameObject> gameObj = new List<GameObject>();//实例化对象
  71.         //合并一
  72.         gameObjects = new GameObject(source_Obj.name);
  73.         gameObjects.transform.position = source_ObjChild[0].transform.localPosition;
  74.         gameObj.Add(Instantiate(source_ObjChild[0]));
  75.         gameObj.Add(Instantiate(source_ObjChild[1]));
  76.         for (int i = 0; i < gameObj.Count; i++)
  77.         {
  78.             gameObj[i].transform.parent = gameObjects.transform;
  79.         }
  80.         //这里我用的是MergeMesh1,可自行根据实际更改MergeMesh2、MergeMesh3
  81.         gameObjects = Combinemeshes.MergeMesh1(gameObjects);
  82.         gameObj.Clear();
  83.         gameObjects.transform.parent = tar_Obj.transform;
  84.         //合并二
  85.         gameObjects = new GameObject("joint");
  86.         gameObjects.transform.position = source_ObjChild[2].transform.localPosition;
  87.         gameObj.Add(Instantiate(source_ObjChild[2]));
  88.         gameObj.Add(Instantiate(source_ObjChild[3]));
  89.         for (int i = 0; i < gameObj.Count; i++)
  90.         {
  91.             gameObj[i].transform.parent = gameObjects.transform;
  92.         }
  93.         //这里我用的是MergeMesh1,可自行根据实际更改MergeMesh2、MergeMesh3
  94.         gameObjects = Combinemeshes.MergeMesh1(gameObjects);
  95.         gameObj.Clear();
  96.         gameObjects.transform.parent = tar_Obj.transform;
  97.         Vector3 initialPos = source_Obj.transform.position;
  98.         Quaternion initialRot = source_Obj.transform.rotation;
  99.         tar_Obj.transform.position = initialPos;
  100.         tar_Obj.transform.rotation = initialRot;
  101.         //设置父子关系
  102.         for (int i = 0; i < tar_Obj.transform.childCount; i++)
  103.         {
  104.             gameObj.Add(tar_Obj.transform.GetChild(i).gameObject);
  105.         }
  106.         for (int i = 1; i < gameObj.Count; i++)
  107.         {
  108.             gameObj[i].transform.parent = gameObj[i - 1].transform;
  109.         }
  110.         #region<根据需要请自行更改>
  111.         ///<summary>
  112.         ///父节点有mesh
  113.         /// </summary>
  114.         gameObj[0].transform.parent = tar_Obj.transform.parent;
  115.         gameObj[0].name = "PartMergeMesh";
  116.         Destroy(tar_Obj);
  117.         return gameObj[0];
  118.         ///<summary>
  119.         ///父节点无mesh
  120.         /// </summary>
  121.         //return tar_Obj;
  122.         #endregion
  123.     }
  124. }
复制代码
三、利用方法

 1.创建如下测试示例模子,拖入框中

 
 2.创建好的模子如下

 3.点击Enter和A键就会得到如下合并后的模子,合并后模子与原模子位置是重合的,为了方便各人看清合并的模子,我将它们移了出来。


 
 参考文章:
Unity 合并Mesh 将多个小的物体合并成一个大物体,同批次渲染_万兴丶的博客-CSDN博客_unity合并物体

来源:https://blog.csdn.net/weixin_48388330/article/details/127588694
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则