[Unity]手工Mesh合批

2019/12 30 21:12
public static class EditorCombineTool
{
    const string CombineNodeName = "__COMBMESH__";

    [MenuItem("GameObject/Tool/合批")]
    static public void CombineTool()
    {
        if (Selection.activeGameObject == null)
        {
            Log.Error("没有选中的物件!");
            return;
        }

        CombineObj(Selection.activeGameObject);
    }

    static public void CombineObj(GameObject rootObj)
    {
        int childCount = rootObj.transform.childCount;
        for(int i=childCount-1; i>=0; i--)
        {
            if ( rootObj.transform.GetChild(i).name == CombineNodeName )
            {
                GameObject.DestroyImmediate( rootObj.transform.GetChild(i).gameObject );
            }
        }

        Dictionary<long, List<GameObject>> matMap = new Dictionary<long, List<GameObject>>();

        var mrs = rootObj.GetComponentsInChildren<MeshRenderer>();
        if (mrs.Length == 0)
        {            
            Log.Error("没有可操作的对象1!");
            return;            
        }
        for(int i=0; i<mrs.Length; ++i)
        {
            var mr = mrs[i];

            if (mr.sharedMaterial == null)
            {
                continue;
            }

            int insId = mr.sharedMaterial.GetInstanceID();
            var key = Long64.Make64(insId, mr.gameObject.layer);

            List<GameObject> list;
            if (!matMap.TryGetValue(key, out list))
            {
                list = new List<GameObject>();
                matMap.Add(key, list);
            }
            list.Add( mr.gameObject );
        }

        if (matMap.Count == 0)
        {
            Log.Error("没有可操作的对象2!");
            return;
        }

        List< List<GameObject> > comblist = new List<List<GameObject>> ();
        foreach(var kv in matMap)
        {
            var gos = kv.Value;
            if (gos.Count > 1)
            {
                comblist.Add(gos);
            }
            else
            {
                Log.Warning("单位过少,不合批:{0}", gos[0].GetFullPathName());
            }
        }
        
        for(int i=0; i< comblist.Count;++i)
        {
            var newChild = new GameObject(CombineNodeName);
            newChild.transform.SetParent(rootObj.transform, false);
            var gos = comblist[i];
        
            var oldMr = gos[0].GetComponent<MeshRenderer>();
            if (oldMr == null)
            {
                Log.Error("老的材质出了问题! {0}", gos[0].GetFullPathName());
                continue;
            }

            var newMf = newChild.AddComponent<MeshFilter>();
            newMf.sharedMesh = CombineMeshRender(rootObj.name, rootObj, gos);

            var newMr = newChild.AddComponent<MeshRenderer>();
            newMr.sharedMaterial = oldMr.sharedMaterial;

            foreach(var g in gos)
            {
                g.SetActive(false);
            }
        }       

        AssetDatabase.Refresh();
        EditorSceneManager.MarkAllScenesDirty();
    }

    public static Mesh CombineMeshRender(string prefixName, GameObject rootObj, List<GameObject> input)
    {
        var matrix = rootObj.transform.worldToLocalMatrix;

        CombineInstance[] combine = new CombineInstance[input.Count];
        for(int i=0; i<input.Count; ++i)
        {
            var mf = input[i].GetComponent<MeshFilter>();
            combine[i].mesh = mf.sharedMesh;
            combine[i].transform = matrix * mf.transform.localToWorldMatrix;
        }
        
        Mesh mesh = new Mesh();
        mesh.CombineMeshes(combine, true);

        var number = string.Empty;
        string assetPath = string.Empty;
        for(int i=0; i<100; ++i)
        {
            number = Random.Range(100000, 900000).ToString();
            assetPath = "Assets/Arts/CombineMesh/" + prefixName + "_"+  number + ".asset";
            if (!System.IO.File.Exists(assetPath))
            {
                break;
            }
        }

        AssetDatabase.CreateAsset(mesh , assetPath);
        AssetDatabase.SaveAssets();

        var meshRet = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Mesh)) as Mesh;
        return meshRet;
    }
}