日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MeshBaker一键合并网格编辑器实现

發布時間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MeshBaker一键合并网格编辑器实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

MeshBaker是一款性能優化的插件。它可以做到材質、網格合并,從而降低渲染消耗。
MeshBaker

功能

本次編寫的編輯器對場景特定目錄下的網格進行合并,隱藏原零散網格,并將合并后的網格掛在CombinedMeshNode節點,由于移動端打包后在手機上沒有GI效果,故合批后刪除了放置光源重新烘焙的過程,有需要的話自行補充編寫。

代碼

實現流程

// ******************************************************** // 描述:自動烘焙器 自動分析當前場景 對網格和材質進行合批 // 作者:ShadowRabbit // 創建時間: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() {//數據儲存目錄檢測Abi.CheckGenerateAssetsFolder();//組合節點檢測Abi.CheckCombinedNode();//組合節點var combinedNode = Abi.GetCombinedNode();//場景自動解析var sceneAnalysisResults = Abi.AutoAnalysis();//合并網格和材質var generateAssetsFolder = Abi.GetGenerateAssetsFolder();Abi.CombineMeshAndMaterials(sceneAnalysisResults, generateAssetsFolder, combinedNode);//重新生成光照//因打包后移動端GI不生效 故省略此過程}} }

具體實現過程

// ******************************************************** // AutoBakerInternal.cs // 描述: // 作者:ShadowRabbit // 創建時間: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"; //所有生成的組合網格將掛載于場景中的這個節點下private Transform combinedNode;/// <summary>/// 目錄檢測/// </summary>public void CheckGenerateAssetsFolder() {//資源生成路徑var folder = GetGenerateAssetsFolder();//創建數據儲存目錄if (!Directory.Exists(folder)) {Directory.CreateDirectory(folder);}//刪除目錄下舊數據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>/// 組合節點檢測/// </summary>public void CheckCombinedNode() {var objCombined = GameObject.Find(CombinedMeshNodeName);//銷毀掛載節點if (objCombined != null) {Undo.DestroyObjectImmediate(objCombined);}combinedNode = new GameObject {name = CombinedMeshNodeName}.transform;}/// <summary>/// 獲取組合節點/// </summary>public Transform GetCombinedNode() {if (combinedNode == null) {Debug.LogWarning("組合節點不存在");}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);//儲存當前烘焙數據的路徑return sb.ToString();}/// <summary>/// 合并網格和材質/// </summary>/// <param name="sceneAnalysisResults">分析結果</param>/// <param name="generateAssetsFolder">烘焙數據保存路徑</param>/// <param name="generateNode">生成后需要掛載的節點</param>public void CombineMeshAndMaterials(IEnumerable<List<GameObjectFilterInfo>> sceneAnalysisResults,string generateAssetsFolder, Transform generateNode) {var buildingNode = FindBuildingNode();if (buildingNode == null) {Debug.LogError("建筑節點不存在");return;}//激活建筑節點buildingNode.gameObject.SetActive(true);//根據解析結果創建meshBakerforeach (var objFilterInfoList in sceneAnalysisResults) {//分析結果過濾var activeObjFilterInfoList = objFilterInfoList.Where(objFilterInfo => objFilterInfo.go.IsValid()).Where(objFilterInfo => objFilterInfo.go.IsInBuildingNode());var gameObjectFilterInfos = activeObjFilterInfoList.ToList();//沒有有效數據if (!gameObjectFilterInfos.Any()) {Debug.LogWarning("沒有有效數據");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);}//建筑節點失活buildingNode.gameObject.SetActive(false);}/// <summary>/// 自動分析場景/// </summary>/// <returns></returns>public IEnumerable<List<GameObjectFilterInfo>> AutoAnalysis() {try {EditorUtility.DisplayProgressBar("自動合并烘焙", "合并開始", .99f);//過濾組var filters = GetFilters();//已經包含在baker中的objsEditorUtility.DisplayProgressBar("自動合并烘焙", "開始計算baker中的objs", .7f);GetObjectsAlreadyIncludedInBakers(out var objectsAlreadyIncludedInBakers);//獲取場景中obj過濾信息列表EditorUtility.DisplayProgressBar("自動合并烘焙", "獲取物體過濾信息列表", .5f);GetObjectFilterInfos(objectsAlreadyIncludedInBakers, filters, out var gameObjectFilterInfoList);//網格分析EditorUtility.DisplayProgressBar("自動合并烘焙", "正在分析網格數據", .4f);MeshAnalysis(gameObjectFilterInfoList);//不會被添加到烘焙器中的obj集合var objsNotAddedToBaker = new List<GameObjectFilterInfo>();//分析結果 EditorUtility.DisplayProgressBar("自動合并烘焙", "正在分析結果", .3f);var mapAnalysisResults =MB3_MeshBakerEditorWindow.sortIntoBakeGroups3(gameObjectFilterInfoList, objsNotAddedToBaker,filters.ToArray(),true, AtlasSize);//三元集合轉成2元EditorUtility.DisplayProgressBar("自動合并烘焙", "正在轉換結果", .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("合并網格中", msg, progress);}/// <summary>/// 創建baker/// </summary>/// <param name="objFilterInfoList">過濾信息集合</param>/// <param name="dataSavePath">數據儲存路徑</param>private GameObject CreateAndSetupBaker(IList<GameObjectFilterInfo> objFilterInfoList,string dataSavePath) {//清除無效數據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;}//頂點數累加var numVerts = objFilterInfoList.Sum(t => t.numVerts);//頂點數超過整型最大了 使用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();//去重復 添加到待組合列表中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) {// 多材質有bug 暫時不調用// //材質儲存路徑// var pthMat = AssetDatabase.GenerateUniqueAssetPath(dataSavePath + newMeshBaker.name + ".asset");// //創建圖集// 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 + "使用了多個材質,由于MeshBaker同一網格使用多材質有bug,故本次沒有調用");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>/// 數據結構轉換/// </summary>/// <param name="mapAnalysisResults"></param>/// <param name="sceneAnalysisResults"></param>private void AnalysisResultsMapToList(Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> mapAnalysisResults,out List<List<GameObjectFilterInfo>> sceneAnalysisResults) {//三元集合轉成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>/// 獲取已經包含在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">已經包含在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());// //沒有材質的不考慮// if (objFilterInfo.materials.Length <= 0) continue;// gameObjectFilterInfoList.Add(objFilterInfo);// }}/// <summary>/// mesh相關分析/// </summary>private void MeshAnalysis(IReadOnlyList<GameObjectFilterInfo> gameObjects) {//網格分析 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); //當前物體的網格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>();//多個子網格使用相同材質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>/// 查找建筑節點/// </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">關鍵詞</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 // 創建時間: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) {//當前節點沒有激活if (!gameObject.activeSelf) {Debug.Log("失效物體:" + gameObject.name + "原因:自身失效");return false;}//父節點var currentTransform = gameObject.transform.parent;//遍歷節點鏈表 查找父節點中是否有節點未激活while (currentTransform != null) {if (!currentTransform.gameObject.activeSelf) {Debug.Log("失效物體:" + gameObject.name + "原因:父節點" + currentTransform.gameObject.name + "失效");return false;}currentTransform = currentTransform.parent;}return true;}/// <summary>/// 判斷某個物體是否在Building節點下/// </summary>/// <param name="gameObject"></param>/// <returns></returns>public static bool IsInBuildingNode(this GameObject gameObject) {//建筑節點Transform buildingNode = null;//遍歷父節點var parent = gameObject.transform.parent;while (parent != null) {if (parent.name.Equals("Building")) {buildingNode = parent;break;}parent = parent.parent;}//建筑節點不存在if (buildingNode==null) {return false;}var rootNode = buildingNode.parent;//建筑節點沒有在根節點上if (!rootNode.name.Equals("Root")) {return false;}return rootNode.parent == null;}} }

總結

以上是生活随笔為你收集整理的MeshBaker一键合并网格编辑器实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。