From 45b5b618d9e9dec77cb426eff9e76c8f0e1cc786 Mon Sep 17 00:00:00 2001 From: pochang007 Date: Tue, 2 Feb 2021 12:18:42 +0900 Subject: [PATCH] moved BoneMeshEraser to Meshutility editor --- .../Editor/BoneMeshEraserWizard.cs | 3 +- .../MeshUtility/Editor/MeshProcessDialog.cs | 221 +++++++++++++++++- .../UniGLTF/MeshUtility/Editor/MeshUtility.cs | 6 +- 3 files changed, 218 insertions(+), 12 deletions(-) diff --git a/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs b/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs index b28d0a50c7..1af6af2d0a 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs @@ -37,6 +37,7 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent public class BoneMeshEraserWizard : ScriptableWizard { + public const string BONE_MESH_ERASER_NAME = "BoneMeshEraser"; const string ASSET_SUFFIX = ".asset"; [SerializeField] @@ -132,7 +133,7 @@ SkinnedMeshRenderer _Erase(GameObject go) .Select(x => Array.IndexOf(bones, x.Bone)) .ToArray(); - var meshNode = new GameObject("BoneMeshEraser"); + var meshNode = new GameObject(BONE_MESH_ERASER_NAME); meshNode.transform.SetParent(go.transform, false); var erased = meshNode.AddComponent(); diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs index 6cecdc4e4c..88a8525926 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Linq; using System.Reflection; using UnityEngine; using UnityEditor; @@ -6,6 +8,24 @@ namespace MeshUtility { + [CustomEditor(typeof(MeshProcessDialog), true)] + public class BoneMeshEraserGUI : Editor + { + public override void OnInspectorGUI() + { + serializedObject.Update(); + var skinnedMesh = serializedObject.FindProperty("_cSkinnedMesh"); + EditorGUILayout.PropertyField(skinnedMesh, new GUIContent("Skinned Mesh"), true); + var animator = serializedObject.FindProperty("_cAnimator"); + EditorGUILayout.PropertyField(animator, new GUIContent("Animator"), false); + var eraseRoot = serializedObject.FindProperty("_cEraseRoot"); + EditorGUILayout.PropertyField(eraseRoot, new GUIContent("Erase Root"), false); + var list = serializedObject.FindProperty("_eraseBones"); + EditorGUILayout.PropertyField(list, new GUIContent("Erase Bones"), true); + serializedObject.ApplyModifiedProperties(); + } + } + public class MeshProcessDialog : EditorWindow { const string MESH_UTILITY_DICT = "UniGLTF/Mesh Utility/"; @@ -13,8 +33,9 @@ public class MeshProcessDialog : EditorWindow [MenuItem(MESH_UTILITY_DICT + "MeshProcessing Wizard", priority = 30)] static void MeshProcessFromMenu() { - var window = (MeshProcessDialog)EditorWindow.GetWindowWithRect(typeof(MeshProcessDialog), new Rect(0, 0, 500, 250)); - window.titleContent = new GUIContent ("Mesh Processing Window"); + var window = (MeshProcessDialog)EditorWindow.GetWindowWithRect(typeof(MeshProcessDialog), new Rect(0, 0, 650, 500)); + window.titleContent = new GUIContent("Mesh Processing Window"); + window.Show(); } enum Tabs @@ -22,10 +43,26 @@ enum Tabs MeshSeparator, MeshIntegrator, StaticMeshIntegrator, + BoneMeshEraser, } private Tabs _tab; private GameObject _exportTarget; + private Editor _boneMeshEraserEditor; + private SkinnedMeshRenderer _pSkinnedMesh; + private Animator _pAnimator; + private Transform _pEraseRoot; + private Vector2 _scrollPos = new Vector2(0, 0); + + [SerializeField] + private SkinnedMeshRenderer _cSkinnedMesh = null; + [SerializeField] + private Animator _cAnimator; + [SerializeField] + private Transform _cEraseRoot; + [SerializeField] + private BoneMeshEraser.EraseBone[] _eraseBones; + private MethodInfo _processFunction; private bool _isInvokeSuccess = false; @@ -42,14 +79,26 @@ private enum MeshProcessingMessages [LangMsg(Languages.en, "Meshes containing BlendShape will be split")] MESH_SEPARATOR, - [LangMsg(Languages.ja, "メッシュを統合する。BlendShapeを含むメッシュは独立して統合されます")] + [LangMsg(Languages.ja, "メッシュを統合します。BlendShapeを含むメッシュは独立して統合されます")] [LangMsg(Languages.en, "Generate a single mesh. Meshes w/ BlendShape will be grouped into another one")] MESH_INTEGRATOR, - [LangMsg(Languages.ja, "静的メッシュを一つに統合する")] + [LangMsg(Languages.ja, "静的メッシュを一つに統合します")] [LangMsg(Languages.en, "Integrate static meshes into one")] STATIC_MESH_INTEGRATOR, + [LangMsg(Languages.ja, "ボーン(Erase Rootのヒエラルキー)に関連するメッシュを削除します")] + [LangMsg(Languages.en, "Eliminate meshes associated with the bones in EraseRoot hierarchy")] + BONE_MESH_ERASER, + + [LangMsg(Languages.ja, "Skinned Meshを選んでください")] + [LangMsg(Languages.en, "Select a skinned mesh")] + SELECT_SKINNED_MESH, + + [LangMsg(Languages.ja, "Erase Rootを選んでください")] + [LangMsg(Languages.en, "Select a erase root")] + SELECT_ERASE_ROOT, + [LangMsg(Languages.ja, "GameObjectを選んでください")] [LangMsg(Languages.en, "Select a GameObject first")] NO_GAMEOBJECT_SELECTED, @@ -71,8 +120,17 @@ private enum MeshProcessingMessages VRM_DETECTED, } + private void OnEnable() + { + if (!_boneMeshEraserEditor) + { + _boneMeshEraserEditor = Editor.CreateEditor(this); + } + } + private void OnGUI() { + _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos); EditorGUIUtility.labelWidth = 150; // lang Getter.OnGuiSelectLang(); @@ -82,23 +140,44 @@ private void OnGUI() switch (_tab) { case Tabs.MeshSeparator: - EditorGUILayout.TextField(MeshProcessingMessages.MESH_SEPARATOR.Msg()); + EditorGUILayout.LabelField(MeshProcessingMessages.MESH_SEPARATOR.Msg()); break; case Tabs.MeshIntegrator: - EditorGUILayout.TextField(MeshProcessingMessages.MESH_INTEGRATOR.Msg()); + EditorGUILayout.LabelField(MeshProcessingMessages.MESH_INTEGRATOR.Msg()); break; case Tabs.StaticMeshIntegrator: - EditorGUILayout.TextField(MeshProcessingMessages.STATIC_MESH_INTEGRATOR.Msg()); + EditorGUILayout.LabelField(MeshProcessingMessages.STATIC_MESH_INTEGRATOR.Msg()); + break; + case Tabs.BoneMeshEraser: + EditorGUILayout.LabelField(MeshProcessingMessages.BONE_MESH_ERASER.Msg()); break; } - EditorGUILayout.LabelField(MeshProcessingMessages.TARGET_OBJECT.Msg()); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(MeshProcessingMessages.TARGET_OBJECT.Msg(), GUILayout.MaxWidth(146.0f)); _exportTarget = (GameObject)EditorGUILayout.ObjectField(_exportTarget, typeof(GameObject), true); + EditorGUILayout.EndHorizontal(); if (_exportTarget == null && MeshUtility.IsGameObjectSelected()) { _exportTarget = Selection.activeObject as GameObject; } + if (_tab == Tabs.BoneMeshEraser) + { + if (_boneMeshEraserEditor) + { + _boneMeshEraserEditor.OnInspectorGUI(); + } + // any better way we can detect component change? + if (_cSkinnedMesh != _pSkinnedMesh || _cAnimator != _pAnimator || _cEraseRoot != _pEraseRoot) + { + BoneMeshEraserValidate(); + } + _pSkinnedMesh = _cSkinnedMesh; + _pAnimator = _cAnimator; + _pEraseRoot = _cEraseRoot; + } + // Create Other Buttons { GUILayout.BeginVertical(); @@ -119,6 +198,9 @@ private void OnGUI() case Tabs.StaticMeshIntegrator: _isInvokeSuccess = InvokeWizardUpdate("StaticMeshIntegrator"); break; + case Tabs.BoneMeshEraser: + _isInvokeSuccess = InvokeWizardUpdate("BoneMeshRemover"); + break; } if (_isInvokeSuccess) { @@ -132,6 +214,7 @@ private void OnGUI() } GUILayout.EndVertical(); } + EditorGUILayout.EndScrollView(); } private bool InvokeWizardUpdate(string processFuntion) @@ -218,5 +301,127 @@ private bool StaticMeshIntegrator() return false; } } + + private bool BoneMeshRemover() + { + if (_exportTarget == null) return GameObjectNull(); + var go = _exportTarget; + + if (_cSkinnedMesh == null) + { + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.SELECT_SKINNED_MESH.Msg(), "ok"); + return false; + } + else if (_cEraseRoot == null) + { + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.SELECT_ERASE_ROOT.Msg(), "ok"); + return false; + } + BoneMeshRemove(go); + + return true; + } + + private void BoneMeshEraserValidate() + { + if (_cSkinnedMesh == null) + { + _eraseBones = new BoneMeshEraser.EraseBone[] { }; + return; + } + + if (_cEraseRoot == null) + { + if (_cAnimator != null) + { + _cEraseRoot = _cAnimator.GetBoneTransform(HumanBodyBones.Head); + //Debug.LogFormat("head: {0}", EraseRoot); + } + } + + _eraseBones = _cSkinnedMesh.bones.Select(x => + { + var eb = new BoneMeshEraser.EraseBone + { + Bone = x, + }; + + if (_cEraseRoot != null) + { + // 首の子孫を消去 + if (eb.Bone.Ancestor().Any(y => y == _cEraseRoot)) + { + //Debug.LogFormat("erase {0}", x); + eb.Erase = true; + } + } + + return eb; + }) + .ToArray(); + } + + private void BoneMeshRemove(GameObject go) + { + var renderer = Remove(go); + var outputObject = GameObject.Instantiate(go); + outputObject.name = outputObject.name + "_bone_mesh_erase"; + if (renderer == null) + { + return; + } + + // save mesh to Assets + var assetPath = string.Format("{0}{1}", go.name, MeshUtility.ASSET_SUFFIX); + var prefab = MeshUtility.GetPrefab(go); + if (prefab != null) + { + var prefabPath = AssetDatabase.GetAssetPath(prefab); + assetPath = string.Format("{0}/{1}{2}", + Path.GetDirectoryName(prefabPath), + Path.GetFileNameWithoutExtension(prefabPath), + MeshUtility.ASSET_SUFFIX + ); + } + + Debug.LogFormat("CreateAsset: {0}", assetPath); + AssetDatabase.CreateAsset(renderer.sharedMesh, assetPath); + + // destroy BoneMeshEraser in the source + foreach (var skinnedMesh in go.GetComponentsInChildren()) + { + if (skinnedMesh.gameObject.name == BoneMeshEraserWizard.BONE_MESH_ERASER_NAME) + { + GameObject.DestroyImmediate(skinnedMesh.gameObject); + } + } + // destroy the original mesh in the copied GameObject + foreach (var skinnedMesh in outputObject.GetComponentsInChildren()) + { + if (skinnedMesh.sharedMesh == _cSkinnedMesh.sharedMesh) + { + GameObject.DestroyImmediate(skinnedMesh); + } + } + } + + private SkinnedMeshRenderer Remove(GameObject go) + { + var bones = _cSkinnedMesh.bones; + var eraseBones = _eraseBones + .Where(x => x.Erase) + .Select(x => Array.IndexOf(bones, x.Bone)) + .ToArray(); + + var meshNode = new GameObject(BoneMeshEraserWizard.BONE_MESH_ERASER_NAME); + meshNode.transform.SetParent(go.transform, false); + + var erased = meshNode.AddComponent(); + erased.sharedMesh = BoneMeshEraser.CreateErasedMesh(_cSkinnedMesh.sharedMesh, eraseBones); + erased.sharedMaterials = _cSkinnedMesh.sharedMaterials; + erased.bones = _cSkinnedMesh.bones; + + return erased; + } } } \ No newline at end of file diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs b/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs index a7eb133de0..04794d39e8 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs @@ -9,7 +9,7 @@ namespace MeshUtility public class MeshUtility { public const string MENU_PARENT = "UniGLTF/Mesh Utility/"; - private const string ASSET_SUFFIX = ".mesh.asset"; + public const string ASSET_SUFFIX = ".mesh.asset"; private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero; public static Object GetPrefab(GameObject instance) @@ -302,7 +302,7 @@ public static void IntegrateSelected(GameObject go) AssetDatabase.CreateAsset(meshWithMaterials.Mesh, assetPath); // add component - var meshObject = new GameObject(go.name + ".integrated"); + var meshObject = new GameObject(go.name + ".static_meshes_integrated"); if (go.transform.parent != null) { meshObject.transform.SetParent(go.transform.parent, false); @@ -353,7 +353,7 @@ public static void MeshIntegrator(GameObject go) else if (skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_NAME || skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_BLENDSHAPE_NAME) { - // SaveMeshData(skinnedMesh.sharedMesh); + SaveMeshData(skinnedMesh.sharedMesh); } } foreach (var normalMesh in normalMeshes)