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键就会得到如下合并后的模型,合并后模型与原模型位置是重合的,为了方便大家看清合并的模型,我将它们移了出来。

 

 参考文章:

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

  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平杨猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值