From adae26ecd0dab83684fdd5cb844dfda27e33a446 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Mon, 20 Jul 2020 19:38:49 +0900 Subject: [PATCH 01/17] =?UTF-8?q?ScriptableWizard=E3=82=92=E3=82=AB?= =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=83=9E=E3=82=A4=E3=82=BA=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=B9=E3=81=8F=E3=80=81CustomScriptableWizard=E3=82=92?= =?UTF-8?q?=E5=B0=8E=E5=85=A5(UnityCsReference=E3=81=AEScriptableWizard.cs?= =?UTF-8?q?=E3=82=92=E3=83=99=E3=83=BC=E3=82=B9=E3=81=AB=E5=8D=98=E7=B4=94?= =?UTF-8?q?=E5=8C=96=E3=81=97=E3=81=9F=E3=82=82=E3=81=AE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniVRM/Editor/CustomScriptableWizard.cs | 253 ++++++++++++++++++ .../Editor/CustomScriptableWizard.cs.meta | 11 + .../UniVRM/Editor/Format/VRMExporterWizard.cs | 4 +- 3 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs create mode 100644 Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs new file mode 100644 index 0000000000..2a84c61891 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs @@ -0,0 +1,253 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Linq; +using System.Reflection; +using UnityEditor; +// using UnityEditor; +using UnityEngine; +using uei = UnityEngine.Internal; + +namespace VRM +{ + // Derive from this class to create an editor wizard. + public class CustomScriptableWizard : EditorWindow + { + Editor m_Inspector; + + private string m_HelpString = ""; + private string m_ErrorString = ""; + private bool m_IsValid = true; + private Vector2 m_ScrollPosition; + private string m_CreateButton = "Create"; + private string m_OtherButton = ""; + + private void OnDestroy() + { + UnityEditor.Editor.DestroyImmediate(m_Inspector); + } + + private void InvokeWizardUpdate() + { + const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; + MethodInfo method = GetType().GetMethod("OnWizardUpdate", kInstanceInvokeFlags); + if (method != null) + method.Invoke(this, null); + } + + private class Styles + { + public static string errorText = "Wizard Error"; + public static string box = "Wizard Box"; + } + + public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options); + static BeginVerticalScrollViewFunc s_func; + static BeginVerticalScrollViewFunc BeginVerticalScrollView + { + get + { + if (s_func is null) + { + var methods = typeof(EditorGUILayout).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.Name == "BeginVerticalScrollView").ToArray(); + var method = methods.First(x => x.GetParameters()[1].ParameterType == typeof(bool)); + s_func = (BeginVerticalScrollViewFunc)method.CreateDelegate(typeof(BeginVerticalScrollViewFunc)); + } + return s_func; + } + } + + + //@TODO: Force repaint if scripts recompile + private void OnGUI() + { + EditorGUIUtility.labelWidth = 150; + GUILayout.Label(m_HelpString, EditorStyles.wordWrappedLabel, GUILayout.ExpandHeight(true)); + + // Render contents using Generic Inspector GUI + m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); + GUIUtility.GetControlID(645789, FocusType.Passive); + bool modified = DrawWizardGUI(); + EditorGUILayout.EndScrollView(); + + // Create and Other Buttons + GUILayout.BeginVertical(); + if (m_ErrorString != string.Empty) + GUILayout.Label(m_ErrorString, Styles.errorText, GUILayout.MinHeight(32)); + else + GUILayout.Label(string.Empty, GUILayout.MinHeight(32)); + GUILayout.FlexibleSpace(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUI.enabled = m_IsValid; + + const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; + if (m_OtherButton != "" && GUILayout.Button(m_OtherButton, GUILayout.MinWidth(100))) + { + MethodInfo method = GetType().GetMethod("OnWizardOtherButton", kInstanceInvokeFlags); + if (method != null) + { + method.Invoke(this, null); + GUIUtility.ExitGUI(); + } + else + Debug.LogError("OnWizardOtherButton has not been implemented in script"); + } + + if (m_CreateButton != "" && GUILayout.Button(m_CreateButton, GUILayout.MinWidth(100))) + { + MethodInfo method = GetType().GetMethod("OnWizardCreate", kInstanceInvokeFlags); + if (method != null) + method.Invoke(this, null); + else + Debug.LogError("OnWizardCreate has not been implemented in script"); + Close(); + GUIUtility.ExitGUI(); + } + GUI.enabled = true; + + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + if (modified) + InvokeWizardUpdate(); + + GUILayout.Space(8); + } + + protected virtual bool DrawWizardGUI() + { + if (m_Inspector == null) + { + m_Inspector = Editor.CreateEditor(this); + } + m_Inspector.OnInspectorGUI(); + return true; + } + + // Creates a wizard. + public static T DisplayWizard(string title) where T : CustomScriptableWizard + { + return DisplayWizard(title, "Create", ""); + } + + ///*listonly* + public static T DisplayWizard(string title, string createButtonName) where T : CustomScriptableWizard + { + return DisplayWizard(title, createButtonName, ""); + } + + ///*listonly* + public static T DisplayWizard(string title, string createButtonName, string otherButtonName) where T : CustomScriptableWizard + { + return (T)DisplayWizard(title, typeof(T), createButtonName, otherButtonName); + } + + [uei.ExcludeFromDocsAttribute] + public static CustomScriptableWizard DisplayWizard(string title, Type klass, string createButtonName) + { + string otherButtonName = ""; + return DisplayWizard(title, klass, createButtonName, otherButtonName); + } + + [uei.ExcludeFromDocsAttribute] + public static CustomScriptableWizard DisplayWizard(string title, Type klass) + { + string otherButtonName = ""; + string createButtonName = "Create"; + return DisplayWizard(title, klass, createButtonName, otherButtonName); + } + + // Creates a wizard. + public static CustomScriptableWizard DisplayWizard(string title, Type klass, [uei.DefaultValueAttribute("\"Create\"")] string createButtonName, [uei.DefaultValueAttribute("\"\"")] string otherButtonName) + { + CustomScriptableWizard wizard = CreateInstance(klass) as CustomScriptableWizard; + wizard.m_CreateButton = createButtonName; + wizard.m_OtherButton = otherButtonName; + wizard.titleContent = new GUIContent(title); + if (wizard != null) + { + wizard.InvokeWizardUpdate(); + wizard.ShowUtility(); + } + return wizard; + } + + // // Magic Methods + + // // This is called when the wizard is opened or whenever the user changes something in the wizard. + // void OnWizardUpdate(); + + // // This is called when the user clicks on the Create button. + // void OnWizardCreate(); + + // Allows you to set the help text of the wizard. + public string helpString + { + get { return m_HelpString; } + set + { + var newString = value ?? string.Empty; + if (m_HelpString != newString) + { + m_HelpString = newString; + Repaint(); + } + } + } + + // Allows you to set the error text of the wizard. + public string errorString + { + get { return m_ErrorString; } + set + { + var newString = value ?? string.Empty; + if (m_ErrorString != newString) + { + m_ErrorString = newString; + Repaint(); + } + } + } + + // Allows you to set the create button text of the wizard. + public string createButtonName + { + get { return m_CreateButton; } + set + { + var newString = value ?? string.Empty; + if (m_CreateButton != newString) + { + m_CreateButton = newString; + Repaint(); + } + } + } + + // Allows you to set the other button text of the wizard. + public string otherButtonName + { + get { return m_OtherButton; } + set + { + var newString = value ?? string.Empty; + if (m_OtherButton != newString) + { + m_OtherButton = newString; + Repaint(); + } + } + } + + // Allows you to enable and disable the wizard create button, so that the user can not click it. + public bool isValid + { + get { return m_IsValid; } + set { m_IsValid = value; } + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta new file mode 100644 index 0000000000..45882814d1 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c18c8a8a304985b4180418b42e544ac5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 90b1faa123..d3f7b58037 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -6,7 +6,7 @@ namespace VRM { - public class VRMExporterWizard : ScriptableWizard + public class VRMExporterWizard : CustomScriptableWizard { const string EXTENSION = ".vrm"; @@ -18,7 +18,7 @@ public class VRMExporterWizard : ScriptableWizard public static void CreateWizard() { - var wiz = ScriptableWizard.DisplayWizard( + var wiz = CustomScriptableWizard.DisplayWizard( "VRM Exporter", "Export"); var go = Selection.activeObject as GameObject; From d019a5e0ceef655666b4ac103f95ee6cf43730d2 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 14:50:11 +0900 Subject: [PATCH 02/17] =?UTF-8?q?=E5=90=88=E4=BD=93=E3=81=97=E3=81=A6?= =?UTF-8?q?=E7=B6=99=E6=89=BF=E3=81=97=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniVRM/Editor/CustomScriptableWizard.cs | 1 - .../UniVRM/Editor/Format/VRMExporterWizard.cs | 249 +++++++++++++++++- 2 files changed, 245 insertions(+), 5 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs index 2a84c61891..83045cc322 100644 --- a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs +++ b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using UnityEditor; -// using UnityEditor; using UnityEngine; using uei = UnityEngine.Internal; diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index d3f7b58037..60340d3ae1 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -1,13 +1,254 @@ -using System.IO; +using System; +using System.IO; +using System.Linq; +using System.Reflection; using System.Text; using UnityEditor; using UnityEngine; - +using uei = UnityEngine.Internal; namespace VRM { - public class VRMExporterWizard : CustomScriptableWizard + /// + /// エクスポートダイアログ + /// + public class VRMExporterWizard : EditorWindow { + Editor m_Inspector; + + private string m_HelpString = ""; + private string m_ErrorString = ""; + private bool m_IsValid = true; + private Vector2 m_ScrollPosition; + private string m_CreateButton = "Create"; + private string m_OtherButton = ""; + + private void OnDestroy() + { + UnityEditor.Editor.DestroyImmediate(m_Inspector); + } + + private void InvokeWizardUpdate() + { + const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; + MethodInfo method = GetType().GetMethod("OnWizardUpdate", kInstanceInvokeFlags); + if (method != null) + method.Invoke(this, null); + } + + private class Styles + { + public static string errorText = "Wizard Error"; + public static string box = "Wizard Box"; + } + + public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options); + static BeginVerticalScrollViewFunc s_func; + static BeginVerticalScrollViewFunc BeginVerticalScrollView + { + get + { + if (s_func is null) + { + var methods = typeof(EditorGUILayout).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.Name == "BeginVerticalScrollView").ToArray(); + var method = methods.First(x => x.GetParameters()[1].ParameterType == typeof(bool)); + s_func = (BeginVerticalScrollViewFunc)method.CreateDelegate(typeof(BeginVerticalScrollViewFunc)); + } + return s_func; + } + } + + + //@TODO: Force repaint if scripts recompile + private void OnGUI() + { + EditorGUIUtility.labelWidth = 150; + GUILayout.Label(m_HelpString, EditorStyles.wordWrappedLabel, GUILayout.ExpandHeight(true)); + + // Render contents using Generic Inspector GUI + m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); + GUIUtility.GetControlID(645789, FocusType.Passive); + bool modified = DrawWizardGUI(); + EditorGUILayout.EndScrollView(); + + // Create and Other Buttons + GUILayout.BeginVertical(); + if (m_ErrorString != string.Empty) + GUILayout.Label(m_ErrorString, Styles.errorText, GUILayout.MinHeight(32)); + else + GUILayout.Label(string.Empty, GUILayout.MinHeight(32)); + GUILayout.FlexibleSpace(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUI.enabled = m_IsValid; + + const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; + if (m_OtherButton != "" && GUILayout.Button(m_OtherButton, GUILayout.MinWidth(100))) + { + MethodInfo method = GetType().GetMethod("OnWizardOtherButton", kInstanceInvokeFlags); + if (method != null) + { + method.Invoke(this, null); + GUIUtility.ExitGUI(); + } + else + Debug.LogError("OnWizardOtherButton has not been implemented in script"); + } + + if (m_CreateButton != "" && GUILayout.Button(m_CreateButton, GUILayout.MinWidth(100))) + { + MethodInfo method = GetType().GetMethod("OnWizardCreate", kInstanceInvokeFlags); + if (method != null) + method.Invoke(this, null); + else + Debug.LogError("OnWizardCreate has not been implemented in script"); + Close(); + GUIUtility.ExitGUI(); + } + GUI.enabled = true; + + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + if (modified) + InvokeWizardUpdate(); + + GUILayout.Space(8); + } + + protected virtual bool DrawWizardGUI() + { + if (m_Inspector == null) + { + m_Inspector = Editor.CreateEditor(this); + } + m_Inspector.OnInspectorGUI(); + return true; + } + + // Creates a wizard. + public static T DisplayWizard(string title) where T : VRMExporterWizard + { + return DisplayWizard(title, "Create", ""); + } + + ///*listonly* + public static T DisplayWizard(string title, string createButtonName) where T : VRMExporterWizard + { + return DisplayWizard(title, createButtonName, ""); + } + + ///*listonly* + public static T DisplayWizard(string title, string createButtonName, string otherButtonName) where T : VRMExporterWizard + { + return (T)DisplayWizard(title, typeof(T), createButtonName, otherButtonName); + } + + [uei.ExcludeFromDocsAttribute] + public static VRMExporterWizard DisplayWizard(string title, Type klass, string createButtonName) + { + string otherButtonName = ""; + return DisplayWizard(title, klass, createButtonName, otherButtonName); + } + + [uei.ExcludeFromDocsAttribute] + public static VRMExporterWizard DisplayWizard(string title, Type klass) + { + string otherButtonName = ""; + string createButtonName = "Create"; + return DisplayWizard(title, klass, createButtonName, otherButtonName); + } + + // Creates a wizard. + public static VRMExporterWizard DisplayWizard(string title, Type klass, [uei.DefaultValueAttribute("\"Create\"")] string createButtonName, [uei.DefaultValueAttribute("\"\"")] string otherButtonName) + { + VRMExporterWizard wizard = CreateInstance(klass) as VRMExporterWizard; + wizard.m_CreateButton = createButtonName; + wizard.m_OtherButton = otherButtonName; + wizard.titleContent = new GUIContent(title); + if (wizard != null) + { + wizard.InvokeWizardUpdate(); + wizard.ShowUtility(); + } + return wizard; + } + + // // Magic Methods + + // // This is called when the wizard is opened or whenever the user changes something in the wizard. + // void OnWizardUpdate(); + + // // This is called when the user clicks on the Create button. + // void OnWizardCreate(); + + // Allows you to set the help text of the wizard. + public string helpString + { + get { return m_HelpString; } + set + { + var newString = value ?? string.Empty; + if (m_HelpString != newString) + { + m_HelpString = newString; + Repaint(); + } + } + } + + // Allows you to set the error text of the wizard. + public string errorString + { + get { return m_ErrorString; } + set + { + var newString = value ?? string.Empty; + if (m_ErrorString != newString) + { + m_ErrorString = newString; + Repaint(); + } + } + } + + // Allows you to set the create button text of the wizard. + public string createButtonName + { + get { return m_CreateButton; } + set + { + var newString = value ?? string.Empty; + if (m_CreateButton != newString) + { + m_CreateButton = newString; + Repaint(); + } + } + } + + // Allows you to set the other button text of the wizard. + public string otherButtonName + { + get { return m_OtherButton; } + set + { + var newString = value ?? string.Empty; + if (m_OtherButton != newString) + { + m_OtherButton = newString; + Repaint(); + } + } + } + + // Allows you to enable and disable the wizard create button, so that the user can not click it. + public bool isValid + { + get { return m_IsValid; } + set { m_IsValid = value; } + } + const string EXTENSION = ".vrm"; VRMMeta m_meta; @@ -18,7 +259,7 @@ public class VRMExporterWizard : CustomScriptableWizard public static void CreateWizard() { - var wiz = CustomScriptableWizard.DisplayWizard( + var wiz = VRMExporterWizard.DisplayWizard( "VRM Exporter", "Export"); var go = Selection.activeObject as GameObject; From 27bcb99812b030bceeddb9fb413bd33c44930198 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 15:27:34 +0900 Subject: [PATCH 03/17] add VRMMetaObjectEditor --- .../VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs | 127 +----------------- .../UniVRM/Editor/Meta/VRMMetaObjectEditor.cs | 114 ++++++++++++++++ .../Editor/Meta/VRMMetaObjectEditor.cs.meta | 11 ++ 3 files changed, 132 insertions(+), 120 deletions(-) create mode 100644 Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs create mode 100644 Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs index 339c983588..fac50451f3 100644 --- a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using UnityEditor; -using UnityEngine; +using UnityEditor; namespace VRM @@ -8,135 +6,24 @@ namespace VRM [CustomEditor(typeof(VRMMeta))] public class VRMMetaEditor : Editor { - //VRMMeta m_target; - SerializedProperty m_ScriptProp; - + Editor m_Inspector; SerializedProperty m_VRMMetaObjectProp; - SerializedProperty VRMMetaObjectProp - { - get { return m_VRMMetaObjectProp; } - set - { - if (m_VRMMetaObjectProp == value) return; - m_VRMMetaObjectProp = value; - // get props - Debug.Log("clear"); - m_propMap.Clear(); - } - } - - Dictionary m_propMap = new Dictionary(); - void InitMap(SerializedObject so) + private void OnDestroy() { - if (VRMMetaObjectProp == null) return; - //if (m_propMap.Count > 0) return; - m_propMap.Clear(); - - for (var it = so.GetIterator(); it.NextVisible(true);) - { - if (it.name == "m_Script") continue; - //Debug.LogFormat("{0}", it.name); - m_propMap.Add(it.name, so.FindProperty(it.name)); - } + UnityEditor.Editor.DestroyImmediate(m_Inspector); } private void OnEnable() { - //m_target = (VRMMeta)target; - m_ScriptProp = serializedObject.FindProperty("m_Script"); m_VRMMetaObjectProp = serializedObject.FindProperty("Meta"); + m_Inspector = Editor.CreateEditor(m_VRMMetaObjectProp.objectReferenceValue); } public override void OnInspectorGUI() { - serializedObject.Update(); - EditorGUILayout.PropertyField(m_ScriptProp, true); - EditorGUILayout.PropertyField(m_VRMMetaObjectProp, true); - serializedObject.ApplyModifiedProperties(); - - EditorGUILayout.Space(); -#if true - if (m_VRMMetaObjectProp.objectReferenceValue != null) - { - VRMMetaObjectGUI(new SerializedObject(m_VRMMetaObjectProp.objectReferenceValue)); - } -#else - if(m_target!=null && m_target.Meta != null) - { - VRMMetaObjectGUI(new SerializedObject(m_target.Meta)); - } -#endif - } - - bool m_foldoutInfo = true; - bool m_foldoutPermission = true; - bool m_foldoutDistribution = true; - void VRMMetaObjectGUI(SerializedObject so) - { - InitMap(so); - if (m_propMap == null || m_propMap.Count == 0) return; - - so.Update(); - - GUI.enabled = false; - EditorGUILayout.PropertyField(m_propMap["ExporterVersion"]); - if (VRMVersion.IsNewer(m_propMap["ExporterVersion"].stringValue)) - { - EditorGUILayout.HelpBox("Check UniVRM new version. https://github.com/dwango/UniVRM/releases", MessageType.Warning); - } - GUI.enabled = true; - - m_foldoutInfo = EditorGUILayout.Foldout(m_foldoutInfo, "Information"); - if (m_foldoutInfo) - { - EditorGUILayout.PropertyField(m_propMap["Title"]); - EditorGUILayout.PropertyField(m_propMap["Version"]); - EditorGUILayout.PropertyField(m_propMap["Author"]); - EditorGUILayout.PropertyField(m_propMap["ContactInformation"]); - EditorGUILayout.PropertyField(m_propMap["Reference"]); - - var thumbnail = m_propMap["Thumbnail"]; - EditorGUILayout.PropertyField(thumbnail); - thumbnail.objectReferenceValue = TextureField("", (Texture2D)thumbnail.objectReferenceValue, 100); - } - - EditorGUILayout.LabelField("License ", EditorStyles.boldLabel); - - m_foldoutPermission = EditorGUILayout.Foldout(m_foldoutPermission, "Personation / Characterization Permission"); - if (m_foldoutPermission) - { - EditorGUILayout.PropertyField(m_propMap["AllowedUser"], new GUIContent("A person who can perform with this avatar"), false); - EditorGUILayout.PropertyField(m_propMap["ViolentUssage"], new GUIContent("Violent acts using this avatar")); - EditorGUILayout.PropertyField(m_propMap["SexualUssage"], new GUIContent("Sexuality acts using this avatar")); - EditorGUILayout.PropertyField(m_propMap["CommercialUssage"], new GUIContent("For commercial use")); - EditorGUILayout.PropertyField(m_propMap["OtherPermissionUrl"], new GUIContent("Other License Url")); - } - - m_foldoutDistribution = EditorGUILayout.Foldout(m_foldoutDistribution, "Redistribution / Modifications License"); - if (m_foldoutDistribution) - { - var licenseType = m_propMap["LicenseType"]; - EditorGUILayout.PropertyField(licenseType); - if ((LicenseType)licenseType.intValue == LicenseType.Other) - { - EditorGUILayout.PropertyField(m_propMap["OtherLicenseUrl"]); - } - } - - so.ApplyModifiedProperties(); - } - - private static Texture2D TextureField(string name, Texture2D texture, int size) - { - GUILayout.BeginHorizontal(); - var style = new GUIStyle(GUI.skin.label); - style.alignment = TextAnchor.UpperCenter; - //style.fixedWidth = size; - GUILayout.Label(name, style); - var result = (Texture2D)EditorGUILayout.ObjectField(texture, typeof(Texture2D), false, GUILayout.Width(size), GUILayout.Height(size)); - GUILayout.EndVertical(); - return result; + EditorGUILayout.PropertyField(m_VRMMetaObjectProp); + m_Inspector.OnInspectorGUI(); } } } diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs new file mode 100644 index 0000000000..44a5848693 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace VRM +{ + [CustomEditor(typeof(VRMMetaObject))] + public class VRMMetaObjectEditor : Editor + { + SerializedProperty m_ScriptProp; + Dictionary m_propMap = new Dictionary(); + void InitMap(SerializedObject so) + { + // if (VRMMetaObjectProp == null) return; + //if (m_propMap.Count > 0) return; + m_propMap.Clear(); + + for (var it = so.GetIterator(); it.NextVisible(true);) + { + if (it.name == "m_Script") continue; + //Debug.LogFormat("{0}", it.name); + m_propMap.Add(it.name, so.FindProperty(it.name)); + } + } + + private void OnEnable() + { + m_ScriptProp = serializedObject.FindProperty("m_Script"); + InitMap(serializedObject); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + GUI.enabled = false; + EditorGUILayout.PropertyField(m_ScriptProp, true); + GUI.enabled = true; + serializedObject.ApplyModifiedProperties(); + + EditorGUILayout.Space(); + VRMMetaObjectGUI(serializedObject); + } + + bool m_foldoutInfo = true; + bool m_foldoutPermission = true; + bool m_foldoutDistribution = true; + void VRMMetaObjectGUI(SerializedObject so) + { + InitMap(so); + if (m_propMap == null || m_propMap.Count == 0) return; + + so.Update(); + + GUI.enabled = false; + EditorGUILayout.PropertyField(m_propMap["ExporterVersion"]); + if (VRMVersion.IsNewer(m_propMap["ExporterVersion"].stringValue)) + { + EditorGUILayout.HelpBox("Check UniVRM new version. https://github.com/dwango/UniVRM/releases", MessageType.Warning); + } + GUI.enabled = true; + + m_foldoutInfo = EditorGUILayout.Foldout(m_foldoutInfo, "Information"); + if (m_foldoutInfo) + { + EditorGUILayout.PropertyField(m_propMap["Title"]); + EditorGUILayout.PropertyField(m_propMap["Version"]); + EditorGUILayout.PropertyField(m_propMap["Author"]); + EditorGUILayout.PropertyField(m_propMap["ContactInformation"]); + EditorGUILayout.PropertyField(m_propMap["Reference"]); + + var thumbnail = m_propMap["Thumbnail"]; + EditorGUILayout.PropertyField(thumbnail); + thumbnail.objectReferenceValue = TextureField("", (Texture2D)thumbnail.objectReferenceValue, 100); + } + + EditorGUILayout.LabelField("License ", EditorStyles.boldLabel); + + m_foldoutPermission = EditorGUILayout.Foldout(m_foldoutPermission, "Personation / Characterization Permission"); + if (m_foldoutPermission) + { + EditorGUILayout.PropertyField(m_propMap["AllowedUser"], new GUIContent("A person who can perform with this avatar"), false); + EditorGUILayout.PropertyField(m_propMap["ViolentUssage"], new GUIContent("Violent acts using this avatar")); + EditorGUILayout.PropertyField(m_propMap["SexualUssage"], new GUIContent("Sexuality acts using this avatar")); + EditorGUILayout.PropertyField(m_propMap["CommercialUssage"], new GUIContent("For commercial use")); + EditorGUILayout.PropertyField(m_propMap["OtherPermissionUrl"], new GUIContent("Other License Url")); + } + + m_foldoutDistribution = EditorGUILayout.Foldout(m_foldoutDistribution, "Redistribution / Modifications License"); + if (m_foldoutDistribution) + { + var licenseType = m_propMap["LicenseType"]; + EditorGUILayout.PropertyField(licenseType); + if ((LicenseType)licenseType.intValue == LicenseType.Other) + { + EditorGUILayout.PropertyField(m_propMap["OtherLicenseUrl"]); + } + } + + so.ApplyModifiedProperties(); + } + + private static Texture2D TextureField(string name, Texture2D texture, int size) + { + GUILayout.BeginHorizontal(); + var style = new GUIStyle(GUI.skin.label); + style.alignment = TextAnchor.UpperCenter; + //style.fixedWidth = size; + GUILayout.Label(name, style); + var result = (Texture2D)EditorGUILayout.ObjectField(texture, typeof(Texture2D), false, GUILayout.Width(size), GUILayout.Height(size)); + GUILayout.EndVertical(); + return result; + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta new file mode 100644 index 0000000000..b14c584ee9 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c244f35fd2772004c9d51095f6326420 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 8d62269821b7fd52e8c4e44b7511bf255759cbbf Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 16:01:44 +0900 Subject: [PATCH 04/17] VRMMetaObjectEditor --- .../UniVRM/Editor/Meta/VRMMetaObjectEditor.cs | 75 ++++++++++++++++--- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs index 44a5848693..27af08d49f 100644 --- a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; @@ -8,18 +9,74 @@ namespace VRM public class VRMMetaObjectEditor : Editor { SerializedProperty m_ScriptProp; + + class CustomProperty + { + public SerializedProperty m_prop; + + public delegate (string, MessageType) Validator(SerializedProperty prop); + Validator m_validator; + + public CustomProperty(SerializedProperty prop, Validator validator) + { + m_prop = prop; + m_validator = validator; + } + + public void OnGUI() + { + // var old = m_prop.stringValue; + EditorGUILayout.PropertyField(m_prop); + var (msg, msgType) = m_validator(m_prop); + if (!string.IsNullOrEmpty(msg)) + { + EditorGUILayout.HelpBox(msg, msgType); + } + // return old != m_prop.stringValue; + } + } + List> m_customPropMap = new List>(); + Dictionary m_propMap = new Dictionary(); void InitMap(SerializedObject so) { - // if (VRMMetaObjectProp == null) return; - //if (m_propMap.Count > 0) return; m_propMap.Clear(); + m_customPropMap.Clear(); for (var it = so.GetIterator(); it.NextVisible(true);) { - if (it.name == "m_Script") continue; + switch (it.name) + { + case "m_Script": + break; + + case "Title": + case "Version": + case "Author": + m_customPropMap.Add(new KeyValuePair(it.name, new CustomProperty(so.FindProperty(it.name), prop => + { + if (string.IsNullOrEmpty(prop.stringValue)) + { + return ($"必須項目。{prop.name} を入力してください", MessageType.Error); + } + return ("", MessageType.None); + }))); + break; + + case "ContactInformation": + case "Reference": + m_customPropMap.Add(new KeyValuePair(it.name, + new CustomProperty(so.FindProperty(it.name), prop => + { + return ("", MessageType.None); + }))); + break; + + default: + m_propMap.Add(it.name, so.FindProperty(it.name)); + break; + } //Debug.LogFormat("{0}", it.name); - m_propMap.Add(it.name, so.FindProperty(it.name)); } } @@ -35,7 +92,6 @@ public override void OnInspectorGUI() GUI.enabled = false; EditorGUILayout.PropertyField(m_ScriptProp, true); GUI.enabled = true; - serializedObject.ApplyModifiedProperties(); EditorGUILayout.Space(); VRMMetaObjectGUI(serializedObject); @@ -62,11 +118,10 @@ void VRMMetaObjectGUI(SerializedObject so) m_foldoutInfo = EditorGUILayout.Foldout(m_foldoutInfo, "Information"); if (m_foldoutInfo) { - EditorGUILayout.PropertyField(m_propMap["Title"]); - EditorGUILayout.PropertyField(m_propMap["Version"]); - EditorGUILayout.PropertyField(m_propMap["Author"]); - EditorGUILayout.PropertyField(m_propMap["ContactInformation"]); - EditorGUILayout.PropertyField(m_propMap["Reference"]); + foreach (var kv in m_customPropMap) + { + kv.Value.OnGUI(); + } var thumbnail = m_propMap["Thumbnail"]; EditorGUILayout.PropertyField(thumbnail); From 01ec2a633c398fe51c4fc280e2724bf3792c93f0 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 16:29:44 +0900 Subject: [PATCH 05/17] VRMExporterWizard use VRMMetaObjectEditor --- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 10 +- .../UniVRM/Editor/Format/VRMExportSettings.cs | 102 +++++++++--------- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 55 ++++++++-- 3 files changed, 104 insertions(+), 63 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index 3bb10dce96..a8c30903bb 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -213,11 +213,11 @@ static void Export(string path, VRMExportSettings settings, List des ExportOnlyBlendShapePosition = settings.OnlyBlendshapePosition, RemoveVertexColor = settings.RemoveVertexColor }); - vrm.extensions.VRM.meta.title = settings.Title; - vrm.extensions.VRM.meta.version = settings.Version; - vrm.extensions.VRM.meta.author = settings.Author; - vrm.extensions.VRM.meta.contactInformation = settings.ContactInformation; - vrm.extensions.VRM.meta.reference = settings.Reference; + // vrm.extensions.VRM.meta.title = settings.Title; + // vrm.extensions.VRM.meta.version = settings.Version; + // vrm.extensions.VRM.meta.author = settings.Author; + // vrm.extensions.VRM.meta.contactInformation = settings.ContactInformation; + // vrm.extensions.VRM.meta.reference = settings.Reference; var bytes = vrm.ToGlbBytes(settings.UseExperimentalExporter ? SerializerTypes.Generated : SerializerTypes.UniJSON); File.WriteAllBytes(path, bytes); diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 8938243704..10c17bfea3 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -16,32 +16,32 @@ public class VRMExportSettings /// public GameObject Source; - #region Meta - /// - /// エクスポート名 - /// - public string Title; - - /// - /// エクスポートバージョン(エクスポートするModelのバージョン) - /// - public string Version; - - /// - /// 作者 - /// - public string Author; - - /// - /// 作者連絡先 - /// - public string ContactInformation; - - /// - /// 作品引用 - /// - public string Reference; - #endregion + // #region Meta + // /// + // /// エクスポート名 + // /// + // public string Title; + + // /// + // /// エクスポートバージョン(エクスポートするModelのバージョン) + // /// + // public string Version; + + // /// + // /// 作者 + // /// + // public string Author; + + // /// + // /// 作者連絡先 + // /// + // public string ContactInformation; + + // /// + // /// 作品引用 + // /// + // public string Reference; + // #endregion #region Settings /// @@ -189,18 +189,18 @@ public IEnumerable Validate() yield return Validation.Warning("There is a bone with the same name in the hierarchy. If exported, these bones will be automatically renamed."); } - if (string.IsNullOrEmpty(Title)) - { - yield return Validation.Error("Require Title. "); - } - if (string.IsNullOrEmpty(Version)) - { - yield return Validation.Error("Require Version. "); - } - if (string.IsNullOrEmpty(Author)) - { - yield return Validation.Error("Require Author. "); - } + // if (string.IsNullOrEmpty(Title)) + // { + // yield return Validation.Error("Require Title. "); + // } + // if (string.IsNullOrEmpty(Version)) + // { + // yield return Validation.Error("Require Version. "); + // } + // if (string.IsNullOrEmpty(Author)) + // { + // yield return Validation.Error("Require Author. "); + // } if (ReduceBlendshape && Source.GetComponent() == null) { @@ -331,19 +331,19 @@ public void InitializeFrom(GameObject go) // Meta // var meta = Source == 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"; - } + // 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 60340d3ae1..0b52436d2a 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -14,6 +14,29 @@ namespace VRM /// public class VRMExporterWizard : EditorWindow { + [SerializeField] + public VRMExportSettings m_settings = new VRMExportSettings(); + + VRMMetaObject m_meta; + VRMMetaObject Meta + { + get { return m_meta; } + set + { + if (m_meta == value) return; + if (m_meta != null) + { + UnityEditor.Editor.DestroyImmediate(m_metaEditor); + } + m_meta = value; + if (m_meta != null) + { + m_metaEditor = Editor.CreateEditor(m_meta); + } + } + } + + Editor m_metaEditor; Editor m_Inspector; private string m_HelpString = ""; @@ -26,6 +49,7 @@ public class VRMExporterWizard : EditorWindow private void OnDestroy() { UnityEditor.Editor.DestroyImmediate(m_Inspector); + Meta = null; } private void InvokeWizardUpdate() @@ -118,11 +142,17 @@ private void OnGUI() protected virtual bool DrawWizardGUI() { - if (m_Inspector == null) + if (m_metaEditor != null) + { + m_metaEditor.OnInspectorGUI(); + } { - m_Inspector = Editor.CreateEditor(this); + if (m_Inspector == null) + { + m_Inspector = Editor.CreateEditor(this); + } + m_Inspector.OnInspectorGUI(); } - m_Inspector.OnInspectorGUI(); return true; } @@ -251,12 +281,8 @@ public bool isValid const string EXTENSION = ".vrm"; - VRMMeta m_meta; - private static string m_lastExportDir; - public VRMExportSettings m_settings = new VRMExportSettings(); - public static void CreateWizard() { var wiz = VRMExporterWizard.DisplayWizard( @@ -326,6 +352,21 @@ void OnWizardUpdate() helpString = helpBuilder.ToString(); errorString = errorBuilder.ToString(); + + if (m_settings.Source == null) + { + Meta = null; + } + else + { + var meta = m_settings.Source.GetComponent(); + if (meta != null) + { + Meta = meta.Meta; + } + } + + Repaint(); } } } From f9c240476fc44b11d7e21604456ca6106f180c70 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 18:39:54 +0900 Subject: [PATCH 06/17] separate Validation.cs --- .../UniVRM/Editor/Format/VRMExportSettings.cs | 73 ++------ .../UniVRM/Editor/Format/VRMExporterWizard.cs | 169 +++++++++--------- .../UniVRM/Editor/Meta/VRMMetaObjectEditor.cs | 19 +- .../VRM/UniVRM/Scripts/Meta/VRMMetaObject.cs | 19 +- Assets/VRM/UniVRM/Scripts/Validation.cs | 55 ++++++ 5 files changed, 186 insertions(+), 149 deletions(-) create mode 100644 Assets/VRM/UniVRM/Scripts/Validation.cs diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 10c17bfea3..368a602cff 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -5,7 +5,6 @@ using UnityEditor; using UnityEngine; - namespace VRM { [Serializable] @@ -98,32 +97,6 @@ public static bool IsFileNameLengthTooLong(string fileName) return fileName.Length > 64; } - public struct Validation - { - /// - /// エクスポート可能か否か。 - /// true のメッセージは警告 - /// false のメッセージはエラー - /// - public readonly bool CanExport; - public readonly String Message; - - Validation(bool canExport, string message) - { - CanExport = canExport; - Message = message; - } - - public static Validation Error(string msg) - { - return new Validation(false, msg); - } - - public static Validation Warning(string msg) - { - return new Validation(true, msg); - } - } /// /// ボーン名の重複を確認 @@ -165,23 +138,26 @@ public IEnumerable Validate() { 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) + else { - yield return Validation.Error("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. "); - } + 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"); + 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()) @@ -189,19 +165,6 @@ public IEnumerable Validate() yield return Validation.Warning("There is a bone with the same name in the hierarchy. If exported, these bones will be automatically renamed."); } - // if (string.IsNullOrEmpty(Title)) - // { - // yield return Validation.Error("Require Title. "); - // } - // if (string.IsNullOrEmpty(Version)) - // { - // yield return Validation.Error("Require Version. "); - // } - // if (string.IsNullOrEmpty(Author)) - // { - // yield return Validation.Error("Require Author. "); - // } - if (ReduceBlendshape && Source.GetComponent() == null) { yield return Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once."); diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 0b52436d2a..0cc1013bf5 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -1,8 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Text; using UnityEditor; using UnityEngine; using uei = UnityEngine.Internal; @@ -29,19 +29,34 @@ VRMMetaObject Meta UnityEditor.Editor.DestroyImmediate(m_metaEditor); } m_meta = value; - if (m_meta != null) + if (m_meta == null) + { + m_meta = TmpMeta; + } + // m_metaEditor = Editor.CreateEditor(m_meta); + } + } + + VRMMetaObject m_tmpMeta; + VRMMetaObject TmpMeta + { + get + { + if (m_tmpMeta == null) { - m_metaEditor = Editor.CreateEditor(m_meta); + m_tmpMeta = ScriptableObject.CreateInstance(); } + return m_tmpMeta; } } Editor m_metaEditor; Editor m_Inspector; - private string m_HelpString = ""; - private string m_ErrorString = ""; private bool m_IsValid = true; + + List m_validations = new List(); + private Vector2 m_ScrollPosition; private string m_CreateButton = "Create"; private string m_OtherButton = ""; @@ -50,6 +65,7 @@ private void OnDestroy() { UnityEditor.Editor.DestroyImmediate(m_Inspector); Meta = null; + ScriptableObject.DestroyImmediate(m_tmpMeta); } private void InvokeWizardUpdate() @@ -87,7 +103,6 @@ static BeginVerticalScrollViewFunc BeginVerticalScrollView private void OnGUI() { EditorGUIUtility.labelWidth = 150; - GUILayout.Label(m_HelpString, EditorStyles.wordWrappedLabel, GUILayout.ExpandHeight(true)); // Render contents using Generic Inspector GUI m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); @@ -96,44 +111,49 @@ private void OnGUI() EditorGUILayout.EndScrollView(); // Create and Other Buttons - GUILayout.BeginVertical(); - if (m_ErrorString != string.Empty) - GUILayout.Label(m_ErrorString, Styles.errorText, GUILayout.MinHeight(32)); - else - GUILayout.Label(string.Empty, GUILayout.MinHeight(32)); - GUILayout.FlexibleSpace(); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - GUI.enabled = m_IsValid; - - const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; - if (m_OtherButton != "" && GUILayout.Button(m_OtherButton, GUILayout.MinWidth(100))) { - MethodInfo method = GetType().GetMethod("OnWizardOtherButton", kInstanceInvokeFlags); - if (method != null) + // errors + GUILayout.BeginVertical(); + // foreach (var v in m_validations) + // { + // v.DrawGUI(); + // } + GUILayout.FlexibleSpace(); + { - method.Invoke(this, null); - GUIUtility.ExitGUI(); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUI.enabled = m_IsValid; + + const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; + if (m_OtherButton != "" && GUILayout.Button(m_OtherButton, GUILayout.MinWidth(100))) + { + MethodInfo method = GetType().GetMethod("OnWizardOtherButton", kInstanceInvokeFlags); + if (method != null) + { + method.Invoke(this, null); + GUIUtility.ExitGUI(); + } + else + Debug.LogError("OnWizardOtherButton has not been implemented in script"); + } + + if (m_CreateButton != "" && GUILayout.Button(m_CreateButton, GUILayout.MinWidth(100))) + { + MethodInfo method = GetType().GetMethod("OnWizardCreate", kInstanceInvokeFlags); + if (method != null) + method.Invoke(this, null); + else + Debug.LogError("OnWizardCreate has not been implemented in script"); + Close(); + GUIUtility.ExitGUI(); + } + GUI.enabled = true; + + GUILayout.EndHorizontal(); } - else - Debug.LogError("OnWizardOtherButton has not been implemented in script"); - } - - if (m_CreateButton != "" && GUILayout.Button(m_CreateButton, GUILayout.MinWidth(100))) - { - MethodInfo method = GetType().GetMethod("OnWizardCreate", kInstanceInvokeFlags); - if (method != null) - method.Invoke(this, null); - else - Debug.LogError("OnWizardCreate has not been implemented in script"); - Close(); - GUIUtility.ExitGUI(); + GUILayout.EndVertical(); } - GUI.enabled = true; - - GUILayout.EndHorizontal(); - GUILayout.EndVertical(); if (modified) InvokeWizardUpdate(); @@ -142,8 +162,12 @@ private void OnGUI() protected virtual bool DrawWizardGUI() { - if (m_metaEditor != null) + if (Meta != null) { + if (m_metaEditor == null) + { + m_metaEditor = Editor.CreateEditor(Meta); + } m_metaEditor.OnInspectorGUI(); } { @@ -212,36 +236,6 @@ public static VRMExporterWizard DisplayWizard(string title, Type klass, [uei.Def // // This is called when the user clicks on the Create button. // void OnWizardCreate(); - // Allows you to set the help text of the wizard. - public string helpString - { - get { return m_HelpString; } - set - { - var newString = value ?? string.Empty; - if (m_HelpString != newString) - { - m_HelpString = newString; - Repaint(); - } - } - } - - // Allows you to set the error text of the wizard. - public string errorString - { - get { return m_ErrorString; } - set - { - var newString = value ?? string.Empty; - if (m_ErrorString != newString) - { - m_ErrorString = newString; - Repaint(); - } - } - } - // Allows you to set the create button text of the wizard. public string createButtonName { @@ -299,11 +293,13 @@ void OnEnable() { // Debug.Log("OnEnable"); Undo.willFlushUndoRecord += OnWizardUpdate; + Selection.selectionChanged += OnWizardUpdate; } void OnDisable() { // Debug.Log("OnDisable"); + Selection.selectionChanged -= OnWizardUpdate; Undo.willFlushUndoRecord -= OnWizardUpdate; } @@ -333,25 +329,19 @@ void OnWizardCreate() void OnWizardUpdate() { - isValid = true; - var helpBuilder = new StringBuilder(); - var errorBuilder = new StringBuilder(); - - foreach (var validation in m_settings.Validate()) + m_validations.Clear(); + m_validations.AddRange(m_settings.Validate()); + if (Meta != null) { - if (!validation.CanExport) - { - isValid = false; - errorBuilder.Append(validation.Message); - } - else - { - helpBuilder.AppendLine(validation.Message); - } + m_validations.AddRange(Meta.Validate()); + } + else + { + m_validations.Add(Validation.Error("meta がありません")); } - helpString = helpBuilder.ToString(); - errorString = errorBuilder.ToString(); + var hasError = m_validations.Any(x => !x.CanExport); + m_IsValid = !hasError; if (m_settings.Source == null) { @@ -364,9 +354,14 @@ void OnWizardUpdate() { Meta = meta.Meta; } + else + { + Meta = null; + } } Repaint(); + // GUIUtility.ExitGUI(); } } } diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs index 27af08d49f..3768e9459a 100644 --- a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs @@ -8,7 +8,7 @@ namespace VRM [CustomEditor(typeof(VRMMetaObject))] public class VRMMetaObjectEditor : Editor { - SerializedProperty m_ScriptProp; + // SerializedProperty m_ScriptProp; class CustomProperty { @@ -42,6 +42,10 @@ void InitMap(SerializedObject so) { m_propMap.Clear(); m_customPropMap.Clear(); + if (so == null) + { + return; + } for (var it = so.GetIterator(); it.NextVisible(true);) { @@ -82,16 +86,19 @@ void InitMap(SerializedObject so) private void OnEnable() { - m_ScriptProp = serializedObject.FindProperty("m_Script"); - InitMap(serializedObject); + // m_ScriptProp = serializedObject.FindProperty("m_Script"); + if (serializedObject != null) + { + InitMap(serializedObject); + } } public override void OnInspectorGUI() { serializedObject.Update(); - GUI.enabled = false; - EditorGUILayout.PropertyField(m_ScriptProp, true); - GUI.enabled = true; + // GUI.enabled = false; + // EditorGUILayout.PropertyField(m_ScriptProp, true); + // GUI.enabled = true; EditorGUILayout.Space(); VRMMetaObjectGUI(serializedObject); diff --git a/Assets/VRM/UniVRM/Scripts/Meta/VRMMetaObject.cs b/Assets/VRM/UniVRM/Scripts/Meta/VRMMetaObject.cs index cff8d80a97..bf352264cb 100644 --- a/Assets/VRM/UniVRM/Scripts/Meta/VRMMetaObject.cs +++ b/Assets/VRM/UniVRM/Scripts/Meta/VRMMetaObject.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using System.Collections.Generic; +using UnityEngine; namespace VRM @@ -63,5 +64,21 @@ public bool Equals(VRMMetaObject other) ; } */ + + public IEnumerable Validate() + { + if (string.IsNullOrEmpty(Title)) + { + yield return Validation.Error("Require Title. "); + } + if (string.IsNullOrEmpty(Version)) + { + yield return Validation.Error("Require Version. "); + } + if (string.IsNullOrEmpty(Author)) + { + yield return Validation.Error("Require Author. "); + } + } } } diff --git a/Assets/VRM/UniVRM/Scripts/Validation.cs b/Assets/VRM/UniVRM/Scripts/Validation.cs new file mode 100644 index 0000000000..df2d03a888 --- /dev/null +++ b/Assets/VRM/UniVRM/Scripts/Validation.cs @@ -0,0 +1,55 @@ +using System; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace VRM +{ + public struct Validation + { + /// + /// エクスポート可能か否か。 + /// true のメッセージは警告 + /// false のメッセージはエラー + /// + public readonly bool CanExport; + public readonly String Message; + + Validation(bool canExport, string message) + { + CanExport = canExport; + Message = message; + } + +#if UNITY_EDITOR + public void DrawGUI() + { + if (string.IsNullOrEmpty(Message)) + { + return; + } + + if (CanExport) + { + // warning + EditorGUILayout.HelpBox(Message, MessageType.Warning); + } + else + { + // error + EditorGUILayout.HelpBox(Message, MessageType.Error); + } + } +#endif + + public static Validation Error(string msg) + { + return new Validation(false, msg); + } + + public static Validation Warning(string msg) + { + return new Validation(true, msg); + } + } +} \ No newline at end of file From 501eb6edd0759a2e3cb0195ab2ee03fca3385023 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 19:29:57 +0900 Subject: [PATCH 07/17] =?UTF-8?q?WIP=20=E6=A7=8B=E6=88=90=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E4=B8=AD=E3=80=82validation=20=E4=B8=80=E6=99=82?= =?UTF-8?q?=E5=81=9C=E6=AD=A2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniVRM/Editor/Format/VRMEditorExporter.cs | 12 +- .../UniVRM/Editor/Format/VRMExportSettings.cs | 448 ++++++++---------- .../UniVRM/Editor/Format/VRMExporterMenu.cs | 30 +- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 111 +++-- .../UniVRM/Editor/Meta/VRMMetaObjectEditor.cs | 15 +- Assets/VRM/UniVRM/Scripts/Validation.cs.meta | 11 + 6 files changed, 315 insertions(+), 312 deletions(-) create mode 100644 Assets/VRM/UniVRM/Scripts/Validation.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs index a8c30903bb..c9f88f170c 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs @@ -16,12 +16,12 @@ public static class VRMEditorExporter /// /// 出力先 /// エクスポート設定 - public static void Export(string path, VRMExportSettings settings) + public static void Export(string path, GameObject exportRoot, VRMExportSettings settings) { List destroy = new List(); try { - Export(path, settings, destroy); + Export(path, exportRoot, settings, destroy); } finally { @@ -136,9 +136,9 @@ static void ForceUniqueName(Transform transform, Dictionary nameCou /// /// /// 作業が終わったらDestoryするべき一時オブジェクト - static void Export(string path, VRMExportSettings settings, List destroy) + static void Export(string path, GameObject exportRoot, VRMExportSettings settings, List destroy) { - var target = settings.Source; + var target = exportRoot; // 常にコピーする。シーンを変化させない target = GameObject.Instantiate(target); @@ -146,8 +146,8 @@ static void Export(string path, VRMExportSettings settings, List des { // copy元 - var animator = settings.Source.GetComponent(); - var beforeTransforms = settings.Source.GetComponentsInChildren(); + var animator = exportRoot.GetComponent(); + var beforeTransforms = exportRoot.GetComponentsInChildren(); // copy先 var afterTransforms = target.GetComponentsInChildren(); // copy先のhumanoidBoneのリストを得る diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 368a602cff..62dc72c404 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -8,40 +8,8 @@ namespace VRM { [Serializable] - public class VRMExportSettings + public class VRMExportSettings : ScriptableObject { - /// - /// エクスポート対象 - /// - public GameObject Source; - - // #region Meta - // /// - // /// エクスポート名 - // /// - // public string Title; - - // /// - // /// エクスポートバージョン(エクスポートするModelのバージョン) - // /// - // public string Version; - - // /// - // /// 作者 - // /// - // public string Author; - - // /// - // /// 作者連絡先 - // /// - // public string ContactInformation; - - // /// - // /// 作品引用 - // /// - // public string Reference; - // #endregion - #region Settings /// /// エクスポート時に強制的にT-Pose化する @@ -97,216 +65,212 @@ 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()); + // } - /// - /// ボーン名の重複を確認 - /// - /// - bool DuplicateBoneNameExists() - { - var bones = Source.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 (Source == null) - { - yield return Validation.Error("Require source"); - yield break; - } - - if (Source.transform.position != Vector3.zero || - Source.transform.rotation != Quaternion.identity || - Source.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 = Source.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 && Source.GetComponent() == null) - { - yield return Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once."); - } - - var vertexColor = Source.GetComponentsInChildren().Any(x => x.sharedMesh.colors.Length > 0); - if (vertexColor) - { - yield return Validation.Warning("This model contains vertex color"); - } - - var renderers = Source.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 = Source.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 = Source.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 = Source.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) - { - if (Source == go) return; - Source = go; - - // - // initialize - // - var desc = Source == null ? null : go.GetComponent(); - if (desc == null) - { - // 初回のVRMエクスポートとみなす - ForceTPose = false; // option - PoseFreeze = true; - } - else - { - // すでに正規化済みとみなす - ForceTPose = false; - PoseFreeze = false; - } + // /// + // /// エクスポート可能か検証する + // /// + // /// + // 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 - // - var meta = Source == 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"; - // } - } + // /// + // /// 対象のモデルから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/VRMExporterMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs index 38bd6da806..53564a092e 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs @@ -8,23 +8,23 @@ public static class VRMExporterMenu { const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid"; - [MenuItem(CONVERT_HUMANOID_KEY, true, 1)] - private static bool ExportValidate() - { - var root = Selection.activeObject as GameObject; - if (root == null) - { - return false; - } + // [MenuItem(CONVERT_HUMANOID_KEY, true, 1)] + // private static bool ExportValidate() + // { + // var root = Selection.activeObject as GameObject; + // if (root == null) + // { + // return false; + // } - var animator = root.GetComponent(); - if (animator == null) - { - return false; - } + // var animator = root.GetComponent(); + // if (animator == null) + // { + // return false; + // } - return true; - } + // return true; + // } [MenuItem(CONVERT_HUMANOID_KEY, false, 1)] private static void ExportFromMenu() diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 0cc1013bf5..684d91f898 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -15,7 +15,12 @@ namespace VRM public class VRMExporterWizard : EditorWindow { [SerializeField] - public VRMExportSettings m_settings = new VRMExportSettings(); + public GameObject ExportRoot; + + SerializedProperty m_exportRoot; + + [SerializeField] + public VRMExportSettings m_settings; VRMMetaObject m_meta; VRMMetaObject Meta @@ -24,16 +29,37 @@ VRMMetaObject Meta set { if (m_meta == value) return; - if (m_meta != null) + if (m_metaEditor != null) { UnityEditor.Editor.DestroyImmediate(m_metaEditor); + m_metaEditor = null; } m_meta = value; - if (m_meta == null) + } + } + + void UpdateRoot(GameObject root) + { + if (root == ExportRoot) + { + return; + } + ExportRoot = root; + if (ExportRoot == null) + { + Meta = null; + } + else + { + var meta = ExportRoot.GetComponent(); + if (meta != null) + { + Meta = meta.Meta; + } + else { - m_meta = TmpMeta; + Meta = null; } - // m_metaEditor = Editor.CreateEditor(m_meta); } } @@ -66,6 +92,7 @@ private void OnDestroy() UnityEditor.Editor.DestroyImmediate(m_Inspector); Meta = null; ScriptableObject.DestroyImmediate(m_tmpMeta); + ScriptableObject.DestroyImmediate(m_settings); } private void InvokeWizardUpdate() @@ -104,6 +131,10 @@ private void OnGUI() { EditorGUIUtility.labelWidth = 150; + EditorGUILayout.LabelField("ExportRoot"); + var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true); + UpdateRoot(root); + // Render contents using Generic Inspector GUI m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); GUIUtility.GetControlID(645789, FocusType.Passive); @@ -162,19 +193,32 @@ private void OnGUI() protected virtual bool DrawWizardGUI() { - if (Meta != null) { if (m_metaEditor == null) { - m_metaEditor = Editor.CreateEditor(Meta); + if (Meta != null) + { + m_metaEditor = Editor.CreateEditor(Meta); + } + else + { + m_metaEditor = Editor.CreateEditor(TmpMeta); + } } + EditorGUILayout.LabelField("Meta ", EditorStyles.boldLabel); m_metaEditor.OnInspectorGUI(); } { if (m_Inspector == null) { - m_Inspector = Editor.CreateEditor(this); + if (m_settings == null) + { + m_settings = ScriptableObject.CreateInstance(); + } + m_Inspector = Editor.CreateEditor(m_settings); } + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Export Settings ", EditorStyles.boldLabel); m_Inspector.OnInspectorGUI(); } return true; @@ -284,7 +328,7 @@ public static void CreateWizard() var go = Selection.activeObject as GameObject; // update checkbox - wiz.m_settings.InitializeFrom(go); + wiz.UpdateRoot(go); wiz.OnWizardUpdate(); } @@ -315,7 +359,7 @@ void OnWizardCreate() var path = EditorUtility.SaveFilePanel( "Save vrm", directory, - m_settings.Source.name + EXTENSION, + ExportRoot.name + EXTENSION, EXTENSION.Substring(1)); if (string.IsNullOrEmpty(path)) { @@ -324,41 +368,26 @@ void OnWizardCreate() m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/"); // export - VRMEditorExporter.Export(path, m_settings); + VRMEditorExporter.Export(path, ExportRoot, m_settings); } void OnWizardUpdate() { - m_validations.Clear(); - m_validations.AddRange(m_settings.Validate()); - if (Meta != null) - { - m_validations.AddRange(Meta.Validate()); - } - else - { - m_validations.Add(Validation.Error("meta がありません")); - } - - var hasError = m_validations.Any(x => !x.CanExport); - m_IsValid = !hasError; - - if (m_settings.Source == null) - { - Meta = null; - } - else - { - var meta = m_settings.Source.GetComponent(); - if (meta != null) - { - Meta = meta.Meta; - } - else - { - Meta = null; - } - } + // m_validations.Clear(); + // m_validations.AddRange(m_settings.Validate()); + // if (Meta != null) + // { + // m_validations.AddRange(Meta.Validate()); + // } + // else + // { + // m_validations.Add(Validation.Error("meta がありません")); + // } + + // var hasError = m_validations.Any(x => !x.CanExport); + // m_IsValid = !hasError; + + UpdateRoot(ExportRoot); Repaint(); // GUIUtility.ExitGUI(); diff --git a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs index 3768e9459a..5a6e21e807 100644 --- a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs @@ -87,10 +87,7 @@ void InitMap(SerializedObject so) private void OnEnable() { // m_ScriptProp = serializedObject.FindProperty("m_Script"); - if (serializedObject != null) - { - InitMap(serializedObject); - } + InitMap(serializedObject); } public override void OnInspectorGUI() @@ -115,6 +112,7 @@ void VRMMetaObjectGUI(SerializedObject so) so.Update(); GUI.enabled = false; + EditorGUILayout.PropertyField(m_propMap["ExporterVersion"]); if (VRMVersion.IsNewer(m_propMap["ExporterVersion"].stringValue)) { @@ -125,14 +123,15 @@ void VRMMetaObjectGUI(SerializedObject so) m_foldoutInfo = EditorGUILayout.Foldout(m_foldoutInfo, "Information"); if (m_foldoutInfo) { + // texture + var thumbnail = m_propMap["Thumbnail"]; + EditorGUILayout.PropertyField(thumbnail); + thumbnail.objectReferenceValue = TextureField("", (Texture2D)thumbnail.objectReferenceValue, 100); + foreach (var kv in m_customPropMap) { kv.Value.OnGUI(); } - - var thumbnail = m_propMap["Thumbnail"]; - EditorGUILayout.PropertyField(thumbnail); - thumbnail.objectReferenceValue = TextureField("", (Texture2D)thumbnail.objectReferenceValue, 100); } EditorGUILayout.LabelField("License ", EditorStyles.boldLabel); diff --git a/Assets/VRM/UniVRM/Scripts/Validation.cs.meta b/Assets/VRM/UniVRM/Scripts/Validation.cs.meta new file mode 100644 index 0000000000..b7ff477307 --- /dev/null +++ b/Assets/VRM/UniVRM/Scripts/Validation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 703726d4973c85447a3bd2ae30c760f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 97368fb99249d79afe6fbebd0c4b6219d3a9b462 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 7 Aug 2020 19:42:46 +0900 Subject: [PATCH 08/17] tab --- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 684d91f898..c659248028 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -191,8 +191,46 @@ private void OnGUI() 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() { + using (new EditorGUILayout.HorizontalScope()) + { + GUILayout.FlexibleSpace(); + // タブを描画する + _tab = (Tabs)GUILayout.Toolbar((int)_tab, TabStyles.TabToggles, TabStyles.TabButtonStyle, TabStyles.TabButtonSize); + GUILayout.FlexibleSpace(); + } + { if (m_metaEditor == null) { @@ -205,8 +243,6 @@ protected virtual bool DrawWizardGUI() m_metaEditor = Editor.CreateEditor(TmpMeta); } } - EditorGUILayout.LabelField("Meta ", EditorStyles.boldLabel); - m_metaEditor.OnInspectorGUI(); } { if (m_Inspector == null) @@ -217,10 +253,19 @@ protected virtual bool DrawWizardGUI() } m_Inspector = Editor.CreateEditor(m_settings); } - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Export Settings ", EditorStyles.boldLabel); - m_Inspector.OnInspectorGUI(); } + + switch (_tab) + { + case Tabs.Meta: + m_metaEditor.OnInspectorGUI(); + break; + + case Tabs.ExportSettings: + m_Inspector.OnInspectorGUI(); + break; + } + return true; } @@ -393,4 +438,5 @@ void OnWizardUpdate() // GUIUtility.ExitGUI(); } } + } From fb2667923771efdfff61229523e0f8e958ecfe10 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 14:30:09 +0900 Subject: [PATCH 09/17] fix editor error when compiled --- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index c659248028..9108b2a4a7 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -64,17 +64,6 @@ void UpdateRoot(GameObject root) } VRMMetaObject m_tmpMeta; - VRMMetaObject TmpMeta - { - get - { - if (m_tmpMeta == null) - { - m_tmpMeta = ScriptableObject.CreateInstance(); - } - return m_tmpMeta; - } - } Editor m_metaEditor; Editor m_Inspector; @@ -87,12 +76,30 @@ VRMMetaObject TmpMeta private string m_CreateButton = "Create"; private string m_OtherButton = ""; - private void OnDestroy() + void OnEnable() { + // Debug.Log("OnEnable"); + Undo.willFlushUndoRecord += OnWizardUpdate; + Selection.selectionChanged += OnWizardUpdate; + + m_tmpMeta = ScriptableObject.CreateInstance(); + } + + void OnDisable() + { + // Debug.Log("OnDisable"); + Selection.selectionChanged -= OnWizardUpdate; + Undo.willFlushUndoRecord -= OnWizardUpdate; + + UnityEditor.Editor.DestroyImmediate(m_metaEditor); + m_metaEditor = null; UnityEditor.Editor.DestroyImmediate(m_Inspector); + m_Inspector = null; Meta = null; ScriptableObject.DestroyImmediate(m_tmpMeta); + m_tmpMeta = null; ScriptableObject.DestroyImmediate(m_settings); + m_settings = null; } private void InvokeWizardUpdate() @@ -223,6 +230,11 @@ public static GUIContent[] TabToggles protected virtual bool DrawWizardGUI() { + if(m_tmpMeta == null) + { + return false; + } + using (new EditorGUILayout.HorizontalScope()) { GUILayout.FlexibleSpace(); @@ -240,7 +252,7 @@ protected virtual bool DrawWizardGUI() } else { - m_metaEditor = Editor.CreateEditor(TmpMeta); + m_metaEditor = Editor.CreateEditor(m_tmpMeta); } } } @@ -378,20 +390,6 @@ public static void CreateWizard() wiz.OnWizardUpdate(); } - void OnEnable() - { - // Debug.Log("OnEnable"); - Undo.willFlushUndoRecord += OnWizardUpdate; - Selection.selectionChanged += OnWizardUpdate; - } - - void OnDisable() - { - // Debug.Log("OnDisable"); - Selection.selectionChanged -= OnWizardUpdate; - Undo.willFlushUndoRecord -= OnWizardUpdate; - } - void OnWizardCreate() { string directory; @@ -438,5 +436,4 @@ void OnWizardUpdate() // GUIUtility.ExitGUI(); } } - } From 6a9d309042a2a75482609db4ad6f937622bbd52b Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 16:11:49 +0900 Subject: [PATCH 10/17] restore validation. separate TabBar.cs --- .../UniVRM/Editor/Format/VRMExportSettings.cs | 221 +----------- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 324 +++++++++++++++--- .../UniVRM/Editor/Meta/VRMMetaObjectEditor.cs | 2 +- Assets/VRM/UniVRM/Editor/TabBar.cs | 39 +++ Assets/VRM/UniVRM/Editor/TabBar.cs.meta | 11 + .../Editor/Tests/InvalidFileNameTest.cs | 2 +- 6 files changed, 333 insertions(+), 266 deletions(-) create mode 100644 Assets/VRM/UniVRM/Editor/TabBar.cs create mode 100644 Assets/VRM/UniVRM/Editor/TabBar.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 62dc72c404..33f2c1501e 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 @@ public class VRMExportSettings : ScriptableObject [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 9108b2a4a7..433062a72d 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -63,6 +63,181 @@ void UpdateRoot(GameObject root) } } + /// + /// ボーン名の重複を確認 + /// + /// + 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 @@ static BeginVerticalScrollViewFunc BeginVerticalScrollView } } - //@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 @@ private void OnGUI() { // errors GUILayout.BeginVertical(); - // foreach (var v in m_validations) - // { - // v.DrawGUI(); - // } - GUILayout.FlexibleSpace(); + // GUILayout.FlexibleSpace(); { GUILayout.BeginHorizontal(); @@ -198,50 +442,28 @@ private void OnGUI() GUILayout.Space(8); } - // Style定義 enum Tabs { Meta, ExportSettings, } + Tabs _tab; - 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"; + GUIStyle TabButtonStyle => "LargeButton"; - // GUI.ToolbarButtonSize.FitToContentsも設定できる - public static readonly GUI.ToolbarButtonSize TabButtonSize = GUI.ToolbarButtonSize.Fixed; - } + // GUI.ToolbarButtonSize.FitToContentsも設定できる + GUI.ToolbarButtonSize TabButtonSize => GUI.ToolbarButtonSize.Fixed; - Tabs _tab; - - protected virtual bool DrawWizardGUI() + 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 @@ void OnWizardCreate() 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 5a6e21e807..c06ca36754 100644 --- a/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs +++ b/Assets/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs @@ -88,7 +88,7 @@ private void OnEnable() { // 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 0000000000..76a1c748bc --- /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 0000000000..ee1d88d40b --- /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 ef6d0831c6..d7727ce782 100644 --- a/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs +++ b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs @@ -14,7 +14,7 @@ public class InvalidFileNameTest [TestCase("AliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAlicia", true)] public void DetectFileNameLength(string fileName, bool isIllegal) { - var result = VRMExportSettings.IsFileNameLengthTooLong(fileName); + var result = VRMExporterWizard.IsFileNameLengthTooLong(fileName); Assert.AreEqual(result, isIllegal); } From 4a6849c13886da65c1770d25b01505124a22c98b Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 17:52:18 +0900 Subject: [PATCH 11/17] VRMExportSettingsEditor.cs --- .../UniVRM/Editor/Format/VRMExportSettings.cs | 2 - .../Editor/Format/VRMExportSettingsEditor.cs | 121 ++++++++++++++++++ .../Format/VRMExportSettingsEditor.cs.meta | 11 ++ .../UniVRM/Editor/Format/VRMExporterWizard.cs | 115 ++++++++++------- 4 files changed, 200 insertions(+), 49 deletions(-) create mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs create mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 33f2c1501e..9c2dc96e5d 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -6,7 +6,6 @@ namespace VRM [Serializable] public class VRMExportSettings : ScriptableObject { - #region Settings /// /// エクスポート時に強制的にT-Pose化する /// @@ -54,6 +53,5 @@ public class VRMExportSettings : ScriptableObject /// [Tooltip("Remove vertex color")] public bool RemoveVertexColor = false; - #endregion } } diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs new file mode 100644 index 0000000000..027547e558 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs @@ -0,0 +1,121 @@ + +using UnityEditor; +using UnityEngine; + +namespace VRM +{ + [CustomEditor(typeof(VRMExportSettings))] + public class VRMExportSettingsEditor : Editor + { + class CheckBoxProp + { + public SerializedProperty Property; + public string Description; + + public CheckBoxProp(SerializedProperty property, string desc) + { + Property = property; + Description = desc; + } + + public bool Draw() + { + EditorGUILayout.HelpBox(Description, MessageType.None); + var modified = EditorGUILayout.PropertyField(Property); + EditorGUILayout.Space(); + return modified; + } + } + + /// + /// エクスポート時に強制的にT-Pose化する + /// + [Tooltip("Option")] + public bool ForceTPose = false; + + /// + /// エクスポート時にヒエラルキーの正規化を実施する + /// + [Tooltip("Require only first time")] + public bool PoseFreeze = true; + + /// + /// エクスポート時に新しいJsonSerializerを使う + /// + [Tooltip("Use new JSON serializer")] + public bool UseExperimentalExporter = false; + + /// + /// BlendShapeのシリアライズにSparseAccessorを使う + /// + [Tooltip("Use sparse accessor for blendshape. This may reduce vrm size")] + public bool UseSparseAccessor = false; + + /// + /// BlendShapeのPositionのみをエクスポートする + /// + [Tooltip("UniVRM-0.54 or later can load it. Otherwise fail to load")] + public bool OnlyBlendshapePosition = false; + + /// + /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する + /// + [Tooltip("Remove blendshape that is not used from BlendShapeClip")] + public bool ReduceBlendshape = false; + + /// + /// skip if BlendShapeClip.Preset == Unknown + /// + [Tooltip("Remove blendShapeClip that preset is Unknown")] + public bool ReduceBlendshapeClip = false; + + /// + /// 頂点カラーを削除する + /// + [Tooltip("Remove vertex color")] + public bool RemoveVertexColor = false; + + CheckBoxProp m_forceTPose; + CheckBoxProp m_poseFreeze; + CheckBoxProp m_useExcperimentalExporter; + CheckBoxProp m_useSparseAccessor; + CheckBoxProp m_onlyBlendShapePosition; + CheckBoxProp m_reduceBlendShape; + CheckBoxProp m_reduceBlendShapeClip; + CheckBoxProp m_removeVertexColor; + + private void OnEnable() + { + m_forceTPose = new CheckBoxProp(serializedObject.FindProperty(nameof(ForceTPose)), + "エクスポート時に強制的にT-Pose化する。これを使わずに手動でT-Poseを作っても問題ありません"); + m_poseFreeze = new CheckBoxProp(serializedObject.FindProperty(nameof(PoseFreeze)), + "エクスポート時に正規化(ヒエラルキーから回転と拡大縮小を取り除くためにベイク)する"); + m_useExcperimentalExporter = new CheckBoxProp(serializedObject.FindProperty(nameof(UseExperimentalExporter)), + "エクスポート時に新しいJsonSerializerを使う"); + m_useSparseAccessor = new CheckBoxProp(serializedObject.FindProperty(nameof(UseSparseAccessor)), + "BlendShapeの容量を GLTF の Sparse Accessor 機能で削減する。修正中: UniGLTF以外でロードできません"); + m_onlyBlendShapePosition = new CheckBoxProp(serializedObject.FindProperty(nameof(OnlyBlendshapePosition)), + "BlendShapeClipのエクスポートに法線とTangentを含めない。UniVRM-0.53 以前ではロードがエラーになるのに注意してください"); + m_reduceBlendShape = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshape)), + "BlendShapeClipから参照されないBlendShapeをエクスポートに含めない"); + m_reduceBlendShapeClip = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshapeClip)), + "BlendShapeClip.Preset == Unknown のBlendShapeClipをエクスポートに含めない"); + m_removeVertexColor = new CheckBoxProp(serializedObject.FindProperty(nameof(RemoveVertexColor)), + "エクスポートに頂点カラーを含めない"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + m_forceTPose.Draw(); + m_poseFreeze.Draw(); + m_useExcperimentalExporter.Draw(); + m_useSparseAccessor.Draw(); + m_onlyBlendShapePosition.Draw(); + m_reduceBlendShape.Draw(); + m_reduceBlendShapeClip.Draw(); + m_removeVertexColor.Draw(); + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta new file mode 100644 index 0000000000..a731154098 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4dedd0bd9be75140833b413c489c2fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 433062a72d..528de0a535 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -38,6 +38,21 @@ VRMMetaObject Meta } } + bool MetaHasError + { + get + { + if (Meta != null) + { + return Meta.Validate().Any(); + } + else + { + return m_tmpMeta.Validate().Any(); + } + } + } + void UpdateRoot(GameObject root) { if (root == ExportRoot) @@ -45,6 +60,9 @@ void UpdateRoot(GameObject root) return; } ExportRoot = root; + UnityEditor.Editor.DestroyImmediate(m_metaEditor); + m_metaEditor = null; + if (ExportRoot == null) { Meta = null; @@ -227,15 +245,6 @@ public IEnumerable Validate() 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; @@ -258,10 +267,21 @@ void OnEnable() Selection.selectionChanged += OnWizardUpdate; m_tmpMeta = ScriptableObject.CreateInstance(); + + if (m_settings == null) + { + m_settings = ScriptableObject.CreateInstance(); + } + if (m_Inspector == null) + { + m_Inspector = Editor.CreateEditor(m_settings); + } } void OnDisable() { + ExportRoot = null; + // Debug.Log("OnDisable"); Selection.selectionChanged -= OnWizardUpdate; Undo.willFlushUndoRecord -= OnWizardUpdate; @@ -310,7 +330,7 @@ static BeginVerticalScrollViewFunc BeginVerticalScrollView //@TODO: Force repaint if scripts recompile private void OnGUI() { - if(m_tmpMeta==null) + if (m_tmpMeta == null) { // OnDisable return; @@ -342,11 +362,25 @@ private void OnGUI() } if (HasRotationOrScale(ExportRoot)) { - Validation.Warning("回転・拡大縮小を持つノードが含まれています。正規化が必用です").DrawGUI(); + if (m_settings.PoseFreeze) + { + EditorGUILayout.HelpBox("Root OK", MessageType.Info); + } + else + { + Validation.Warning("回転・拡大縮小を持つノードが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください").DrawGUI(); + } } else { - EditorGUILayout.HelpBox("Root OK", MessageType.Info); + if (m_settings.PoseFreeze) + { + Validation.Warning("正規化済みです。Setting の PoseFreeze は不要です").DrawGUI(); + } + else + { + EditorGUILayout.HelpBox("Root OK", MessageType.Info); + } } // @@ -358,6 +392,16 @@ private void OnGUI() Validation.Error("ExportRootに Animator がありません").DrawGUI(); return; } + + 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) + { + Validation.Error("Z+ 向きにしてください").DrawGUI(); + return; + } + var avatar = animator.avatar; if (avatar == null) { @@ -464,34 +508,20 @@ bool DrawWizardGUI() // tabbar _tab = TabBar.OnGUI(_tab, TabButtonStyle, TabButtonSize); - - { - if (m_metaEditor == null) - { - if (Meta != null) - { - m_metaEditor = Editor.CreateEditor(Meta); - } - else - { - m_metaEditor = Editor.CreateEditor(m_tmpMeta); - } - } - } - { - if (m_Inspector == null) - { - if (m_settings == null) - { - m_settings = ScriptableObject.CreateInstance(); - } - m_Inspector = Editor.CreateEditor(m_settings); - } - } - switch (_tab) { case Tabs.Meta: + if (m_metaEditor == null) + { + if (m_meta != null) + { + m_metaEditor = Editor.CreateEditor(Meta); + } + else + { + m_metaEditor = Editor.CreateEditor(m_tmpMeta); + } + } m_metaEditor.OnInspectorGUI(); break; @@ -655,17 +685,8 @@ void OnWizardUpdate() m_validations.Clear(); m_validations.AddRange(Validate()); - // if (Meta != null) - // { - // m_validations.AddRange(Meta.Validate()); - // } - // else - // { - // // m_validations.Add(Validation.Error("meta がありません")); - // } - var hasError = m_validations.Any(x => !x.CanExport); - m_IsValid = !hasError; + m_IsValid = !hasError && !MetaHasError; Repaint(); } From eb7cca8a89f38c68d7b82a2d1d2b3d0dac8c5b57 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 17:53:14 +0900 Subject: [PATCH 12/17] validation --- Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 528de0a535..9820762c08 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -148,10 +148,10 @@ public IEnumerable Validate() 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."); - // } + if (m_settings.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) From 9ed432731606c738cb5c2312eec2ed036974f383 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 17:55:18 +0900 Subject: [PATCH 13/17] =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E9=A0=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs index 027547e558..8d1cac09e5 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs @@ -18,12 +18,11 @@ public CheckBoxProp(SerializedProperty property, string desc) Description = desc; } - public bool Draw() + public void Draw() { + EditorGUILayout.PropertyField(Property); EditorGUILayout.HelpBox(Description, MessageType.None); - var modified = EditorGUILayout.PropertyField(Property); EditorGUILayout.Space(); - return modified; } } From 5f0d547eb1b0f7b8f54186df24f42c86b5583a82 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 18:16:39 +0900 Subject: [PATCH 14/17] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E3=82=B3?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniVRM/Editor/Format/VRMExporterMenu.cs | 20 ------------------- .../UniVRM/Editor/Format/VRMExporterWizard.cs | 8 -------- 2 files changed, 28 deletions(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs index 53564a092e..1403491897 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs @@ -1,6 +1,4 @@ using UnityEditor; -using UnityEngine; - namespace VRM { @@ -8,24 +6,6 @@ public static class VRMExporterMenu { const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid"; - // [MenuItem(CONVERT_HUMANOID_KEY, true, 1)] - // private static bool ExportValidate() - // { - // var root = Selection.activeObject as GameObject; - // if (root == null) - // { - // return false; - // } - - // var animator = root.GetComponent(); - // if (animator == null) - // { - // return false; - // } - - // return true; - // } - [MenuItem(CONVERT_HUMANOID_KEY, false, 1)] private static void ExportFromMenu() { diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 9820762c08..35ff1cc487 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -581,14 +581,6 @@ public static VRMExporterWizard DisplayWizard(string title, Type klass, [uei.Def return wizard; } - // // Magic Methods - - // // This is called when the wizard is opened or whenever the user changes something in the wizard. - // void OnWizardUpdate(); - - // // This is called when the user clicks on the Create button. - // void OnWizardCreate(); - // Allows you to set the create button text of the wizard. public string createButtonName { From be5b7449706e84dd4a585410080f6733b536b813 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 11 Aug 2020 18:21:27 +0900 Subject: [PATCH 15/17] =?UTF-8?q?=E3=83=92=E3=82=A8=E3=83=A9=E3=83=AB?= =?UTF-8?q?=E3=82=AD=E3=83=BC=E3=81=AE=E5=9B=9E=E8=BB=A2=E3=83=BB=E6=8B=A1?= =?UTF-8?q?=E5=A4=A7=E3=81=AE=E6=9C=89=E7=84=A1=E3=81=AB=E3=82=88=E3=82=8A?= =?UTF-8?q?=20PoseFreeze=20=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF?= =?UTF-8?q?=E7=8A=B6=E6=85=8B=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 35ff1cc487..03544e2c7d 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -69,6 +69,9 @@ void UpdateRoot(GameObject root) } else { + // default setting + m_settings.PoseFreeze = HasRotationOrScale(ExportRoot); + var meta = ExportRoot.GetComponent(); if (meta != null) { @@ -105,7 +108,7 @@ public static bool IsFileNameLengthTooLong(string fileName) return fileName.Length > 64; } - static bool HasRotationOrScale(GameObject root) + public static bool HasRotationOrScale(GameObject root) { foreach (var t in root.GetComponentsInChildren()) { @@ -631,6 +634,11 @@ public static void CreateWizard() // update checkbox wiz.UpdateRoot(go); + if (go != null) + { + wiz.m_settings.PoseFreeze = HasRotationOrScale(go); + } + wiz.OnWizardUpdate(); } From 0676e2fa9a999974c4cefdfcf34a9d9dd0333f28 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 12 Aug 2020 16:41:37 +0900 Subject: [PATCH 16/17] =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E3=82=92?= =?UTF-8?q?=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UniVRM/Editor/CustomScriptableWizard.cs | 252 ------------------ .../Editor/CustomScriptableWizard.cs.meta | 11 - .../UniVRM/Editor/Format/VRMExporterMenu.cs | 15 -- .../Editor/Format/VRMExporterMenu.cs.meta | 11 - .../UniVRM/Editor/Format/VRMExporterWizard.cs | 14 +- 5 files changed, 9 insertions(+), 294 deletions(-) delete mode 100644 Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs delete mode 100644 Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta delete mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs delete mode 100644 Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta diff --git a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs deleted file mode 100644 index 83045cc322..0000000000 --- a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs +++ /dev/null @@ -1,252 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Linq; -using System.Reflection; -using UnityEditor; -using UnityEngine; -using uei = UnityEngine.Internal; - -namespace VRM -{ - // Derive from this class to create an editor wizard. - public class CustomScriptableWizard : EditorWindow - { - Editor m_Inspector; - - private string m_HelpString = ""; - private string m_ErrorString = ""; - private bool m_IsValid = true; - private Vector2 m_ScrollPosition; - private string m_CreateButton = "Create"; - private string m_OtherButton = ""; - - private void OnDestroy() - { - UnityEditor.Editor.DestroyImmediate(m_Inspector); - } - - private void InvokeWizardUpdate() - { - const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; - MethodInfo method = GetType().GetMethod("OnWizardUpdate", kInstanceInvokeFlags); - if (method != null) - method.Invoke(this, null); - } - - private class Styles - { - public static string errorText = "Wizard Error"; - public static string box = "Wizard Box"; - } - - public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options); - static BeginVerticalScrollViewFunc s_func; - static BeginVerticalScrollViewFunc BeginVerticalScrollView - { - get - { - if (s_func is null) - { - var methods = typeof(EditorGUILayout).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.Name == "BeginVerticalScrollView").ToArray(); - var method = methods.First(x => x.GetParameters()[1].ParameterType == typeof(bool)); - s_func = (BeginVerticalScrollViewFunc)method.CreateDelegate(typeof(BeginVerticalScrollViewFunc)); - } - return s_func; - } - } - - - //@TODO: Force repaint if scripts recompile - private void OnGUI() - { - EditorGUIUtility.labelWidth = 150; - GUILayout.Label(m_HelpString, EditorStyles.wordWrappedLabel, GUILayout.ExpandHeight(true)); - - // Render contents using Generic Inspector GUI - m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); - GUIUtility.GetControlID(645789, FocusType.Passive); - bool modified = DrawWizardGUI(); - EditorGUILayout.EndScrollView(); - - // Create and Other Buttons - GUILayout.BeginVertical(); - if (m_ErrorString != string.Empty) - GUILayout.Label(m_ErrorString, Styles.errorText, GUILayout.MinHeight(32)); - else - GUILayout.Label(string.Empty, GUILayout.MinHeight(32)); - GUILayout.FlexibleSpace(); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - GUI.enabled = m_IsValid; - - const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; - if (m_OtherButton != "" && GUILayout.Button(m_OtherButton, GUILayout.MinWidth(100))) - { - MethodInfo method = GetType().GetMethod("OnWizardOtherButton", kInstanceInvokeFlags); - if (method != null) - { - method.Invoke(this, null); - GUIUtility.ExitGUI(); - } - else - Debug.LogError("OnWizardOtherButton has not been implemented in script"); - } - - if (m_CreateButton != "" && GUILayout.Button(m_CreateButton, GUILayout.MinWidth(100))) - { - MethodInfo method = GetType().GetMethod("OnWizardCreate", kInstanceInvokeFlags); - if (method != null) - method.Invoke(this, null); - else - Debug.LogError("OnWizardCreate has not been implemented in script"); - Close(); - GUIUtility.ExitGUI(); - } - GUI.enabled = true; - - GUILayout.EndHorizontal(); - GUILayout.EndVertical(); - if (modified) - InvokeWizardUpdate(); - - GUILayout.Space(8); - } - - protected virtual bool DrawWizardGUI() - { - if (m_Inspector == null) - { - m_Inspector = Editor.CreateEditor(this); - } - m_Inspector.OnInspectorGUI(); - return true; - } - - // Creates a wizard. - public static T DisplayWizard(string title) where T : CustomScriptableWizard - { - return DisplayWizard(title, "Create", ""); - } - - ///*listonly* - public static T DisplayWizard(string title, string createButtonName) where T : CustomScriptableWizard - { - return DisplayWizard(title, createButtonName, ""); - } - - ///*listonly* - public static T DisplayWizard(string title, string createButtonName, string otherButtonName) where T : CustomScriptableWizard - { - return (T)DisplayWizard(title, typeof(T), createButtonName, otherButtonName); - } - - [uei.ExcludeFromDocsAttribute] - public static CustomScriptableWizard DisplayWizard(string title, Type klass, string createButtonName) - { - string otherButtonName = ""; - return DisplayWizard(title, klass, createButtonName, otherButtonName); - } - - [uei.ExcludeFromDocsAttribute] - public static CustomScriptableWizard DisplayWizard(string title, Type klass) - { - string otherButtonName = ""; - string createButtonName = "Create"; - return DisplayWizard(title, klass, createButtonName, otherButtonName); - } - - // Creates a wizard. - public static CustomScriptableWizard DisplayWizard(string title, Type klass, [uei.DefaultValueAttribute("\"Create\"")] string createButtonName, [uei.DefaultValueAttribute("\"\"")] string otherButtonName) - { - CustomScriptableWizard wizard = CreateInstance(klass) as CustomScriptableWizard; - wizard.m_CreateButton = createButtonName; - wizard.m_OtherButton = otherButtonName; - wizard.titleContent = new GUIContent(title); - if (wizard != null) - { - wizard.InvokeWizardUpdate(); - wizard.ShowUtility(); - } - return wizard; - } - - // // Magic Methods - - // // This is called when the wizard is opened or whenever the user changes something in the wizard. - // void OnWizardUpdate(); - - // // This is called when the user clicks on the Create button. - // void OnWizardCreate(); - - // Allows you to set the help text of the wizard. - public string helpString - { - get { return m_HelpString; } - set - { - var newString = value ?? string.Empty; - if (m_HelpString != newString) - { - m_HelpString = newString; - Repaint(); - } - } - } - - // Allows you to set the error text of the wizard. - public string errorString - { - get { return m_ErrorString; } - set - { - var newString = value ?? string.Empty; - if (m_ErrorString != newString) - { - m_ErrorString = newString; - Repaint(); - } - } - } - - // Allows you to set the create button text of the wizard. - public string createButtonName - { - get { return m_CreateButton; } - set - { - var newString = value ?? string.Empty; - if (m_CreateButton != newString) - { - m_CreateButton = newString; - Repaint(); - } - } - } - - // Allows you to set the other button text of the wizard. - public string otherButtonName - { - get { return m_OtherButton; } - set - { - var newString = value ?? string.Empty; - if (m_OtherButton != newString) - { - m_OtherButton = newString; - Repaint(); - } - } - } - - // Allows you to enable and disable the wizard create button, so that the user can not click it. - public bool isValid - { - get { return m_IsValid; } - set { m_IsValid = value; } - } - } -} diff --git a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta b/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta deleted file mode 100644 index 45882814d1..0000000000 --- a/Assets/VRM/UniVRM/Editor/CustomScriptableWizard.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c18c8a8a304985b4180418b42e544ac5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs deleted file mode 100644 index 1403491897..0000000000 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UnityEditor; - -namespace VRM -{ - public static class VRMExporterMenu - { - const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid"; - - [MenuItem(CONVERT_HUMANOID_KEY, false, 1)] - private static void ExportFromMenu() - { - VRMExporterWizard.CreateWizard(); - } - } -} diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta b/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta deleted file mode 100644 index 5100b682ae..0000000000 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterMenu.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a1429b9028f33544e94aa367c2acb7fb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 03544e2c7d..24ab2c9903 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -14,13 +14,17 @@ namespace VRM /// public class VRMExporterWizard : EditorWindow { - [SerializeField] - public GameObject ExportRoot; + const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid"; - SerializedProperty m_exportRoot; + [MenuItem(CONVERT_HUMANOID_KEY, false, 1)] + private static void ExportFromMenu() + { + VRMExporterWizard.CreateWizard(); + } + + GameObject ExportRoot; - [SerializeField] - public VRMExportSettings m_settings; + VRMExportSettings m_settings; VRMMetaObject m_meta; VRMMetaObject Meta From 877c40f87f29fdf2b45374ca4e584728af2e9373 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 12 Aug 2020 17:11:00 +0900 Subject: [PATCH 17/17] prefab check --- Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 24ab2c9903..91ae5b9b1c 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -349,6 +349,10 @@ private void OnGUI() var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true); UpdateRoot(root); + // + // ここでも validate している。ここで失敗して return した場合は Export UI を表示しない + // + // // root // @@ -367,6 +371,12 @@ private void OnGUI() Validation.Error("ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください").DrawGUI(); return; } + if (AssetDatabase.GetAssetPath(root) != null) + { + // is prefab + Validation.Error("シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください").DrawGUI(); + return; + } if (HasRotationOrScale(ExportRoot)) { if (m_settings.PoseFreeze) @@ -696,3 +706,4 @@ void OnWizardUpdate() } } } +