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