diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
index 62dc72c40..33f2c1501 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using UniGLTF;
-using UnityEditor;
using UnityEngine;
namespace VRM
@@ -59,218 +55,5 @@ namespace VRM
[Tooltip("Remove vertex color")]
public bool RemoveVertexColor = false;
#endregion
-
- public static bool IsFileNameLengthTooLong(string fileName)
- {
- return fileName.Length > 64;
- }
-
- // ///
- // /// ボーン名の重複を確認
- // ///
- // ///
- // bool DuplicateBoneNameExists()
- // {
- // var bones = ExportRoot.transform.Traverse().ToArray();
- // var duplicates = bones
- // .GroupBy(p => p.name)
- // .Where(g => g.Count() > 1)
- // .Select(g => g.Key);
-
- // return (duplicates.Any());
- // }
-
- // ///
- // /// エクスポート可能か検証する
- // ///
- // ///
- // public IEnumerable Validate()
- // {
- // if (ExportRoot == null)
- // {
- // yield return Validation.Error("Require source");
- // yield break;
- // }
-
- // if (ExportRoot.transform.position != Vector3.zero ||
- // ExportRoot.transform.rotation != Quaternion.identity ||
- // ExportRoot.transform.localScale != Vector3.one)
- // {
- // // EditorUtility.DisplayDialog("Error", "The Root transform should have Default translation, rotation and scale.", "ok");
- // yield return Validation.Warning("The Root translation, rotation and scale will be dropped.");
- // }
-
- // var animator = ExportRoot.GetComponent();
- // if (animator == null)
- // {
- // yield return Validation.Error("Require animator. ");
- // }
- // else
- // {
- // if (animator.avatar == null)
- // {
- // yield return Validation.Error("Require animator.avatar. ");
- // }
- // else if (!animator.avatar.isValid)
- // {
- // yield return Validation.Error("Animator.avatar is not valid. ");
- // }
- // else if (!animator.avatar.isHuman)
- // {
- // yield return Validation.Error("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ");
- // }
-
- // var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
- // if (jaw != null)
- // {
- // yield return Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen");
- // }
- // }
-
- // if (DuplicateBoneNameExists())
- // {
- // yield return Validation.Warning("There is a bone with the same name in the hierarchy. If exported, these bones will be automatically renamed.");
- // }
-
- // if (ReduceBlendshape && ExportRoot.GetComponent() == null)
- // {
- // yield return Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once.");
- // }
-
- // var vertexColor = ExportRoot.GetComponentsInChildren().Any(x => x.sharedMesh.colors.Length > 0);
- // if (vertexColor)
- // {
- // yield return Validation.Warning("This model contains vertex color");
- // }
-
- // var renderers = ExportRoot.GetComponentsInChildren();
- // if (renderers.All(x => !x.gameObject.activeInHierarchy))
- // {
- // yield return Validation.Error("No active mesh");
- // }
-
- // var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct();
- // foreach (var material in materials)
- // {
- // if (material.shader.name == "Standard")
- // {
- // // standard
- // continue;
- // }
-
- // if (VRMMaterialExporter.UseUnlit(material.shader.name))
- // {
- // // unlit
- // continue;
- // }
-
- // if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name))
- // {
- // // VRM supported
- // continue;
- // }
-
- // yield return Validation.Warning(string.Format("{0}: unknown shader '{1}' is used. this will export as `Standard` fallback",
- // material.name,
- // material.shader.name));
- // }
-
- // foreach (var material in materials)
- // {
- // if (IsFileNameLengthTooLong(material.name))
- // yield return Validation.Error(string.Format("FileName '{0}' is too long. ", material.name));
- // }
-
- // var textureNameList = new List();
- // foreach (var material in materials)
- // {
- // var shader = material.shader;
- // int propertyCount = ShaderUtil.GetPropertyCount(shader);
- // for (int i = 0; i < propertyCount; i++)
- // {
- // if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
- // {
- // if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null))
- // {
- // var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name;
- // if (!textureNameList.Contains(textureName))
- // textureNameList.Add(textureName);
- // }
- // }
- // }
- // }
-
- // foreach (var textureName in textureNameList)
- // {
- // if (IsFileNameLengthTooLong(textureName))
- // yield return Validation.Error(string.Format("FileName '{0}' is too long. ", textureName));
- // }
-
- // var vrmMeta = ExportRoot.GetComponent();
- // if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null)
- // {
- // var thumbnailName = vrmMeta.Meta.Thumbnail.name;
- // if (IsFileNameLengthTooLong(thumbnailName))
- // yield return Validation.Error(string.Format("FileName '{0}' is too long. ", thumbnailName));
- // }
-
- // var meshFilters = ExportRoot.GetComponentsInChildren();
- // var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct();
- // foreach (var meshName in meshesName)
- // {
- // if (IsFileNameLengthTooLong(meshName))
- // yield return Validation.Error(string.Format("FileName '{0}' is too long. ", meshName));
- // }
-
- // var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren();
- // var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct();
- // foreach (var skinnedmeshName in skinnedmeshesName)
- // {
- // if (IsFileNameLengthTooLong(skinnedmeshName))
- // yield return Validation.Error(string.Format("FileName '{0}' is too long. ", skinnedmeshName));
- // }
- // }
-
- // ///
- // /// 対象のモデルからMeta情報を取得し、エクスポート設定を初期する
- // ///
- // ///
- // public void InitializeFrom(GameObject go)
- // {
- // //
- // // initialize
- // //
- // // var desc = ExportRoot == null ? null : go.GetComponent();
- // // if (desc == null)
- // // {
- // // // 初回のVRMエクスポートとみなす
- // // ForceTPose = false; // option
- // // PoseFreeze = true;
- // // }
- // // else
- // // {
- // // // すでに正規化済みとみなす
- // // ForceTPose = false;
- // // PoseFreeze = false;
- // // }
-
- // //
- // // Meta
- // //
- // // var meta = ExportRoot == null ? null : go.GetComponent();
- // // if (meta != null && meta.Meta != null)
- // // {
- // // Title = meta.Meta.Title;
- // // Version = string.IsNullOrEmpty(meta.Meta.Version) ? "0.0" : meta.Meta.Version;
- // // Author = meta.Meta.Author;
- // // ContactInformation = meta.Meta.ContactInformation;
- // // Reference = meta.Meta.Reference;
- // // }
- // // else
- // // {
- // // Title = go.name;
- // // Version = "0.0";
- // // }
- // }
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
index 9108b2a4a..433062a72 100644
--- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
+++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
@@ -63,6 +63,181 @@ namespace VRM
}
}
+ ///
+ /// ボーン名の重複を確認
+ ///
+ ///
+ bool DuplicateBoneNameExists()
+ {
+ if (ExportRoot == null)
+ {
+ return false;
+ }
+ var bones = ExportRoot.transform.GetComponentsInChildren();
+ var duplicates = bones
+ .GroupBy(p => p.name)
+ .Where(g => g.Count() > 1)
+ .Select(g => g.Key);
+
+ return (duplicates.Any());
+ }
+
+ public static bool IsFileNameLengthTooLong(string fileName)
+ {
+ return fileName.Length > 64;
+ }
+
+ static bool HasRotationOrScale(GameObject root)
+ {
+ foreach (var t in root.GetComponentsInChildren())
+ {
+ if (t.localRotation != Quaternion.identity)
+ {
+ return true;
+ }
+ if (t.localScale != Vector3.one)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static Vector3 GetForward(Transform l, Transform r)
+ {
+ if (l is null || r is null)
+ {
+ return Vector3.zero;
+ }
+ var lr = (r.position - l.position).normalized;
+ return Vector3.Cross(lr, Vector3.up);
+ }
+
+ ///
+ /// エクスポート可能か検証する
+ ///
+ ///
+ public IEnumerable Validate()
+ {
+ if (ExportRoot == null)
+ {
+ yield break;
+ }
+
+ if (DuplicateBoneNameExists())
+ {
+ yield return Validation.Warning("There is a bone with the same name in the hierarchy. If exported, these bones will be automatically renamed.");
+ }
+
+ // if (ReduceBlendshape && ExportRoot.GetComponent() == null)
+ // {
+ // yield return Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once.");
+ // }
+
+ var vertexColor = ExportRoot.GetComponentsInChildren().Any(x => x.sharedMesh.colors.Length > 0);
+ if (vertexColor)
+ {
+ yield return Validation.Warning("This model contains vertex color");
+ }
+
+ var renderers = ExportRoot.GetComponentsInChildren();
+ if (renderers.All(x => !x.gameObject.activeInHierarchy))
+ {
+ yield return Validation.Error("No active mesh");
+ }
+
+ var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct();
+ foreach (var material in materials)
+ {
+ if (material.shader.name == "Standard")
+ {
+ // standard
+ continue;
+ }
+
+ if (VRMMaterialExporter.UseUnlit(material.shader.name))
+ {
+ // unlit
+ continue;
+ }
+
+ if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name))
+ {
+ // VRM supported
+ continue;
+ }
+
+ yield return Validation.Warning(string.Format("{0}: unknown shader '{1}' is used. this will export as `Standard` fallback",
+ material.name,
+ material.shader.name));
+ }
+
+ foreach (var material in materials)
+ {
+ if (IsFileNameLengthTooLong(material.name))
+ yield return Validation.Error(string.Format("FileName '{0}' is too long. ", material.name));
+ }
+
+ var textureNameList = new List();
+ foreach (var material in materials)
+ {
+ var shader = material.shader;
+ int propertyCount = ShaderUtil.GetPropertyCount(shader);
+ for (int i = 0; i < propertyCount; i++)
+ {
+ if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
+ {
+ if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null))
+ {
+ var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name;
+ if (!textureNameList.Contains(textureName))
+ textureNameList.Add(textureName);
+ }
+ }
+ }
+ }
+
+ foreach (var textureName in textureNameList)
+ {
+ if (IsFileNameLengthTooLong(textureName))
+ yield return Validation.Error(string.Format("FileName '{0}' is too long. ", textureName));
+ }
+
+ var vrmMeta = ExportRoot.GetComponent();
+ if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null)
+ {
+ var thumbnailName = vrmMeta.Meta.Thumbnail.name;
+ if (IsFileNameLengthTooLong(thumbnailName))
+ yield return Validation.Error(string.Format("FileName '{0}' is too long. ", thumbnailName));
+ }
+
+ var meshFilters = ExportRoot.GetComponentsInChildren();
+ var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct();
+ foreach (var meshName in meshesName)
+ {
+ if (IsFileNameLengthTooLong(meshName))
+ yield return Validation.Error(string.Format("FileName '{0}' is too long. ", meshName));
+ }
+
+ var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren();
+ var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct();
+ foreach (var skinnedmeshName in skinnedmeshesName)
+ {
+ if (IsFileNameLengthTooLong(skinnedmeshName))
+ yield return Validation.Error(string.Format("FileName '{0}' is too long. ", skinnedmeshName));
+ }
+
+ var animator = ExportRoot.GetComponent();
+ var l = animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
+ var r = animator.GetBoneTransform(HumanBodyBones.RightUpperLeg);
+ var f = GetForward(l, r);
+ if (Vector3.Dot(f, Vector3.forward) < 0.8f)
+ {
+ yield return Validation.Error("Z+ 向きにしてください");
+ }
+ }
+
VRMMetaObject m_tmpMeta;
Editor m_metaEditor;
@@ -132,16 +307,89 @@ namespace VRM
}
}
-
//@TODO: Force repaint if scripts recompile
private void OnGUI()
{
+ if(m_tmpMeta==null)
+ {
+ // OnDisable
+ return;
+ }
+
EditorGUIUtility.labelWidth = 150;
EditorGUILayout.LabelField("ExportRoot");
var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true);
UpdateRoot(root);
+ //
+ // root
+ //
+ if (root == null)
+ {
+ Validation.Error("ExportRootをセットしてください").DrawGUI();
+ return;
+ }
+ if (root.transform.parent != null)
+ {
+ Validation.Error("ExportRootに親はオブジェクトは持てません").DrawGUI();
+ return;
+ }
+ if (root.transform.localRotation != Quaternion.identity || root.transform.localScale != Vector3.one)
+ {
+ Validation.Error("ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください").DrawGUI();
+ return;
+ }
+ if (HasRotationOrScale(ExportRoot))
+ {
+ Validation.Warning("回転・拡大縮小を持つノードが含まれています。正規化が必用です").DrawGUI();
+ }
+ else
+ {
+ EditorGUILayout.HelpBox("Root OK", MessageType.Info);
+ }
+
+ //
+ // animator
+ //
+ var animator = root.GetComponent();
+ if (animator == null)
+ {
+ Validation.Error("ExportRootに Animator がありません").DrawGUI();
+ return;
+ }
+ var avatar = animator.avatar;
+ if (avatar == null)
+ {
+ Validation.Error("ExportRootの Animator に Avatar がありません").DrawGUI();
+ return;
+ }
+ if (!avatar.isValid)
+ {
+ Validation.Error("ExportRootの Animator.Avatar が不正です").DrawGUI();
+ return;
+ }
+ if (!avatar.isHuman)
+ {
+ Validation.Error("ExportRootの Animator.Avatar がヒューマノイドではありません。FBX importer の Rig で設定してください").DrawGUI();
+ return;
+ }
+ var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
+ if (jaw != null)
+ {
+ Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen").DrawGUI();
+ }
+ else
+ {
+ EditorGUILayout.HelpBox("Animator OK", MessageType.Info);
+ }
+
+ // validation
+ foreach (var v in m_validations)
+ {
+ v.DrawGUI();
+ }
+
// Render contents using Generic Inspector GUI
m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box");
GUIUtility.GetControlID(645789, FocusType.Passive);
@@ -152,11 +400,7 @@ namespace VRM
{
// errors
GUILayout.BeginVertical();
- // foreach (var v in m_validations)
- // {
- // v.DrawGUI();
- // }
- GUILayout.FlexibleSpace();
+ // GUILayout.FlexibleSpace();
{
GUILayout.BeginHorizontal();
@@ -198,50 +442,28 @@ namespace VRM
GUILayout.Space(8);
}
- // Style定義
enum Tabs
{
Meta,
ExportSettings,
}
-
- static class TabStyles
- {
- private static GUIContent[] _tabToggles = null;
- public static GUIContent[] TabToggles
- {
- get
- {
- if (_tabToggles == null)
- {
- _tabToggles = System.Enum.GetNames(typeof(Tabs)).Select(x => new GUIContent(x)).ToArray();
- }
- return _tabToggles;
- }
- }
-
- public static readonly GUIStyle TabButtonStyle = "LargeButton";
-
- // GUI.ToolbarButtonSize.FitToContentsも設定できる
- public static readonly GUI.ToolbarButtonSize TabButtonSize = GUI.ToolbarButtonSize.Fixed;
- }
-
Tabs _tab;
- protected virtual bool DrawWizardGUI()
+ GUIStyle TabButtonStyle => "LargeButton";
+
+ // GUI.ToolbarButtonSize.FitToContentsも設定できる
+ GUI.ToolbarButtonSize TabButtonSize => GUI.ToolbarButtonSize.Fixed;
+
+ bool DrawWizardGUI()
{
- if(m_tmpMeta == null)
+ if (m_tmpMeta == null)
{
+ // disabled
return false;
}
- using (new EditorGUILayout.HorizontalScope())
- {
- GUILayout.FlexibleSpace();
- // タブを描画する
- _tab = (Tabs)GUILayout.Toolbar((int)_tab, TabStyles.TabToggles, TabStyles.TabButtonStyle, TabStyles.TabButtonSize);
- GUILayout.FlexibleSpace();
- }
+ // tabbar
+ _tab = TabBar.OnGUI(_tab, TabButtonStyle, TabButtonSize);
{
if (m_metaEditor == null)
@@ -411,29 +633,41 @@ namespace VRM
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
// export
+ if (Meta == null)
+ {
+ var metaB = ExportRoot.GetComponent();
+ if (metaB == null)
+ {
+ metaB = ExportRoot.AddComponent();
+ }
+ metaB.Meta = m_tmpMeta;
+ }
VRMEditorExporter.Export(path, ExportRoot, m_settings);
+ if (Meta == null)
+ {
+ UnityEngine.GameObject.DestroyImmediate(ExportRoot.GetComponent());
+ }
}
void OnWizardUpdate()
{
- // m_validations.Clear();
- // m_validations.AddRange(m_settings.Validate());
+ UpdateRoot(ExportRoot);
+
+ m_validations.Clear();
+ m_validations.AddRange(Validate());
// if (Meta != null)
// {
// m_validations.AddRange(Meta.Validate());
// }
// else
// {
- // m_validations.Add(Validation.Error("meta がありません"));
+ // // m_validations.Add(Validation.Error("meta がありません"));
// }
- // var hasError = m_validations.Any(x => !x.CanExport);
- // m_IsValid = !hasError;
-
- UpdateRoot(ExportRoot);
+ var hasError = m_validations.Any(x => !x.CanExport);
+ m_IsValid = !hasError;
Repaint();
- // GUIUtility.ExitGUI();
}
}
}
diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs
index 5a6e21e80..c06ca3675 100644
--- a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs
+++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs
@@ -88,7 +88,7 @@ namespace VRM
{
// m_ScriptProp = serializedObject.FindProperty("m_Script");
InitMap(serializedObject);
- }
+ }
public override void OnInspectorGUI()
{
diff --git a/Assets/VRM/UniVRM/Editor/TabBar.cs b/Assets/VRM/UniVRM/Editor/TabBar.cs
new file mode 100644
index 000000000..76a1c748b
--- /dev/null
+++ b/Assets/VRM/UniVRM/Editor/TabBar.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+
+ static class TabBar
+ {
+ public static T OnGUI(T t, GUIStyle buttonStyle, GUI.ToolbarButtonSize buttonSize) where T : Enum
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ // タブを描画する
+ var value = GUILayout.Toolbar((int)(object)t, TabCache.TabToggles, buttonStyle, buttonSize);
+ GUILayout.FlexibleSpace();
+ return (T)(object)value;
+ }
+ }
+ static class TabCache where T : Enum
+ {
+ private static GUIContent[] _tabToggles = null;
+
+ public static GUIContent[] TabToggles
+ {
+ get
+ {
+ if (_tabToggles == null)
+ {
+ _tabToggles = System.Enum.GetNames(typeof(T)).Select(x => new GUIContent(x)).ToArray();
+ }
+ return _tabToggles;
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/VRM/UniVRM/Editor/TabBar.cs.meta b/Assets/VRM/UniVRM/Editor/TabBar.cs.meta
new file mode 100644
index 000000000..ee1d88d40
--- /dev/null
+++ b/Assets/VRM/UniVRM/Editor/TabBar.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a93e997ef78acca448e14f07cb06e18a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs
index ef6d0831c..d7727ce78 100644
--- a/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs
+++ b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs
@@ -14,7 +14,7 @@ namespace VRM
[TestCase("AliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAlicia", true)]
public void DetectFileNameLength(string fileName, bool isIllegal)
{
- var result = VRMExportSettings.IsFileNameLengthTooLong(fileName);
+ var result = VRMExporterWizard.IsFileNameLengthTooLong(fileName);
Assert.AreEqual(result, isIllegal);
}