1. 程式人生 > >Unity Edior下合併場景 合併網格 合併貼圖

Unity Edior下合併場景 合併網格 合併貼圖

宣告:這篇絕不是網上那些轉來轉去千篇一律的 合併方法 搜老半天看的都是同一篇部落格 這裡寫圖片描述
一點幫助都沒有 還是自己寫個吧

1.工程裡面 合併場景主要是為了降低draw call
2、不同shader的儘量不要合併 不同shader 引數的也儘量不要合併
3、本篇部落格主要是合併場景裡的 不會動的 不考慮人物 只考慮MeshRenderer

步驟:
一、首先要找出場景裡用到最多的shader 不一定是一個 可能是兩個三個個 至少這些shader在90%的物體上用到了
二、開啟這個場景 EditorSceneManger.OpenScene(path) 在Editor下開啟這個場景 拿到一個Scene物件
三、用Scene.GetRootGameObjects() 獲取到根目錄下的遊戲物體
四、遍歷這個根目錄 拿到所有符合條件的MeshRenderer元件(材質球只有一個、shader是滿足條件的shader、貼圖大小滿足條件、元件是啟用的 等等)
五、遍歷這些MeshRenderer 將這些按照 合併策略分類(我這個專案合併策略是 場景裡一個15x15的格子裡 shader一樣、shader引數一樣的才進行合併) (座標|shader|引數 為字典的Key List為字典的Value)
六、遍歷這個字典的每一項 開始進行合併
1、先合併這些對應的貼圖 我用的是texturePacker進行合併貼圖 這樣省去很多功夫
①寫好一個.bat檔案 用於直接進行命令列處理TexturePacker合併檔案
需要設定環境變數
具體引數可以去TexturePacker官網上查

set "Max_Size=%1" 
set "Poweroftow=%2"
set "BorderPadding=%3"
set "ShapePadding=%4"
set "TargetPath=%5"
set "SourcePaths=%~6"

TexturePacker --sheet %TargetPath%_tp{n}.tga ^
--data %TargetPath%_tp{n}_cfg.txt ^
--texture-format tga ^
--disable-rotation ^
--format unity ^
--multipack  ^
--max-size %Max_Size
% ^
--trim-mode None ^ --extrude 0 ^ --opt RGBA8888 ^ --png-opt-level 2 ^ --border-padding %BorderPadding% ^ --shape-padding %ShapePadding% ^ --disable-auto-alias ^ --size-constraints %Poweroftow% %SourcePaths%

②先將要合併的資源拷唄到一個資料夾下
③然後執行這個.bat檔案 具體怎麼執行 用Process 類 再具體就百度吧
④執行完之後 合併出來的可能不只一張圖片 如果超過一張圖片 就要重新合併 此時要講原來的MeshRenderer二分,用遞迴再次合併兩個一半的MeshRenderer陣列 直到合併出來只有一個圖集 同時會生成一個cfg配置檔案 用來顯示原來的圖片在生成的圖片在那一部分
2、貼圖合併成功之後就該進行網格合併了
①拿到上一步貼圖合併成功的MeshRenderer陣列 再拿到MeshRenderer物件上的MeshFilter身上的sharedMesh
②用Unity Api Mesh.Combine(CombineInstance[]) 進行合併網格
3、 網格合併成功之後 要修改這個網格的UV座標
一個網格有多少個頂點 就有多少個Uv座標 這個座標用來控制這個頂點顯示圖片上哪一部分的內容
所以這一步很重要 要重新刷一遍網格的所有UV座標
根據之前合併貼圖成功時生成的配置檔案,這個檔案是個Json檔案,
將原來的MeshUV 根據對映轉換成 大的圖集上的UV座標
4、建立個空物體 帶有MeshRenderer MeshFilter 的元件 用於接收生成的網格 和 材質球
建立材質球 設定shader 和引數 和貼圖
給MeshRenderer設定這個材質球 MeshFilter設定這個Mesh
5、最後將生成的Material 和Mesh 分別生成一個資源 AssetDatabase.CreateAssert(Obejc,path)
因為之前生成的Material和Mesh只是在記憶體裡 一切換場景就沒有了 所有要生成.asset檔案

程式碼:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using UnityEditor.SceneManagement;
using System.Linq;
using System.Collections.Generic;
using System.IO;

public class CombineTool : Editor
{
    private const int GRIDSIDE = 15;
    private const string PATH = "Assets/Resources/scene/";
    private static string ASSET_PATH = EditorConfig.assetPath;

    [MenuItem("Test/Test")]
    public static void Test()
    {
    }

    [MenuItem("Debug/合併場景不烘焙")]
    public static void CombineSceneNotBake()
    {
        CombineScene(false);
    }

    /// <summary>
    /// 合併所有場景
    /// </summary>
    /// <param name="bake"></param>
    private static void CombineScene(bool bake)
    {
       var files=AssetDatebase.GetAllAssetPath().where(s=>s.EndWith(".unity"));
       foreach(var item in files){
        CombineOneScene(item, bake);
       }  
        EditorUtility.ClearProgressBar();
    }
    /// <summary>
    /// 合併一個場景
    /// </summary>
    /// <param name="path"></param>
    private static void CombineOneScene(string path, bool bake)
    {
        var scene = EditorSceneManager.OpenScene(path);
        PBTitle = "正在合併場景:" + scene.name;
        SceneData sd = GetSceneData(scene);
        if (sd == null) return;
        var combineGO = CreatCombineObj(scene);
        var dic = ClassifyByPositionAndShaderAndProprity(scene);
        int count = 0;
        foreach (var item in dic)
        {
            PBContent = count + "/" + dic.Count;
            PBProcess = (float)count / dic.Count;
            string[] ss = item.Key.Split('|');   //0是座標 1是shader 2是顏色 3是cutoff值
            Combine(item.Value.Where(s => s != null).ToArray(), scene.name, ref count, ss, combineGO.transform);
        }
        sd.Export(bake);
        Debug.Log("合併場景:" + scene.name);
    }

    /// <summary>
    /// 指定位置建立一個Combine物體 用於存放合併後的物體
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns>
    private static GameObject CreatCombineObj(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        GameObject combineGO = new GameObject("Combine");
        foreach (var item in roots)
        {
            if (item.name == scene.name)
            {
                combineGO.transform.SetParent(item.transform);
                break;
            }
        }
        return combineGO;
    }
    /// <summary>
    /// 獲取場景裡的所有隻有一個材質球的MeshRenderer shader為Scene/Diffuse  或者 是 Scene/Cutout/Diffuse  且 貼圖長和寬不能大於1024
    /// </summary>
    /// <param name="s"></param>
    private static MeshRenderer[] GetAllMeshsInScene(UnityEngine.SceneManagement.Scene scene)
    {
        PBState = "查詢符合標準的MeshRenderer";
        const int texture_MaxSize = 1024;
        var roots = scene.GetRootGameObjects();
        var meshrenders = new List<MeshRenderer>();
        foreach (var item in roots)
        {
            meshrenders.AddRange(item.GetComponentsInChildren<MeshRenderer>());
        }
        return meshrenders.Where(s => s
        && s.enabled == true
        && s.sharedMaterials.Length == 1
        && s.sharedMaterial
        && (s.sharedMaterial.shader.name == "scenes/Diffuse"
        || s.sharedMaterial.shader.name == "scenes/Cutout/Diffuse")
        && s.sharedMaterial.GetTexture("_MainTex")
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.x <= texture_MaxSize
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.y <= texture_MaxSize)
        .ToArray();
    }
    /// <summary>
    /// 儲存資源
    /// </summary>
    /// <param name="go">物件</param>
    /// <param name="path">相對於Resource/scene/的路經</param>
    /// <param name="name">名字</param>
    private static void CreateAsset(UnityEngine.Object go, string path, string name)
    {
        PBState = "儲存資源";
        if (!Directory.Exists(PATH + path)) Directory.CreateDirectory(PATH + path);
        AssetDatabase.CreateAsset(go, PATH + path + "/" + name);
    }
    /// <summary>
    /// 根據座標,shader,引數分類
    /// </summary>
    /// <param name="meshrenderers"></param>
    private static Dictionary<string, List<MeshRenderer>> ClassifyByPositionAndShaderAndProprity(UnityEngine.SceneManagement.Scene scene)
    {
        PBState = "分類MeshRenderer";
        Dictionary<string, List<MeshRenderer>> dic = new Dictionary<string, List<MeshRenderer>>();
        foreach (var item in GetAllMeshsInScene(scene))
        {
            Material mt = item.sharedMaterial;
            string tag;
            if (mt.shader.name == "scenes/Cutout/Diffuse")
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Color") + "|" + mt.GetFloat("_Cutoff");
            else
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Colors");
            if (!dic.ContainsKey(tag))
            {
                dic.Add(tag, new List<MeshRenderer>() { item });
            }
            else
            {
                dic[tag].Add(item);
            }
        }
        //清除只有1個的List
        List<string> checkOneRenderList = new List<string>();
        foreach (var item in dic)
        {
            if (item.Value.Count == 1) checkOneRenderList.Add(item.Key);
        }
        foreach (var item in checkOneRenderList)
        {
            dic.Remove(item);
        }
        return dic;
    }
    /// <summary>
    /// 合併網格
    /// </summary>
    /// <param name="renderers"></param>
    ///  <param name="filePath">_tp0_cfg.txt的路徑</param>
    /// <returns></returns>
    private static Mesh CombineMesh(MeshRenderer[] renderers, string filePath)
    {
        PBState = "合併網格";
        var dicOfPackFrame = GetPackedFram(filePath);//根據圖片名稱 拿到在合併後的圖片裡的資訊
        List<Vector2> uvs = new List<Vector2>();   //存放原來的Mesh的UV
        CombineInstance[] cis = new CombineInstance[renderers.Length];
        int uvCount = 0;
        for (int i = 0; i < cis.Length; i++)
        {
            cis[i].mesh = renderers[i].GetComponent<MeshFilter>().sharedMesh;
            cis[i].transform = renderers[i].transform.localToWorldMatrix;
            Texture t = renderers[i].sharedMaterial.GetTexture("_MainTex");
            string textureName = Path.GetFileName(AssetDatabase.GetAssetPath(t));
            uvs.AddRange(cis[i].mesh.uv.Select(s => TransfromUV(s, dicOfPackFrame[textureName], t.wrapMode)).ToArray());
            uvCount += cis[i].mesh.uv.Length;
        }
        Mesh m = new Mesh();
        m.CombineMeshes(cis, true, true);
        m.uv = uvs.ToArray();
        return m;
    }
    /// <summary>
    /// 合併   合併一個List MeshRnder 的  整個邏輯
    /// </summary>
    /// <param name="renderers"></param>
    /// <param name="sceneName">場景名稱 用於標記</param>
    /// <param name="index">計數器</param>
    /// <param name="parameter">material的各個引數   //0是座標 1是shader 2是顏色 3是cutoff值</param>
    /// <param name="combineRoot">合併的根物體 所有合併產生的物體都要放到這個物體下面</param>
    /// <returns></returns>
    private static void Combine(MeshRenderer[] renderers, string sceneName, ref int index, string[] parameter, Transform combineRoot)
    {
        if (renderers.Length < 2) return;
        PBState = "合併";
        var textures = renderers.Select(s => {
            if (s == null)
            {
                Debug.Log(s);
                Debug.Log("有貼圖為空");
            }
            return s.sharedMaterial ? s.sharedMaterial.GetTexture("_MainTex") : null;
        });
        var texturePaths = textures.Select(s => ASSET_PATH + AssetDatabase.GetAssetPath(s)).ToArray();//獲取貼圖的絕對路徑 
        string path = CopyFileToDirectiory(texturePaths, PATH + sceneName + "/" + index);//將其拷唄到指定資料夾下
        if (CombineTexture(path))//若果合併成功
        {
            Mesh m = CombineMesh(renderers, path + "/_tp0_cfg.txt");
            Texture t = AssetDatabase.LoadAssetAtPath<Texture>(path + "/_tp0.tga");
            Material mat = new Material(Shader.Find(parameter[1]));
            if (parameter[1].Contains("Cutoff"))
            {
                mat.SetColor("_Color", ColorParse(parameter[2]));
                mat.SetFloat("_Cutoff", float.Parse(parameter[3]));
            }
            else
            {
                mat.SetColor("_Colors", ColorParse(parameter[2]));
            }
            mat.SetTexture("_MainTex", t);
            GameObject go = new GameObject(index + "", typeof(MeshRenderer), typeof(MeshFilter));
            go.transform.SetParent(combineRoot.transform);
            go.GetComponent<MeshFilter>().sharedMesh = m;
            go.GetComponent<MeshRenderer>().sharedMaterial = mat;
            CreateAsset(m, sceneName + "/" + index, index + "_mesh.asset");
            CreateAsset(mat, sceneName + "/" + index, index + "_mat.asset");
            index++;
            foreach (var item in renderers)
            {
                if (item) DestroyImmediate(item.gameObject, false);
            }
        }
        else//合併貼圖失敗
        {
            if (renderers.Length > 2)
            {
                MeshRenderer[] r1 = new MeshRenderer[renderers.Length / 2];
                MeshRenderer[] r2 = new MeshRenderer[renderers.Length - r1.Length];
                for (int i = 0; i < r1.Length; i++)
                {
                    r1[i] = renderers[i];
                }
                for (int i = 0; i < r2.Length; i++)
                {
                    r2[i] = renderers[r1.Length + i];
                }
                if (r1.Length > 1) Combine(r1, sceneName, ref index, parameter, combineRoot);
                Combine(r2, sceneName, ref index, parameter, combineRoot);
            }
        }
    }
    /// <summary>
    /// 找到一個場景裡的SceneData
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns> 
    private static SceneData GetSceneData(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        foreach (var item in roots)
        {
            SceneData m = item.GetComponentInChildren<SceneData>();
            if (m) return m;
        }
        return null;
    }

    /// <summary>
    /// 輸入一個原本的uv 轉換成合並後貼圖的uv
    /// </summary>
    /// <param name="uv"></param>
    /// <param name="packedFrame"></param>
    /// <param name="twm">5.3.7版本 只有兩種 以後可能會有多種</param>
    /// <returns></returns>
    /// 
    static int tempppp = 0;
    private static Vector2 TransfromUV(Vector2 uv, TexturePacker.PackedFrame packedFrame, TextureWrapMode twm)
    {
        Vector2 temp = uv;
        Vector2 temp2 = uv;
        Vector2 temp3 = uv;
        Vector2 temp4 = uv;
        if (twm == TextureWrapMode.Clamp)
        {
            uv.x = Mathf.Clamp(uv.x, 0, 1);
            uv.y = Mathf.Clamp(uv.y, 0, 1);
        }
        else if (twm == TextureWrapMode.Repeat)//*****************以後可能會有多種
        {
            uv.x = Mathf.Repeat(uv.x, 1);
            uv.y = Mathf.Repeat(uv.y, 1);
        }
        Vector2 altaSize = packedFrame.atlasSize;
        Rect frame = packedFrame.frame;
        Vector2 smallZuoxiaJiao = new Vector2(frame.x, altaSize.y - frame.y - frame.height);//原來圖片的左下角相對於合併後的圖片的位置
        Vector2 point = new Vector2(smallZuoxiaJiao.x + Mathf.CeilToInt(uv.x * frame.width), smallZuoxiaJiao.y + Mathf.CeilToInt(uv.y * frame.height));
        Vector2 result = new Vector2(point.x / altaSize.x, point.y / altaSize.y);
        return result;
    }

    /// <summary>
    /// 合併一個路徑下的圖片 還是放在這個路徑下
    /// </summary>
    /// <param name="path"> 絕對路徑 最後不要加/</param>
    private static bool CombineTexture(string path)
    {
        PBState = "合併貼圖";
        string[] files = Directory.GetFiles(path);
        var maxSize = 2048;
        var poweroftow = "POT";
        var borderPadding = 0;
        var shapePadding = 0;
        var command = GlobalEditorHelper.GetAssetsPath(ASSET_PATH + PATH + "combinetexture.bat");
        Util.ExecuteCmd(EditorConfig.processPath, command, string.Format(" {0} {1} {2} {3} {4} {5}", maxSize, poweroftow, borderPadding, shapePadding, path + "/", path));
        foreach (var item in files)
        {
            File.Delete(item);
        }
        if (Directory.GetFiles(path).Length > 2)  //如果大於2 說明合併了1個以上的貼圖
        {
            foreach (var item in Directory.GetFiles(path))
            {
                File.Delete(item);
            }
            return false;
        }
        else
        {
            AssetDatabase.Refresh();
            return true;
        }
    }

    /// <summary>
    /// 將一個txt檔案解析成List PacekdFram
    /// </summary>
    /// <param name="path"></param>
    private static Dictionary<string, TexturePacker.PackedFrame> GetPackedFram(string path)
    {
        TextAsset txt = AssetDatabase.LoadAssetAtPath<TextAsset>(path.Substring(path.IndexOf("Assets")));
        if (txt == null) Debug.Log(path);
        TexturePacker.MetaData meta = TexturePacker.GetMetaData(txt.text);
        Hashtable table = txt.text.hashtableFromJson();
        List<TexturePacker.PackedFrame> frames = new List<TexturePacker.PackedFrame>();
        Hashtable frameTable = (Hashtable)table["frames"];
        foreach (DictionaryEntry entry in frameTable)
        {
            frames.Add(new TexturePacker.PackedFrame((string)entry.Key, meta.size, (Hashtable)entry.Value));
        }
        return frames.ToDictionary(s => s.name, s => s);
    }

    /// <summary>
    /// 拷唄一些檔案 到另一個資料夾下面
    /// </summary>
    /// <param name="files"></param>
    /// <param name="path">絕對路徑 最後不要加斜線</param>
    private static string CopyFileToDirectiory(string[] files, string path)
    {

        PBState = "拷唄貼圖";
        if (!Directory.Exists(path)) Directory.CreateDirectory(path);
        foreach (var item in files)
        {
            if (File.Exists(item)) File.Copy(item, path + "/" + item.Substring(item.LastIndexOf('/')), true);
        }
        return path;
    }

    /// <summary>
    /// 將一個特定字串解析成Color 
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    private static Color ColorParse(string s)
    {
        s = s.Substring(5, s.Length - 6);
        string[] ss = s.Split(',');
        return new Color(float.Parse(ss[0]), float.Parse(ss[1]), float.Parse(ss[2]), float.Parse(ss[3]));
    }
    /// <summary>
    /// 判讀兩個物體是不是在同一個格子裡
    /// </summary>
    /// <param name="t1"></param>
    /// <param name="t2"></param>
    /// <returns></returns>
    private static bool InSameGrid(Transform t1, Transform t2)
    {
        return (GetGridIndex(t1) == GetGridIndex(t2));
    }
    /// <summary>
    /// 計算一個物體的格子座標
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    private static Vector2 GetGridIndex(Transform t)
    {
        return new Vector2((int)(t.position.x / GRIDSIDE), (int)(t.position.z / GRIDSIDE));
    }
}

BUT!!!
There is a big Problem.
如果原來貼圖的格式是Clamp格式的 那麼以上程式碼完全可行
如果是Repeat格式的 就非常有可能不可行
因為轉換UV座標時 要將原來的UV座標給Math.Clamp 或者 Math.Repeat 處理到0~1
假如兩個相鄰的頂點uv座標是(0.9,0.5)(1.1,0.5) 那麼這兩個點之間本應該draw 0.2個單位
經過Math.Repeat處理一下之後就變成了(0.9,0.5)(0.1,0.5) 那麼這兩個點之間就會draw 0.8個單位 而且還是反著draw的
所以 這個問題基本上無法避免
而且 很無奈的是 要自己處理這個問題會非常費時費力 遇到這種情況就要用外掛了
推薦一款MeshBaker外掛 AssetStore 有免費版的
但是這個外掛是一步一步操作的 所以要寫工具一鍵全部合併還是得靠自己寫程式碼
下載完外掛之後 以下程式碼就能運行了
程式碼:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using UnityEditor.SceneManagement;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using DigitalOpus.MB.Core;

public class CombineTool : Editor
{
    private const int GRIDSIDE = 15;
    private const string PATH = "Assets/Resources/scene/";
    private static string ASSET_PATH = EditorConfig.assetPath;
    private const string LAYER_NAME = "block";
    #region ProgressBar
    private static string title = "";
    private static string PBTitle
    {
        set
        {
            title = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
        get { return title; }
    }
    private static string content = "";
    private static string PBContent
    {
        get { return content; }
        set
        {
            content = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
    }
    private static string state = "";
    private static string PBState
    {
        get { return state; }
        set
        {
            state = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
    }
    private static float process = 0;
    private static float PBProcess
    {
        get { return process; }
        set
        {
            process = value;
            EditorUtility.DisplayProgressBar(title, content + "   " + state, process);
        }
    }
    #endregion

    [MenuItem("Debug/合併場景不烘焙")]
    public static void CombineSceneNotBake()
    {
        CombineScene(false);
    }

    /// <summary>
    /// 合併所有場景
    /// </summary>
    /// <param name="bake"></param>
    private static void CombineScene(bool bake)
    {
          var scenes = AssetDatabase.GetAllAssetPaths().Where(s => s.EndsWith(".unity"));
          foreach (var item in scenes)
          {
              CombineOneScene(item, bake);
          }
      //  CombineOneScene("Assets/Scenes/Scenes01_PLZZ.unity",bake);
        EditorUtility.ClearProgressBar();
    }
    /// <summary>
    /// 合併一個場景
    /// </summary>
    /// <param name="path"></param>
    private static void CombineOneScene(string path, bool bake)
    {
        var scene = EditorSceneManager.OpenScene(path);
        PBTitle = "正在合併場景:" + scene.name;
        SceneData sd = GetSceneData(scene);
        if (sd == null) return;
        var combineGO = CreatCombineObj(scene);
        var dic = ClassifyByPositionAndShaderAndProprity(scene);
        if (dic.Count == 0)
        {
            sd.Export(bake);
            return;
        }
        string directory = PATH + scene.name + "/";
        if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
        int count = 0;
        foreach (var item in dic)
        {
            PBContent = count + "/" + dic.Count;
            PBProcess = (float)count / dic.Count;
            Combine(item.Value.Where(s => s != null).Select(s => s.gameObject).ToArray(), scene, ref count, combineGO.transform, directory);
        }
        sd.Export(bake);
        Debug.Log("合併場景:" + scene.name);
    }

    /// <summary>
    /// 指定位置建立一個Combine物體 用於存放合併後的物體
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns>
    private static GameObject CreatCombineObj(UnityEngine.SceneManagement.Scene scene)
    {
        var roots = scene.GetRootGameObjects();
        GameObject combineGO = new GameObject("Combine");
        foreach (var item in roots)
        {
            if (item.name == scene.name)
            {
                combineGO.transform.SetParent(item.transform);
                break;
            }
        }
        return combineGO;
    }
    /// <summary>
    /// 獲取場景裡的所有隻有一個材質球的MeshRenderer shader為Scene/Diffuse  或者 是 Scene/Cutout/Diffuse  且 貼圖長和寬不能大於1024
    /// </summary>
    /// <param name="s"></param>
    private static MeshRenderer[] GetAllMeshsInScene(UnityEngine.SceneManagement.Scene scene)
    {
        const int texture_MaxSize = 1024;
        var roots = scene.GetRootGameObjects();
        var meshrenders = new List<MeshRenderer>();
        GameObject combineGO = new GameObject("Combine");
        foreach (var item in roots)
        {
            if (item.name == scene.name)
            {
                combineGO.transform.SetParent(item.transform);
                meshrenders.AddRange(item.GetComponentsInChildren<MeshRenderer>());
                break;
            }
        }
        foreach (var item in meshrenders.Where(s => s.gameObject.layer == LayerMask.NameToLayer(LAYER_NAME)))
        {
            item.transform.SetParent(combineGO.transform);
        } 
        return meshrenders.Where(s => s
        && s.gameObject.layer!=LayerMask.NameToLayer(LAYER_NAME)
        && s.enabled == true
        && s.sharedMaterials.Length == 1
        && s.sharedMaterial
        && (s.sharedMaterial.shader.name == "scenes/Diffuse"
        || s.sharedMaterial.shader.name == "scenes/Cutout/Diffuse")
        && s.sharedMaterial.GetTexture("_MainTex")
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.x <= texture_MaxSize
        && s.sharedMaterial.GetTexture("_MainTex").texelSize.y <= texture_MaxSize)
        .ToArray();
    }

    /// <summary>
    /// 根據座標,shader,引數分類
    /// </summary>
    /// <param name="meshrenderers"></param>
    private static Dictionary<string, List<MeshRenderer>> ClassifyByPositionAndShaderAndProprity(UnityEngine.SceneManagement.Scene

scene)
    {
        Dictionary<string, List<MeshRenderer>> dic = new Dictionary<string, List<MeshRenderer>>();
        foreach (var item in GetAllMeshsInScene(scene))
        {
            Material mt = item.sharedMaterial;
            string tag;
            if (mt.shader.name == "scenes/Cutout/Diffuse")
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Color") + "|" + mt.GetFloat

("_Cutoff");
            else
                tag = GetGridIndex(item.transform) + "|" + mt.shader.name + "|" + mt.GetColor("_Colors");
            if (!dic.ContainsKey(tag))
            {
                dic.Add(tag, new List<MeshRenderer>() { item });
            }
            else
            {
                dic[tag].Add(item);
            }
        }
        //清除只有1個的List
        List<string> checkOneRenderList = new List<string>();
        foreach (var item in dic)
        {
            if (item.Value.Count == 1) checkOneRenderList.Add(item.Key);
        }
        foreach (var item in checkOneRenderList)
        {
            dic.Remove(item);
        }
        return dic;
    }
    /// <summary>
    /// 合併   合併一個List MeshRnder 的  整個邏輯
    /// </summary>
    /// <param name="renderers"></param>
    /// <param name="sceneName">場景名稱 用於標記</param>
    /// <param name="index">計數器</param>
    /// <param name="parameter">material的各個引數   //0是座標 1是shader 2是顏色 3是cutoff值</param>
    /// <param name="combineRoot">合併的根物體 所有合併產生的物體都要放到這個物體下面</param>
    /// <returns></returns>
    private static void Combine(GameObject[] gos, UnityEngine.SceneManagement.Scene scene, ref int index, Transform

combineRoot, string directory)
    {
        if (gos.Length < 2) return;
        GameObject tempgo = new GameObject(scene.name + "_combine_" + index);
        tempgo.transform.SetParent(combineRoot);

        GameObject meshbaker = MB3_MeshBakerEditor.CreateNewMeshBaker();
        meshbaker.transform.SetParent(tempgo.transform);
        MB3_TextureBaker mbtb = meshbaker.GetComponent<MB3_TextureBaker>();
        MB3_MeshBaker mbmb = meshbaker.GetComponentInChildren<MB3_MeshBaker>();
        mbtb.fixOutOfBoundsUVs = true;
        mbtb.maxAtlasSize = 2048;
        mbtb.maxTilingBakeSize = 1024;
        mbtb.atlasPadding = 0;
        mbtb.considerNonTextureProperties = true;
        mbtb.meshBakerTexturePackerForcePowerOfTwo = true;
        mbtb.objsToMesh = gos.ToList();
        //mbtb.textureBakeResults  這個賦值會包含在下面這個方法裡
        MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(mbtb, directory + index + ".asset");
        mbtb.CreateAtlases(null, true, new MB3_EditorMethods());
        if (mbtb.textureBakeResults != null) EditorUtility.SetDirty(mbtb.textureBakeResults);

        mbmb.textureBakeResults = mbtb.textureBakeResults;
        mbmb.CombinedMeshContains(tempgo);
        mbmb.useObjsToMeshFromTexBaker = true;
        mbmb.meshCombiner.doNorm = true;
        mbmb.meshCombiner.doTan = true;
        mbmb.meshCombiner.doUV = true;
        mbmb.resultPrefab = tempgo;
        mbmb.meshCombiner.outputOption = MB2_OutputOptions.bakeIntoSceneObject;
        mbmb.meshCombiner.resultSceneObject = tempgo;
       // mbmb.meshCombiner.targetRenderer = tempgo.AddComponent<MeshRenderer>();
        mbmb.meshCombiner.Apply();
        MB3_MeshBakerEditorInternal.bake(mbmb);

        Mesh tempMesh = tempgo.GetComponentInChildren<MeshFilter>().sharedMesh;
        AssetDatabase.CreateAsset(tempMesh, directory +