diff --git a/Assets/UniGLTF/Editor/UniGLTF/ImporterMenu.cs b/Assets/UniGLTF/Editor/UniGLTF/ImporterMenu.cs index 2e6dc1eb2..32b1b603d 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ImporterMenu.cs +++ b/Assets/UniGLTF/Editor/UniGLTF/ImporterMenu.cs @@ -7,7 +7,7 @@ namespace UniGLTF { public static class ImporterMenu { - [MenuItem(UniGLTFVersion.MENU + "/Import(gltf, glb)", priority = 1)] + [MenuItem(UniGLTFVersion.MENU + "/Import(gltf, glb)", priority = 20)] public static void ImportMenu() { var path = EditorUtility.OpenFilePanel("open gltf", "", "gltf,glb"); diff --git a/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg index 98adf2b94..ce16445a5 100644 Binary files a/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg and b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg differ diff --git a/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md b/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md index da93e7d9c..d1ad8907b 100644 --- a/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md +++ b/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md @@ -8,9 +8,9 @@ Select a GameObject contained skinned mesh and BlendShape: -Select `Mesh Utility` -> `MeshSeparator`: +From menu go to `UniGLTF` -> `Mesh Utility` -> `MeshProcessing Wizard`, select `MeshSeparator` and click `process`: - + The separate meshes are saved in the Assets folder. GameObjects with separate meshes are also available in the Hierarchy Window: diff --git a/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs b/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs index 0c652159a..b28d0a50c 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/BoneMeshEraserWizard.cs @@ -51,7 +51,7 @@ namespace MeshUtility [SerializeField] BoneMeshEraser.EraseBone[] m_eraseBones; - [MenuItem(MeshUtility.MENU_PARENT + "BoneMeshEraser Wizard", priority = 4)] + [MenuItem(MeshUtility.MENU_PARENT + "BoneMeshEraser Wizard", priority = 31)] static void CreateWizard() { ScriptableWizard.DisplayWizard("BoneMeshEraser", "Erase triangles by bone", "Erase"); diff --git a/Assets/UniGLTF/MeshUtility/Editor/ExportDialog/Valildators/HierarchyValidator.cs b/Assets/UniGLTF/MeshUtility/Editor/ExportDialog/Valildators/HierarchyValidator.cs index d899d14c8..d54fc4061 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/ExportDialog/Valildators/HierarchyValidator.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/ExportDialog/Valildators/HierarchyValidator.cs @@ -26,7 +26,7 @@ namespace MeshUtility.Validators DUPLICATE_BONE_NAME_EXISTS, [LangMsg(Languages.ja, "SkinnedMeshRenderer.bones に重複する内容がある。エクスポートした場合に、bones の重複を取り除き、boneweights, bindposes を調整します")] - [LangMsg(Languages.en, "There are same bone in bones the SkinnedMeshRenderer. They will be automatically uniqued")] + [LangMsg(Languages.en, "There are duplicated bones in SkinnedMeshRenderer.bones. They will be exported as unique bones. boneweights and bindposes will also be adjusted")] NO_UNIQUE_JOINTS, } diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs new file mode 100644 index 000000000..7ce167d11 --- /dev/null +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs @@ -0,0 +1,182 @@ +using UnityEngine; +using UnityEditor; +using System.Reflection; +using MeshUtility; +using System.IO; +using System; +using System.Linq; + +namespace MeshUtility +{ + public class MeshProcessDialog : EditorWindow + { + const string MESH_UTILITY_DICT = "UniGLTF/Mesh Utility/"; + + [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"); + } + + enum Tabs + { + MeshSeparator, + MeshIntegrator, + StaticMeshIntegrator, + } + private Tabs _tab; + + private GameObject _exportTarget; + private MethodInfo _processFunction; + private bool _isInvokeSuccess = false; + + GUIStyle _tabButtonStyle => "LargeButton"; + GUI.ToolbarButtonSize _tabButtonSize => GUI.ToolbarButtonSize.Fixed; + + private void OnGUI() + { + EditorGUIUtility.labelWidth = 150; + _tab = TabBar.OnGUI(_tab, _tabButtonStyle, _tabButtonSize); + + switch (_tab) + { + case Tabs.MeshSeparator: + EditorGUILayout.TextField("Meshes containing BlendShape data will be split"); + break; + case Tabs.MeshIntegrator: + EditorGUILayout.TextField("Generate a single mesh. Meshes w/ BlendShape will be grouped into another one"); + break; + case Tabs.StaticMeshIntegrator: + EditorGUILayout.TextField("Integrate static meshes into one"); + break; + } + + EditorGUILayout.LabelField("ExportTarget"); + _exportTarget = (GameObject)EditorGUILayout.ObjectField(_exportTarget, typeof(GameObject), true); + if (_exportTarget == null && MeshUtility.IsGameObjectSelected()) + { + _exportTarget = Selection.activeObject as GameObject; + } + + // Create Other Buttons + { + GUILayout.BeginVertical(); + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Process", GUILayout.MinWidth(100))) + { + switch (_tab) + { + case Tabs.MeshSeparator: + _isInvokeSuccess = InvokeWizardUpdate("MeshSeparator"); + break; + case Tabs.MeshIntegrator: + _isInvokeSuccess = InvokeWizardUpdate("MeshIntegrator"); + break; + case Tabs.StaticMeshIntegrator: + _isInvokeSuccess = InvokeWizardUpdate("StaticMeshIntegrator"); + break; + } + if (_isInvokeSuccess) + { + Close(); + GUIUtility.ExitGUI(); + } + } + GUI.enabled = true; + + GUILayout.EndHorizontal(); + } + GUILayout.EndVertical(); + } + } + + private bool InvokeWizardUpdate(string processFuntion) + { + const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; + _processFunction = GetType().GetMethod(processFuntion, kInstanceInvokeFlags); + if (_processFunction != null) + { + return (Boolean)_processFunction.Invoke(this, null); + } + else + { + Debug.LogError("This function has not been implemented in script"); + return false; + } + } + + private bool GameObjectNull() + { + EditorUtility.DisplayDialog("Failed", "No GameObject Selected", "ok"); + return false; + } + + private bool MeshSeparator() + { + if (_exportTarget == null) return GameObjectNull(); + var go = _exportTarget; + + if (go.GetComponentsInChildren().Length > 0) + { + MeshUtility.SeparationProcessing(go); + return true; + } + else + { + EditorUtility.DisplayDialog("Failed", "No skinned mesh contained", "ok"); + return false; + } + } + + private bool MeshIntegrator() + { + if (_exportTarget == null) return GameObjectNull(); + var go = _exportTarget; + + Component[] allComponents = go.GetComponents(typeof(Component)); + var keyWord = "VRMMeta"; + + foreach (var component in allComponents) + { + if (component == null) continue; + var sourceString = component.ToString(); + if (sourceString.Contains(keyWord)) + { + EditorUtility.DisplayDialog("Failed", "Target object is VRM file, use `VRM0 -> MeshIntegrator` instead", "ok"); + return false; + } + } + + if (go.GetComponentsInChildren().Length > 0 || go.GetComponentsInChildren().Length > 0) + { + MeshUtility.MeshIntegrator(go); + return true; + } + else + { + EditorUtility.DisplayDialog("Failed", "Neither skinned mesh nor static mesh contained", "ok"); + return false; + } + } + + private bool StaticMeshIntegrator() + { + if (_exportTarget == null) return GameObjectNull(); + var go = _exportTarget; + if (go.GetComponentsInChildren().Length > 0) + { + MeshUtility.IntegrateSelected(go); + return true; + } + else + { + EditorUtility.DisplayDialog("Failed", "No static mesh contained", "ok"); + return false; + } + } + } +} \ No newline at end of file diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs.meta b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs.meta new file mode 100644 index 000000000..1127183e7 --- /dev/null +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86cbd6470ebef2f4c976410efaf45638 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs b/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs index 535278bb4..400d752a9 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; @@ -9,12 +9,14 @@ namespace MeshUtility public class MeshUtility { public const string MENU_PARENT = "UniGLTF/Mesh Utility/"; - public const int MENU_PRIORITY = 11; private const string ASSET_SUFFIX = ".mesh.asset"; private const string MENU_NAME = MENU_PARENT + "MeshSeparator"; private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero; + private const string INTEGRATED_MESH_NAME = "MeshesIntegrated"; + private const string INTEGRATED_MESH_BLENDSHAPE_NAME = "MeshesBlendShapeIntegrated"; + public static Object GetPrefab(GameObject instance) { #if UNITY_2018_2_OR_NEWER @@ -39,31 +41,16 @@ namespace MeshUtility return true; } - [MenuItem(MENU_NAME, priority = 2)] - public static void SeparateSkinnedMeshContainedBlendShape() - { - var go = Selection.activeTransform.gameObject; - - if (go.GetComponentsInChildren().Length > 0) - { - SeparationProcessing(go); - go.SetActive(false); - } - else - { - EditorUtility.DisplayDialog("Error", "No skinnedMeshRenderer contained", "ok"); - } - } - - [MenuItem(MENU_PARENT + "MeshSeparator Docs", priority = MeshUtility.MENU_PRIORITY)] + [MenuItem(MENU_PARENT + "MeshUtility Docs", priority = 32)] public static void LinkToMeshSeparatorDocs() { Application.OpenURL("https://github.com/vrm-c/UniVRM/blob/master/Assets/UniGLTF/MeshUtility/README.md"); } - private static void SeparationProcessing(GameObject go) + public static void SeparationProcessing(GameObject go) { var outputObject = GameObject.Instantiate(go); + outputObject.name = outputObject.name + "_mesh_separation"; var skinnedMeshRenderers = outputObject.GetComponentsInChildren(); foreach (var skinnedMeshRenderer in skinnedMeshRenderers) { @@ -284,16 +271,13 @@ namespace MeshUtility } #if UNITY_EDITOR - [MenuItem(MENU_PARENT + "Integrate Static Mesh", validate = true)] - public static bool CanIntegrateSelected() + public static bool IsGameObjectSelected() { return Selection.activeObject != null && Selection.activeObject is GameObject; } - [MenuItem(MENU_PARENT + "Integrate Static Mesh", priority = 3)] - public static void IntegrateSelected() + public static void IntegrateSelected(GameObject go) { - var go = Selection.activeObject as GameObject; var meshWithMaterials = StaticMeshIntegrator.Integrate(go.transform); // save as asset @@ -347,5 +331,53 @@ namespace MeshUtility renderer.sharedMaterials = meshWithMaterials.Materials; } #endif + public static void MeshIntegrator(GameObject go) + { + MeshIntegratorUtility.Integrate(go, onlyBlendShapeRenderers: true); + MeshIntegratorUtility.Integrate(go, onlyBlendShapeRenderers: false); + + var outputObject = GameObject.Instantiate(go); + outputObject.name = outputObject.name + "_mesh_integration"; + var skinnedMeshes = outputObject.GetComponentsInChildren(); + var normalMeshes = outputObject.GetComponentsInChildren(); + + // destroy integrated meshes in the source + foreach (var skinnedMesh in go.GetComponentsInChildren()) + { + if (skinnedMesh.sharedMesh.name == INTEGRATED_MESH_NAME) + { + GameObject.DestroyImmediate(skinnedMesh.gameObject); + } + } + // destroy original meshes in the copied GameObject + foreach (var skinnedMesh in skinnedMeshes) + { + if (skinnedMesh.sharedMesh.name != INTEGRATED_MESH_NAME) + { + GameObject.DestroyImmediate(skinnedMesh); + } + // avoid repeated name + else if (skinnedMesh.sharedMesh.blendShapeCount > 0) + { + skinnedMesh.sharedMesh.name = INTEGRATED_MESH_BLENDSHAPE_NAME; + } + // check if the integrated mesh is empty + else if (skinnedMesh.sharedMesh.subMeshCount == 0) + { + GameObject.DestroyImmediate(skinnedMesh.gameObject); + } + } + foreach (var normalMesh in normalMeshes) + { + if (normalMesh.sharedMesh.name != INTEGRATED_MESH_NAME) + { + if (normalMesh.gameObject.GetComponent()) + { + GameObject.DestroyImmediate(normalMesh.gameObject.GetComponent()); + } + GameObject.DestroyImmediate(normalMesh); + } + } + } } } \ No newline at end of file diff --git a/Assets/UniGLTF/MeshUtility/README.md b/Assets/UniGLTF/MeshUtility/README.md index 8b434fb3d..72cf3d166 100644 --- a/Assets/UniGLTF/MeshUtility/README.md +++ b/Assets/UniGLTF/MeshUtility/README.md @@ -14,15 +14,15 @@ Currently support BlendShape mesh separation. See [documentation](Documentation/ ### MeshIntegrator -Integrate all the meshes of a Prefab (Project window). +Integrate all the meshes of a (prefab) GameObject. -### Integrate Static Mesh +### StaticMeshIntegrator -Integrate all the static meshes in the Hierarchy (Root and its children). +Integrate all the static meshes of a (prefab) GameObject (Root and its children). ### MeshNormalizer -Bake the Hierarchy. This is VRM normalize backend. +Bake the Hierarchy. This is VRM normalization backend. MeshNormalizer can do blendShape bake. ## Import MeshUtility diff --git a/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs b/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs index cfb1742e8..2f5b343ef 100644 --- a/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs +++ b/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs @@ -54,7 +54,7 @@ namespace MeshUtility } var mesh = new Mesh(); - mesh.name = "integrated"; + mesh.name = "MeshesIntegrated"; if (integrator.Positions.Count > ushort.MaxValue) { diff --git a/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs b/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs index 9edbcc936..219b0d04f 100644 --- a/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs +++ b/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs @@ -66,7 +66,7 @@ namespace MeshUtility public Mesh ToMesh() { var mesh = new Mesh(); - mesh.name = "integrated"; + mesh.name = "MeshesIntegrated"; mesh.vertices = m_positions.ToArray(); if (m_normals.Count > 0) diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs index 8ae3762d8..3919bcedb 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs @@ -23,7 +23,7 @@ namespace UniGLTF return Selection.activeObject != null && Selection.activeObject is GameObject; } - [MenuItem(MENU_EXPORT_GLTF_KEY, false, 1)] + [MenuItem(MENU_EXPORT_GLTF_KEY, priority = 0)] private static void ExportGltfFromMenu() { ExportFromMenu(false, new MeshExportSettings @@ -33,7 +33,7 @@ namespace UniGLTF }); } - [MenuItem(MENU_EXPORT_GLB_KEY, false, 1)] + [MenuItem(MENU_EXPORT_GLB_KEY, priority = 10)] private static void ExportGlbFromMenu() { ExportFromMenu(true, MeshExportSettings.Default); diff --git a/Assets/VRM/Editor/Format/VRMExportSettingsEditor.cs b/Assets/VRM/Editor/Format/VRMExportSettingsEditor.cs index a4c3512a8..6600a4a4b 100644 --- a/Assets/VRM/Editor/Format/VRMExportSettingsEditor.cs +++ b/Assets/VRM/Editor/Format/VRMExportSettingsEditor.cs @@ -118,11 +118,11 @@ namespace VRM REMOVE_VERTEX_COLOR, [LangMsg(Languages.ja, "このボタンで自動で T-Pose にできます。手動で T-Pose にしたり、ボタンの後で手直ししてもOKです。")] - [LangMsg(Languages.en, "You can automatically set the T pose with this button. You can change it manually to T-pose or after the button.")] + [LangMsg(Languages.en, "T-Pose can be made automatically with this button, or you can make the model as T-Pose manually. Adjusting T-Pose manually after applying this function is also OK")] ENALBE_TPOSE_BUTTON, [LangMsg(Languages.ja, "このボタンで自動で T-Pose にできます。prefab には実行できません。")] - [LangMsg(Languages.en, "You can set the T pose automatically with this button. It cannot be run on prefabs.")] + [LangMsg(Languages.en, "T-Pose can be made automatically with this button. It cannot be run on prefabs.")] DISABLE_TPOSE_BUTTON, [LangMsg(Languages.ja, "T-Pose にする")] diff --git a/README.ja.md b/README.ja.md index b2b4bf18b..7557ef9a1 100644 --- a/README.ja.md +++ b/README.ja.md @@ -32,6 +32,14 @@ glTF2.0をベースとしており、誰でも自由に利用することがで ## Contributing to UniVRM +以下コマンドでUniVRMリポジトリをクローンして、UnityでUniVRMフォルダを開きます。 + +```console +$ git clone https://github.com/vrm-c/UniVRM.git +$ cd UniVRM +$ git submodule update --init --recursive +``` + [コントリビューションガイド](https://github.com/vrm-c/UniVRM/wiki/コントリビューションガイド(ja))を参照してください。 ## Documents diff --git a/README.md b/README.md index 48d0ef60f..367751c4d 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,14 @@ VRM is based on glTF2.0. If you comply with the MIT license, you are free to use ## Contributing to UniVRM +Use the commands below to clone UniVRM repository, and open the UniVRM folder by Unity. + +```console +$ git clone https://github.com/vrm-c/UniVRM.git +$ cd UniVRM +$ git submodule update --init --recursive +``` + See [contributing guidelines](https://github.com/vrm-c/UniVRM/wiki/Contributing-Guidelines). ## Documents