MeshBaker一键合并网格编辑器实现
生活随笔
收集整理的這篇文章主要介紹了
MeshBaker一键合并网格编辑器实现
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
簡介
MeshBaker是一款性能優(yōu)化的插件。它可以做到材質(zhì)、網(wǎng)格合并,從而降低渲染消耗。
MeshBaker
功能
本次編寫的編輯器對場景特定目錄下的網(wǎng)格進(jìn)行合并,隱藏原零散網(wǎng)格,并將合并后的網(wǎng)格掛在CombinedMeshNode節(jié)點,由于移動端打包后在手機上沒有GI效果,故合批后刪除了放置光源重新烘焙的過程,有需要的話自行補充編寫。
代碼
實現(xiàn)流程
// ******************************************************** // 描述:自動烘焙器 自動分析當(dāng)前場景 對網(wǎng)格和材質(zhì)進(jìn)行合批 // 作者:ShadowRabbit // 創(chuàng)建時間:2020年7月30日 20:42:14 // ********************************************************using UnityEditor;namespace EditorTools.MeshBakerEditor {public static class AutoBaker{private static readonly AutoBakerInternal Abi = new AutoBakerInternal();/// <summary>/// 自動烘焙/// </summary>[MenuItem("Tools/Mesh Baker/AutoBake &-", false, 100)]public static void AutoBake() {//數(shù)據(jù)儲存目錄檢測Abi.CheckGenerateAssetsFolder();//組合節(jié)點檢測Abi.CheckCombinedNode();//組合節(jié)點var combinedNode = Abi.GetCombinedNode();//場景自動解析var sceneAnalysisResults = Abi.AutoAnalysis();//合并網(wǎng)格和材質(zhì)var generateAssetsFolder = Abi.GetGenerateAssetsFolder();Abi.CombineMeshAndMaterials(sceneAnalysisResults, generateAssetsFolder, combinedNode);//重新生成光照//因打包后移動端GI不生效 故省略此過程}} }具體實現(xiàn)過程
// ******************************************************** // AutoBakerInternal.cs // 描述: // 作者:ShadowRabbit // 創(chuàng)建時間:2020年8月3日 14:22:08 // ********************************************************using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using DigitalOpus.MB.Core; using EditorTools.MeshBakerEditor.Extension; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement;namespace EditorTools.MeshBakerEditor {public class AutoBakerInternal{private const int MaxVertex = 65535;private const int AtlasSize = 2048;private const string CombinedMeshNodeName = "CombinedMeshNode"; //所有生成的組合網(wǎng)格將掛載于場景中的這個節(jié)點下private Transform combinedNode;/// <summary>/// 目錄檢測/// </summary>public void CheckGenerateAssetsFolder() {//資源生成路徑var folder = GetGenerateAssetsFolder();//創(chuàng)建數(shù)據(jù)儲存目錄if (!Directory.Exists(folder)) {Directory.CreateDirectory(folder);}//刪除目錄下舊數(shù)據(jù)var directionInfo = new DirectoryInfo(folder);var fileInfos = directionInfo.GetFiles("*", SearchOption.AllDirectories);foreach (var t in fileInfos) {var filePath = folder + "/" + t.Name;File.Delete(filePath);}}/// <summary>/// 組合節(jié)點檢測/// </summary>public void CheckCombinedNode() {var objCombined = GameObject.Find(CombinedMeshNodeName);//銷毀掛載節(jié)點if (objCombined != null) {Undo.DestroyObjectImmediate(objCombined);}combinedNode = new GameObject {name = CombinedMeshNodeName}.transform;}/// <summary>/// 獲取組合節(jié)點/// </summary>public Transform GetCombinedNode() {if (combinedNode == null) {Debug.LogWarning("組合節(jié)點不存在");}return combinedNode;}/// <summary>/// 獲取烘焙資源保存路徑/// </summary>/// <returns></returns>public string GetGenerateAssetsFolder() {var sb = new StringBuilder();var scenePath = FindSceneAssetPath();sb.Append(scenePath);sb.Append("/MeshBakerData");sb.Append("/");Debug.Log("資源文件生成目錄:" + sb);//儲存當(dāng)前烘焙數(shù)據(jù)的路徑return sb.ToString();}/// <summary>/// 合并網(wǎng)格和材質(zhì)/// </summary>/// <param name="sceneAnalysisResults">分析結(jié)果</param>/// <param name="generateAssetsFolder">烘焙數(shù)據(jù)保存路徑</param>/// <param name="generateNode">生成后需要掛載的節(jié)點</param>public void CombineMeshAndMaterials(IEnumerable<List<GameObjectFilterInfo>> sceneAnalysisResults,string generateAssetsFolder, Transform generateNode) {var buildingNode = FindBuildingNode();if (buildingNode == null) {Debug.LogError("建筑節(jié)點不存在");return;}//激活建筑節(jié)點buildingNode.gameObject.SetActive(true);//根據(jù)解析結(jié)果創(chuàng)建meshBakerforeach (var objFilterInfoList in sceneAnalysisResults) {//分析結(jié)果過濾var activeObjFilterInfoList = objFilterInfoList.Where(objFilterInfo => objFilterInfo.go.IsValid()).Where(objFilterInfo => objFilterInfo.go.IsInBuildingNode());var gameObjectFilterInfos = activeObjFilterInfoList.ToList();//沒有有效數(shù)據(jù)if (!gameObjectFilterInfos.Any()) {Debug.LogWarning("沒有有效數(shù)據(jù)");continue;}var obj = CreateAndSetupBaker(gameObjectFilterInfos, generateAssetsFolder);var texBaker = obj.GetComponent<MB3_TextureBaker>();texBaker.CreateAtlases(UpdateProgressBar, true, new MB3_EditorMethods());EditorUtility.ClearProgressBar();if (texBaker.textureBakeResults != null) EditorUtility.SetDirty(texBaker.textureBakeResults);var meshBaker = obj.GetComponentInChildren<MB3_MeshBaker>();//保留原有光照貼圖 注意光照貼圖索引不同的物體組合在一起,光照貼圖將無法生效meshBaker.meshCombiner.lightmapOption = MB2_LightmapOptions.preserve_current_lightmapping;MB3_MeshBakerEditorInternal.bake(meshBaker);meshBaker.meshCombiner.resultSceneObject.transform.SetParent(generateNode);Undo.DestroyObjectImmediate(obj);}//建筑節(jié)點失活buildingNode.gameObject.SetActive(false);}/// <summary>/// 自動分析場景/// </summary>/// <returns></returns>public IEnumerable<List<GameObjectFilterInfo>> AutoAnalysis() {try {EditorUtility.DisplayProgressBar("自動合并烘焙", "合并開始", .99f);//過濾組var filters = GetFilters();//已經(jīng)包含在baker中的objsEditorUtility.DisplayProgressBar("自動合并烘焙", "開始計算baker中的objs", .7f);GetObjectsAlreadyIncludedInBakers(out var objectsAlreadyIncludedInBakers);//獲取場景中obj過濾信息列表EditorUtility.DisplayProgressBar("自動合并烘焙", "獲取物體過濾信息列表", .5f);GetObjectFilterInfos(objectsAlreadyIncludedInBakers, filters, out var gameObjectFilterInfoList);//網(wǎng)格分析EditorUtility.DisplayProgressBar("自動合并烘焙", "正在分析網(wǎng)格數(shù)據(jù)", .4f);MeshAnalysis(gameObjectFilterInfoList);//不會被添加到烘焙器中的obj集合var objsNotAddedToBaker = new List<GameObjectFilterInfo>();//分析結(jié)果 EditorUtility.DisplayProgressBar("自動合并烘焙", "正在分析結(jié)果", .3f);var mapAnalysisResults =MB3_MeshBakerEditorWindow.sortIntoBakeGroups3(gameObjectFilterInfoList, objsNotAddedToBaker,filters.ToArray(),true, AtlasSize);//三元集合轉(zhuǎn)成2元EditorUtility.DisplayProgressBar("自動合并烘焙", "正在轉(zhuǎn)換結(jié)果", .2f);AnalysisResultsMapToList(mapAnalysisResults, out var sceneAnalysisResults);return sceneAnalysisResults;}catch (Exception ex) {Debug.LogError(ex.StackTrace);}finally {EditorUtility.ClearProgressBar();}return null;}private void UpdateProgressBar(string msg, float progress) {EditorUtility.DisplayProgressBar("合并網(wǎng)格中", msg, progress);}/// <summary>/// 創(chuàng)建baker/// </summary>/// <param name="objFilterInfoList">過濾信息集合</param>/// <param name="dataSavePath">數(shù)據(jù)儲存路徑</param>private GameObject CreateAndSetupBaker(IList<GameObjectFilterInfo> objFilterInfoList,string dataSavePath) {//清除無效數(shù)據(jù)for (var i = objFilterInfoList.Count - 1; i >= 0; i--) {if (objFilterInfoList[i].go == null) objFilterInfoList.RemoveAt(i);}if (objFilterInfoList.Count < 1) {Debug.LogError("空的過濾器信息");return null;}if (string.IsNullOrEmpty(dataSavePath)) {Debug.LogError("空的儲存目錄");return null;}//頂點數(shù)累加var numVerts = objFilterInfoList.Sum(t => t.numVerts);//頂點數(shù)超過整型最大了 使用multiMeshBakervar newMeshBaker = numVerts >= MaxVertex? MB3_MultiMeshBakerEditor.CreateNewMeshBaker(): MB3_MeshBakerEditor.CreateNewMeshBaker();newMeshBaker.name =("MeshBaker-" + objFilterInfoList[0].shaderName + "-LM" + objFilterInfoList[0].lightmapIndex).Replace("/", "-");var tb = newMeshBaker.GetComponent<MB3_TextureBaker>();var mb = tb.GetComponentInChildren<MB3_MeshBakerCommon>();tb.GetObjectsToCombine().Clear();//去重復(fù) 添加到待組合列表中foreach (var t in objFilterInfoList) {if (t.go != null && !tb.GetObjectsToCombine().Contains(t.go)) {tb.GetObjectsToCombine().Add(t.go);}}tb.maxAtlasSize = AtlasSize;if (objFilterInfoList[0].numMaterials > 1) {// 多材質(zhì)有bug 暫時不調(diào)用// //材質(zhì)儲存路徑// var pthMat = AssetDatabase.GenerateUniqueAssetPath(dataSavePath + newMeshBaker.name + ".asset");// //創(chuàng)建圖集// MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat);// tb.doMultiMaterial = true;// var tbr = new SerializedObject(tb);// var resultMaterials = tbr.FindProperty("resultMaterials");// MB3_TextureBakerEditorInternal.ConfigureMutiMaterialsFromObjsToCombine2(tb, resultMaterials, tbr);Debug.LogWarning(objFilterInfoList[0].go.name + "使用了多個材質(zhì),由于MeshBaker同一網(wǎng)格使用多材質(zhì)有bug,故本次沒有調(diào)用");var pthMat = AssetDatabase.GenerateUniqueAssetPath(dataSavePath + newMeshBaker.name + ".asset");MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat);}else {var pthMat = AssetDatabase.GenerateUniqueAssetPath(dataSavePath + newMeshBaker.name + ".asset");MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat);}mb.meshCombiner.renderType = objFilterInfoList[0].isMeshRenderer? MB_RenderType.meshRenderer: MB_RenderType.skinnedMeshRenderer;return newMeshBaker;}/// <summary>/// 數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換/// </summary>/// <param name="mapAnalysisResults"></param>/// <param name="sceneAnalysisResults"></param>private void AnalysisResultsMapToList(Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> mapAnalysisResults,out List<List<GameObjectFilterInfo>> sceneAnalysisResults) {//三元集合轉(zhuǎn)成2元sceneAnalysisResults = new List<List<GameObjectFilterInfo>>();foreach (var gow in mapAnalysisResults.Keys) {var gows = mapAnalysisResults[gow];sceneAnalysisResults.AddRange(gows);}}/// <summary>/// 獲取過濾組/// </summary>/// <returns></returns>private List<IGroupByFilter> GetFilters() {var filters = new List<IGroupByFilter>();var filterByShader = new GroupByShader();var filterByRenderType = new GroupByRenderType();var filterByStatic = new GroupByStatic();var filterByOutOfBoundsUVs = new GroupByOutOfBoundsUVs();var filterByLightMapIndex = new GroupByLightmapIndex();filters.Add(filterByShader);filters.Add(filterByRenderType);filters.Add(filterByStatic);filters.Add(filterByOutOfBoundsUVs);filters.Add(filterByLightMapIndex);return filters;}/// <summary>/// 獲取已經(jīng)包含在baker中的objs/// </summary>/// <param name="objectsAlreadyIncludedInBakers"></param>private void GetObjectsAlreadyIncludedInBakers(out HashSet<GameObject> objectsAlreadyIncludedInBakers) {//尋找場景中的bakervar allBakers = Resources.FindObjectsOfTypeAll<MB3_MeshBakerRoot>();objectsAlreadyIncludedInBakers = new HashSet<GameObject>();//遍歷bakerforeach (var t in allBakers) {//某個baker中等待組合的obj集合var objsToCombine = t.GetObjectsToCombine();//將等待組合的objs存入objectsAlreadyIncludedInBakersforeach (var t1 in objsToCombine.Where(t1 => t1 != null)) {objectsAlreadyIncludedInBakers.Add(t1);}}}/// <summary>/// 獲取場景中obj過濾信息列表/// </summary>/// <param name="objectsAlreadyIncludedInBakers">已經(jīng)包含在baker中的objs</param>/// <param name="filters">過濾器組</param>/// <param name="gameObjectFilterInfoList">obj過濾信息列表</param>/// <returns>obj過濾信息列表</returns>private void GetObjectFilterInfos(HashSet<GameObject> objectsAlreadyIncludedInBakers,List<IGroupByFilter> filters, out List<GameObjectFilterInfo> gameObjectFilterInfoList) {gameObjectFilterInfoList = new List<GameObjectFilterInfo>();//獲取場景中所有renderer組件var renderers = (Renderer[]) Resources.FindObjectsOfTypeAll(typeof(Renderer));gameObjectFilterInfoList.AddRange(from renderer in rendererswhere renderer is MeshRenderer || renderer is SkinnedMeshRendererwhere renderer.GetComponent<TextMesh>() == nullselect new GameObjectFilterInfo(renderer.gameObject, objectsAlreadyIncludedInBakers, filters.ToArray())into objFilterInfowhere objFilterInfo.materials.Length > 0select objFilterInfo);// foreach (var renderer in renderers) {// if (!(renderer is MeshRenderer) && !(renderer is SkinnedMeshRenderer)) continue;// //不管textMesh// if (renderer.GetComponent<TextMesh>() != null) {// continue;// }// //實例化obj過濾器信息// var objFilterInfo = new GameObjectFilterInfo(renderer.gameObject, objectsAlreadyIncludedInBakers,// filters.ToArray());// //沒有材質(zhì)的不考慮// if (objFilterInfo.materials.Length <= 0) continue;// gameObjectFilterInfoList.Add(objFilterInfo);// }}/// <summary>/// mesh相關(guān)分析/// </summary>private void MeshAnalysis(IReadOnlyList<GameObjectFilterInfo> gameObjects) {//網(wǎng)格分析 meshvar meshAnalysisResultCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>();//遍歷obj過濾器信息集合for (var i = 0; i < gameObjects.Count; i++) {var rpt = $"Processing {gameObjects[i].go.name} [{i} of {gameObjects.Count}]";var mm = MB_Utility.GetMesh(gameObjects[i].go); //當(dāng)前物體的網(wǎng)格if (mm != null) {if (!meshAnalysisResultCache.TryGetValue(mm.GetInstanceID(), out var mar)) {EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Check Out Of Bounds UVs", .6f);MB_Utility.hasOutOfBoundsUVs(mm, ref mar);MB_Utility.doSubmeshesShareVertsOrTris(mm, ref mar);meshAnalysisResultCache.Add(mm.GetInstanceID(), mar);}//uv越界檢測if (mar.hasOutOfBoundsUVs) {var w = (int) mar.uvRect.width;var h = (int) mar.uvRect.height;gameObjects[i].outOfBoundsUVs = true;gameObjects[i].warning +=" [WARNING: has uvs outside the range (0,1) tex is tiled " + w + "x" + h +" times]";}//頂點重合if (mar.hasOverlappingSubmeshVerts) {gameObjects[i].submeshesOverlap = true;gameObjects[i].warning +=" [WARNING: Submeshes share verts or triangles. 'Multiple Combined Materials' feature may not work.]";}}var mr = gameObjects[i].go.GetComponent<Renderer>();//多個子網(wǎng)格使用相同材質(zhì)if (!MB_Utility.AreAllSharedMaterialsDistinct(mr.sharedMaterials)) {gameObjects[i].warning +=" [WARNING: Object uses same material on multiple submeshes. This may produce poor results when used with multiple materials or fix out of bounds uvs.]";}}}/// <summary>/// 查找建筑節(jié)點/// </summary>/// <returns></returns>private Transform FindBuildingNode() {var transforms = Resources.FindObjectsOfTypeAll<Transform>();return transforms.FirstOrDefault(transform => transform.name.Equals("Building"));}/// <summary>/// 尋找場景資源路徑/// </summary>private string FindSceneAssetPath() {var scene = SceneManager.GetActiveScene();var scenePath = scene.path;var directoryInfo = Directory.GetParent(scenePath);var fullName = directoryInfo.FullName.Replace("\\", "/");var relativePath = fullName.Substring(fullName.IndexOf("Assets", StringComparison.Ordinal));return relativePath;}//遞歸壓棧搜索目錄,因為有api暫時廢棄了,如果有需要再粘回來#region Abandoned/// <summary>/// 目錄搜索/// </summary>/// <param name="path">要搜索的目錄</param>/// <param name="targetPath">關(guān)鍵詞</param>private string SearchPath(string path, string targetPath) {var pathStack = new Stack<string>();PushPaths(ref pathStack, path);while (pathStack.Count > 0) {var currentPath = pathStack.Pop();if (!currentPath.EndsWith(targetPath)) continue;return currentPath;}Debug.Log(path + "中不存在目錄" + targetPath);return string.Empty;}/// <summary>/// 將待檢測的path入棧/// </summary>/// <param name="pathStack"></param>/// <param name="path"></param>private void PushPaths(ref Stack<string> pathStack, string path) {if (pathStack == null) {return;}if (string.IsNullOrEmpty(path)) {return;}var fixPath = path.Replace("\\", "/");pathStack.Push(fixPath);//目錄修正var subPaths = Directory.GetDirectories(fixPath);//存在子目錄if (subPaths.Length <= 0) return;foreach (var subPath in subPaths) {var fixSubPath = subPath.Replace("\\", "/");PushPaths(ref pathStack, fixSubPath);}}#endregion} }工具類擴展
// ******************************************************** // MBGameObjectExtension.cs // 描述:GameObject輔助擴展類 // 作者:ShadowRabbit // 創(chuàng)建時間:2020年8月1日 10:43:34 // ********************************************************using UnityEngine;namespace EditorTools.MeshBakerEditor.Extension {public static class MbGameObjectExtension{/// <summary>/// 判斷某個物體是否有效/// </summary>/// <param name="gameObject"></param>/// <returns></returns>public static bool IsValid(this GameObject gameObject) {//當(dāng)前節(jié)點沒有激活if (!gameObject.activeSelf) {Debug.Log("失效物體:" + gameObject.name + "原因:自身失效");return false;}//父節(jié)點var currentTransform = gameObject.transform.parent;//遍歷節(jié)點鏈表 查找父節(jié)點中是否有節(jié)點未激活while (currentTransform != null) {if (!currentTransform.gameObject.activeSelf) {Debug.Log("失效物體:" + gameObject.name + "原因:父節(jié)點" + currentTransform.gameObject.name + "失效");return false;}currentTransform = currentTransform.parent;}return true;}/// <summary>/// 判斷某個物體是否在Building節(jié)點下/// </summary>/// <param name="gameObject"></param>/// <returns></returns>public static bool IsInBuildingNode(this GameObject gameObject) {//建筑節(jié)點Transform buildingNode = null;//遍歷父節(jié)點var parent = gameObject.transform.parent;while (parent != null) {if (parent.name.Equals("Building")) {buildingNode = parent;break;}parent = parent.parent;}//建筑節(jié)點不存在if (buildingNode==null) {return false;}var rootNode = buildingNode.parent;//建筑節(jié)點沒有在根節(jié)點上if (!rootNode.name.Equals("Root")) {return false;}return rootNode.parent == null;}} }總結(jié)
以上是生活随笔為你收集整理的MeshBaker一键合并网格编辑器实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言用循环输出塔状五行,从键盘上输入一
- 下一篇: 安卓 修改键盘确定按钮状态,并获取对应点