summaryrefslogtreecommitdiff
path: root/Assets/ThirdParty/VRM/VRM/UniVRM/Editor
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2020-10-11 20:00:58 +0800
committerchai <chaifix@163.com>2020-10-11 20:00:58 +0800
commit8b384dffa0d9c63c7a657c6e567c2ddefbf046cd (patch)
tree3f4d669b73b6622aa49627c4ccb3c78d51a82bde /Assets/ThirdParty/VRM/VRM/UniVRM/Editor
parentcd3aee8d640f6abcc82802ca7abbcdfa031c20d3 (diff)
+Saionji show off scene
Diffstat (limited to 'Assets/ThirdParty/VRM/VRM/UniVRM/Editor')
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs159
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs63
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs171
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs341
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs99
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs277
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs168
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs309
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs71
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs76
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs179
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs19
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs30
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs88
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs48
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs26
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs311
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs214
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs240
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs226
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs53
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs52
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs151
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs327
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs391
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs80
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs55
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs67
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs144
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs58
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs185
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs29
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs250
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs161
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs170
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs.meta13
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs59
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs.meta3
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone.meta8
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs81
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs104
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/TabBar.cs39
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/TabBar.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests.meta9
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs21
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs48
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs63
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs89
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs.meta11
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef21
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef.meta7
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs687
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs.meta3
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs58
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs58
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs.meta12
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef20
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef.meta7
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs30
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs.meta12
106 files changed, 7283 insertions, 0 deletions
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape.meta
new file mode 100644
index 00000000..7eb84d67
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9e31b30e42902a44f94793be121d2479
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs
new file mode 100644
index 00000000..7338589f
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UniGLTF;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+
+namespace VRM
+{
+ [CustomEditor(typeof(BlendShapeAvatar))]
+ public class BlendShapeAvatarEditor : PreviewEditor
+ {
+ ReorderableList m_clipList;
+
+ BlendShapeClipSelector m_selector;
+
+ SerializedBlendShapeEditor m_clipEditor;
+
+ protected override PreviewSceneManager.BakeValue GetBakeValue()
+ {
+ var clip = m_selector.Selected;
+ var value = new PreviewSceneManager.BakeValue();
+ if (clip != null)
+ {
+ value.BlendShapeBindings = clip.Values;
+ value.MaterialValueBindings = clip.MaterialValues;
+ value.Weight = 1.0f;
+ }
+ return value;
+ }
+
+ void OnSelected(BlendShapeClip clip)
+ {
+ if (PreviewSceneManager == null)
+ {
+ m_clipEditor = null;
+ }
+ else if (clip != null)
+ {
+ m_clipEditor = new SerializedBlendShapeEditor(clip, PreviewSceneManager);
+ PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
+ {
+ BlendShapeBindings = clip.Values,
+ MaterialValueBindings = clip.MaterialValues,
+ Weight = 1.0f
+ });
+ }
+ else
+ {
+ m_clipEditor = null;
+ PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue());
+ }
+ }
+
+ protected override void OnEnable()
+ {
+ m_selector = new BlendShapeClipSelector((BlendShapeAvatar)target, OnSelected);
+
+ var prop = serializedObject.FindProperty("Clips");
+ m_clipList = new ReorderableList(serializedObject, prop);
+
+ m_clipList.drawHeaderCallback = (rect) =>
+ EditorGUI.LabelField(rect, "BlendShapeClips");
+
+ m_clipList.elementHeight = BlendShapeClipDrawer.Height;
+ m_clipList.drawElementCallback = (rect, index, isActive, isFocused) =>
+ {
+ var element = prop.GetArrayElementAtIndex(index);
+ rect.height -= 4;
+ rect.y += 2;
+ EditorGUI.PropertyField(rect, element);
+ };
+
+ m_clipList.onAddCallback += (list) =>
+ {
+ // Add slot
+ prop.arraySize++;
+ // select last item
+ list.index = prop.arraySize - 1;
+ // get last item
+ var element = prop.GetArrayElementAtIndex(list.index);
+ element.objectReferenceValue = null;
+
+ var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(target));
+ var path = EditorUtility.SaveFilePanel(
+ "Create BlendShapeClip",
+ dir,
+ string.Format("BlendShapeClip#{0}.asset", list.count),
+ "asset");
+ if (!string.IsNullOrEmpty(path))
+ {
+ var clip = BlendShapeAvatar.CreateBlendShapeClip(path.ToUnityRelativePath());
+ //clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
+
+ element.objectReferenceValue = clip;
+ }
+ };
+
+ m_clipList.onSelectCallback += (list) =>
+ {
+ var a = list.serializedProperty;
+ var selected = a.GetArrayElementAtIndex(list.index);
+ OnSelected((BlendShapeClip)selected.objectReferenceValue);
+ };
+
+ //m_clipList.onCanRemoveCallback += list => true;
+ base.OnEnable();
+
+ OnSelected(m_selector.Selected);
+ }
+
+ int m_mode;
+ static readonly string[] MODES = new string[]{
+ "Editor",
+ "List"
+ };
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ base.OnInspectorGUI();
+
+ m_mode = GUILayout.Toolbar(m_mode, MODES);
+ switch (m_mode)
+ {
+ case 0:
+ m_selector.SelectGUI();
+ if (m_clipEditor != null)
+ {
+ Separator();
+ var result = m_clipEditor.Draw();
+ if (result.Changed)
+ {
+ PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
+ {
+ BlendShapeBindings = result.BlendShapeBindings,
+ MaterialValueBindings = result.MaterialValueBindings,
+ Weight = 1.0f,
+ });
+ }
+ }
+ break;
+
+ case 1:
+ m_clipList.DoLayoutList();
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs.meta
new file mode 100644
index 00000000..e99155b8
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeAvatarEditor.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 1cde1e2369885f14181eb58009310c92
+timeCreated: 1523199409
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs
new file mode 100644
index 00000000..646c8c4d
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs
@@ -0,0 +1,63 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+
+ [CustomPropertyDrawer(typeof(BlendShapeClip))]
+ public class BlendShapeClipDrawer : PropertyDrawer
+ {
+ //public const int Height = 132;
+
+ //public const int ThumbnailSize = 128;
+
+ public const int Height = 80;
+ public const int ThumbnailSize = 0;
+
+ public override void OnGUI(Rect position,
+ SerializedProperty property, GUIContent label)
+ {
+ using (new EditorGUI.PropertyScope(position, label, property))
+ {
+ //EditorGUIUtility.labelWidth = 80;
+
+ position.height = EditorGUIUtility.singleLineHeight;
+
+ //var halfWidth = position.width * 0.5f;
+
+ var rect = new Rect(position.x + ThumbnailSize, position.y, position.width - ThumbnailSize, position.height);
+ EditorGUI.PropertyField(rect, property);
+
+ var clip = property.objectReferenceValue as BlendShapeClip;
+ if (clip != null)
+ {
+ var clipObj = new SerializedObject(clip);
+ //var thumbnail = clipObj.FindProperty("Thumbnail");
+ var blendShapeName = clipObj.FindProperty("BlendShapeName");
+ var preset = clipObj.FindProperty("Preset");
+ var isBinary = clipObj.FindProperty("IsBinary");
+
+ /*
+ EditorGUI.ObjectField(new Rect(position)
+ {
+ width = ThumbnailSize,
+ height = ThumbnailSize
+ }, thumbnail.objectReferenceValue, typeof(Texture), false);
+ */
+
+ rect.y += (EditorGUIUtility.singleLineHeight + 2);
+ EditorGUI.PropertyField(rect, blendShapeName);
+ rect.y += (EditorGUIUtility.singleLineHeight + 2);
+ EditorGUI.PropertyField(rect, preset);
+ rect.y += (EditorGUIUtility.singleLineHeight + 2);
+ EditorGUI.PropertyField(rect, isBinary);
+
+ clipObj.ApplyModifiedProperties();
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs.meta
new file mode 100644
index 00000000..b5ef0081
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipDrawer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6a31667cfcf461c47b3b384211e2d9ff
+timeCreated: 1541179390
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs
new file mode 100644
index 00000000..4186b68c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs
@@ -0,0 +1,171 @@
+using System;
+using System.IO;
+using UniGLTF;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+
+namespace VRM
+{
+ [CustomEditor(typeof(BlendShapeClip))]
+ public class BlendShapeClipEditor : PreviewEditor
+ {
+ SerializedBlendShapeEditor m_serializedEditor;
+
+ BlendShapeClip m_target;
+ protected override PreviewSceneManager.BakeValue GetBakeValue()
+ {
+ return new PreviewSceneManager.BakeValue
+ {
+ BlendShapeBindings = m_target.Values,
+ MaterialValueBindings = m_target.MaterialValues,
+ Weight = 1.0f,
+ };
+ }
+
+ //SerializedProperty m_thumbnailProp;
+ SerializedProperty m_isBinaryProp;
+
+ protected override GameObject GetPrefab()
+ {
+ return m_target.Prefab;
+ }
+
+ protected override void OnEnable()
+ {
+ m_target = (BlendShapeClip)target;
+
+ base.OnEnable();
+ }
+
+ float m_previewSlider = 1.0f;
+
+ static Texture2D SaveResizedImage(RenderTexture rt, UnityPath path, int size)
+ {
+ var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
+ RenderTexture.active = rt;
+ tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
+ tex.Apply();
+
+ //TextureScale.Scale(tex, size, size);
+ tex = TextureScale.GetResized(tex, size, size);
+
+ byte[] bytes;
+ switch (path.Extension.ToLower())
+ {
+ case ".png":
+ bytes = tex.EncodeToPNG();
+ break;
+
+ case ".jpg":
+ bytes = tex.EncodeToJPG();
+ break;
+
+ default:
+ throw new Exception();
+ }
+
+ if (Application.isPlaying)
+ {
+ UnityEngine.Object.Destroy(tex);
+ }
+ else
+ {
+ UnityEngine.Object.DestroyImmediate(tex);
+ }
+ File.WriteAllBytes(path.FullPath, bytes);
+
+ path.ImportAsset();
+ return path.LoadAsset<Texture2D>();
+ }
+
+ public override void OnInspectorGUI()
+ {
+ if (PreviewSceneManager == null)
+ {
+ return;
+ }
+ serializedObject.Update();
+
+ if (m_serializedEditor == null)
+ {
+ m_serializedEditor = new SerializedBlendShapeEditor(serializedObject, PreviewSceneManager);
+ //m_thumbnailProp = serializedObject.FindProperty("Thumbnail");
+ m_isBinaryProp = serializedObject.FindProperty("IsBinary");
+ }
+
+ EditorGUILayout.BeginHorizontal();
+
+ /*
+ int thumbnailSize = 96;
+ var objectReferenceValue = EditorGUILayout.ObjectField(m_thumbnailProp.objectReferenceValue, typeof(Texture), false,
+ GUILayout.Width(thumbnailSize), GUILayout.Height(thumbnailSize));
+ if (m_thumbnailProp.objectReferenceValue != objectReferenceValue)
+ {
+ m_thumbnailProp.objectReferenceValue = objectReferenceValue;
+ serializedObject.ApplyModifiedProperties();
+ }
+ */
+
+ var changed = false;
+ EditorGUILayout.BeginVertical();
+ base.OnInspectorGUI();
+ EditorGUILayout.LabelField("Preview Weight");
+ var previewSlider = EditorGUILayout.Slider(m_previewSlider, 0, 1.0f);
+ GUI.enabled = PreviewTexture != null;
+ /*
+ if (GUILayout.Button("save thumbnail"))
+ {
+ //var ext = "jpg";
+ var ext = "png";
+ var asset = UnityPath.FromAsset(target);
+ var path = EditorUtility.SaveFilePanel(
+ "save thumbnail",
+ asset.Parent.FullPath,
+ string.Format("{0}.{1}", asset.FileNameWithoutExtension, ext),
+ ext);
+ if (!string.IsNullOrEmpty(path))
+ {
+ var thumbnail = SaveResizedImage(PreviewTexture, UnityPath.FromFullpath(path),
+ BlendShapeClipDrawer.ThumbnailSize);
+ m_thumbnailProp.objectReferenceValue = thumbnail;
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+ */
+ GUI.enabled = true;
+ EditorGUILayout.EndVertical();
+
+ if (m_isBinaryProp.boolValue)
+ {
+ previewSlider = Mathf.Round(previewSlider);
+ }
+ if (previewSlider != m_previewSlider)
+ {
+ m_previewSlider = previewSlider;
+ changed = true;
+ }
+
+ EditorGUILayout.EndHorizontal();
+ Separator();
+ EditorGUILayout.Space();
+
+ var result = m_serializedEditor.Draw();
+ if ((changed || result.Changed) && PreviewSceneManager != null)
+ {
+ PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
+ {
+ BlendShapeBindings = result.BlendShapeBindings,
+ MaterialValueBindings = result.MaterialValueBindings,
+ Weight = m_previewSlider
+ });
+ }
+ }
+
+ public override string GetInfoString()
+ {
+ return BlendShapeKey.CreateFromClip((BlendShapeClip)target).ToString();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs.meta
new file mode 100644
index 00000000..c23db230
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 4b3d6730e24442c44baaa50a1e6f3a6d
+timeCreated: 1522927173
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs
new file mode 100644
index 00000000..a8489894
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs
@@ -0,0 +1,341 @@
+using System;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+ public static class BlendShapeClipEditorHelper
+ {
+ ///
+ /// BlendShape List のElement描画
+ ///
+ public static bool DrawBlendShapeBinding(Rect position, SerializedProperty property,
+ PreviewSceneManager scene)
+ {
+ bool changed = false;
+ if (scene != null)
+ {
+ var height = 16;
+
+ var y = position.y;
+ var rect = new Rect(position.x, y, position.width, height);
+ int pathIndex;
+ if (StringPopup(rect, property.FindPropertyRelative("RelativePath"), scene.SkinnedMeshRendererPathList, out pathIndex))
+ {
+ changed = true;
+ }
+
+ y += height;
+ rect = new Rect(position.x, y, position.width, height);
+ int blendShapeIndex;
+ if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetBlendShapeNames(pathIndex), out blendShapeIndex))
+ {
+ changed = true;
+ }
+
+ y += height;
+ rect = new Rect(position.x, y, position.width, height);
+ if (FloatSlider(rect, property.FindPropertyRelative("Weight"), 100))
+ {
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ ///
+ /// Material List のElement描画
+ ///
+ public static bool DrawMaterialValueBinding(Rect position, SerializedProperty property,
+ PreviewSceneManager scene)
+ {
+ bool changed = false;
+ if (scene != null)
+ {
+ var height = 16;
+
+ var y = position.y;
+ var rect = new Rect(position.x, y, position.width, height);
+ int materialIndex;
+ if (StringPopup(rect, property.FindPropertyRelative("MaterialName"), scene.MaterialNames, out materialIndex))
+ {
+ changed = true;
+ }
+
+ if (materialIndex >= 0)
+ {
+ var materialItem = scene.GetMaterialItem(scene.MaterialNames[materialIndex]);
+ if (materialItem != null)
+ {
+ y += height;
+ rect = new Rect(position.x, y, position.width, height);
+
+ // プロパティ名のポップアップ
+ int propIndex;
+ if (StringPopup(rect, property.FindPropertyRelative("ValueName"), materialItem.PropNames, out propIndex))
+ {
+ changed = true;
+ }
+
+ if (propIndex >= 0)
+ {
+ // 有効なプロパティ名が選択された
+ var propItem = materialItem.PropMap[materialItem.PropNames[propIndex]];
+ {
+ switch (propItem.PropertyType)
+ {
+ case ShaderUtil.ShaderPropertyType.Color:
+ {
+ property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
+
+ // max
+ y += height;
+ rect = new Rect(position.x, y, position.width, height);
+ if (ColorProp(rect, property.FindPropertyRelative("TargetValue")))
+ {
+ changed = true;
+ }
+ }
+ break;
+
+ case ShaderUtil.ShaderPropertyType.TexEnv:
+ {
+ property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
+
+ // max
+ y += height;
+ rect = new Rect(position.x, y, position.width, height);
+ if (OffsetProp(rect, property.FindPropertyRelative("TargetValue")))
+ {
+ changed = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return changed;
+ }
+
+ #region Private
+ static bool StringPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
+ {
+ if (options == null)
+ {
+ newIndex = -1;
+ return false;
+ }
+
+ var oldIndex = Array.IndexOf(options, prop.stringValue);
+ newIndex = EditorGUI.Popup(rect, oldIndex, options);
+ if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
+ {
+ prop.stringValue = options[newIndex];
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static bool IntPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
+ {
+ if (options == null)
+ {
+ newIndex = -1;
+ return false;
+ }
+
+ var oldIndex = prop.intValue;
+ newIndex = EditorGUI.Popup(rect, oldIndex, options);
+ if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
+ {
+ prop.intValue = newIndex;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static bool FloatSlider(Rect rect, SerializedProperty prop, float maxValue)
+ {
+ var oldValue = prop.floatValue;
+ var newValue = EditorGUI.Slider(rect, prop.floatValue, 0, 100f);
+ if (newValue != oldValue)
+ {
+ prop.floatValue = newValue;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static bool ColorProp(Rect rect, SerializedProperty prop)
+ {
+ var oldValue = (Color)prop.vector4Value;
+ var newValue = EditorGUI.ColorField(rect, prop.displayName, oldValue);
+ if (newValue != oldValue)
+ {
+ prop.vector4Value = newValue;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static Rect AdvanceRect(ref float x, float y, float w, float h)
+ {
+ var rect = new Rect(x, y, w, h);
+ x += w;
+ return rect;
+ }
+
+ static float[] v2 = new float[2];
+ static GUIContent[] l2 = new GUIContent[]{
+ new GUIContent("x"),
+ new GUIContent("y")
+ };
+ static Vector4 TilingOffset(Rect rect, string label, Vector4 src)
+ {
+ /*
+ var style = new GUIStyle()
+ {
+ alignment = TextAnchor.MiddleRight,
+ };
+ */
+
+ var quad = (rect.width - 56);
+ var x = rect.x;
+ //EditorGUIUtility.labelWidth = 18;
+
+ EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Tiling");
+ v2[0] = src.x;
+ v2[1] = src.y;
+ EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
+ src.x = v2[0];
+ src.y = v2[1];
+
+ //EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
+ //src.y = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.y);
+
+ rect.y += EditorGUIUtility.singleLineHeight;
+ x = rect.x;
+ EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Offset");
+ v2[0] = src.z;
+ v2[1] = src.w;
+ EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
+ src.z = v2[0];
+ src.w = v2[1];
+
+ //EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad * 2, rect.height), "Offset X", style);
+ //src.z = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "X", src.z);
+
+ //EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
+ //src.w = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.w);
+
+ return src;
+ }
+
+ static bool OffsetProp(Rect rect, SerializedProperty prop)
+ {
+ var oldValue = prop.vector4Value;
+ //var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
+ var newValue = TilingOffset(rect, prop.displayName, oldValue);
+ if (newValue != oldValue)
+ {
+ prop.vector4Value = newValue;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ #endregion
+ }
+
+ /// https://gist.github.com/gszauer/7799899
+ public class TextureScale
+ {
+ private static Color[] texColors;
+ private static Color[] newColors;
+ private static int w;
+ private static float ratioX;
+ private static float ratioY;
+ private static int w2;
+
+ public static void Scale(Texture2D tex, int newWidth, int newHeight)
+ {
+ texColors = tex.GetPixels();
+ newColors = new Color[newWidth * newHeight];
+ ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
+ ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
+ w = tex.width;
+ w2 = newWidth;
+
+ BilinearScale(0, newHeight);
+
+ tex.Resize(newWidth, newHeight);
+ tex.SetPixels(newColors);
+ tex.Apply();
+ }
+
+ private static void BilinearScale(int start, int end)
+ {
+ for (var y = start; y < end; y++)
+ {
+ int yFloor = (int)Mathf.Floor(y * ratioY);
+ var y1 = yFloor * w;
+ var y2 = (yFloor + 1) * w;
+ var yw = y * w2;
+
+ for (var x = 0; x < w2; x++)
+ {
+ int xFloor = (int)Mathf.Floor(x * ratioX);
+ var xLerp = x * ratioX - xFloor;
+ newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
+ ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
+ y * ratioY - yFloor);
+ }
+ }
+ }
+
+ private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
+ {
+ return new Color(c1.r + (c2.r - c1.r) * value,
+ c1.g + (c2.g - c1.g) * value,
+ c1.b + (c2.b - c1.b) * value,
+ c1.a + (c2.a - c1.a) * value);
+ }
+
+ /// http://light11.hatenadiary.com/entry/2018/04/19/194015
+ public static Texture2D GetResized(Texture2D texture, int width, int height)
+ {
+ // リサイズ後のサイズを持つRenderTextureを作成して書き込む
+ var rt = RenderTexture.GetTemporary(width, height);
+ Graphics.Blit(texture, rt);
+
+ // リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む
+ var preRT = RenderTexture.active;
+ RenderTexture.active = rt;
+ var ret = new Texture2D(width, height);
+ ret.ReadPixels(new Rect(0, 0, width, height), 0, 0);
+ ret.Apply();
+ RenderTexture.active = preRT;
+
+ RenderTexture.ReleaseTemporary(rt);
+ return ret;
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs.meta
new file mode 100644
index 00000000..173dc5c1
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipEditorHelper.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: fcb56a7eb1db73c4cb9ea1689635b246
+timeCreated: 1541144400
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs
new file mode 100644
index 00000000..2865ed6a
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Linq;
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+using UniGLTF;
+
+namespace VRM
+{
+ class BlendShapeClipSelector
+ {
+ BlendShapeAvatar m_avatar;
+
+ public BlendShapeClip Selected
+ {
+ get
+ {
+ if (m_avatar == null || m_avatar.Clips == null)
+ {
+ return null;
+ }
+ if (m_selectedIndex < 0 || m_selectedIndex >= m_avatar.Clips.Count)
+ {
+ return null;
+ }
+ return m_avatar.Clips[m_selectedIndex];
+ }
+ }
+
+ int m_selectedIndex;
+ int SelectedIndex
+ {
+ get { return m_selectedIndex; }
+ set
+ {
+ if (m_selectedIndex == value) return;
+ m_selectedIndex = value;
+ if (m_onSelected != null)
+ {
+ m_onSelected(Selected);
+ }
+ }
+ }
+
+ Action<BlendShapeClip> m_onSelected;
+
+ public BlendShapeClipSelector(BlendShapeAvatar avatar, Action<BlendShapeClip> onSelected)
+ {
+ avatar.RemoveNullClip();
+
+ m_avatar = avatar;
+ m_onSelected = onSelected;
+
+ onSelected(Selected);
+ }
+
+ public void SelectGUI()
+ {
+ if (m_avatar != null && m_avatar.Clips != null)
+ {
+ EditorGUILayout.Space();
+ EditorGUILayout.LabelField("Select BlendShapeClip", EditorStyles.boldLabel);
+ var array = m_avatar.Clips
+ .Select(x => x != null
+ ? BlendShapeKey.CreateFromClip(x).ToString()
+ : "null"
+ ).ToArray();
+ SelectedIndex = GUILayout.SelectionGrid(SelectedIndex, array, 4);
+ }
+
+ if (GUILayout.Button("Add BlendShapeClip"))
+ {
+ var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_avatar));
+ var path = EditorUtility.SaveFilePanel(
+ "Create BlendShapeClip",
+ dir,
+ string.Format("BlendShapeClip#{0}.asset", m_avatar.Clips.Count),
+ "asset");
+ if (!string.IsNullOrEmpty(path))
+ {
+ var clip = BlendShapeAvatar.CreateBlendShapeClip(path.ToUnityRelativePath());
+ //clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
+
+ m_avatar.Clips.Add(clip);
+ }
+ }
+ }
+
+ public void DuplicateWarn()
+ {
+ var key = BlendShapeKey.CreateFromClip(Selected);
+ if (m_avatar.Clips.Where(x => key.Match(x)).Count() > 1)
+ {
+ EditorGUILayout.HelpBox("duplicate clip: " + key, MessageType.Error);
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs.meta
new file mode 100644
index 00000000..b5985862
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/BlendShapeClipSelector.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7842ed7a65c676740aa02678316dd625
+timeCreated: 1541138009
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs
new file mode 100644
index 00000000..c12f8d6d
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs
@@ -0,0 +1,277 @@
+using UnityEditor;
+using UnityEngine;
+using UnityEditorInternal;
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace VRM
+{
+ /// <summary>
+ /// Prefabをインスタンス化してPreviewに表示する
+ ///
+ /// * https://github.com/Unity-Technologies/UnityCsReference/blob/11bcfd801fccd2a52b09bb6fd636c1ddcc9f1705/Editor/Mono/Inspector/ModelInspector.cs
+ ///
+ /// </summary>
+ public abstract class PreviewEditor : Editor
+ {
+ /// <summary>
+ /// PreviewRenderUtilityを管理する。
+ ///
+ /// * PreviewRenderUtility.m_cameraのUnityVersionによる切り分け
+ ///
+ /// </summary>
+ PreviewFaceRenderer m_renderer;
+
+ /// <summary>
+ /// Prefabをインスタンス化したシーンを管理する。
+ ///
+ /// * BlendShapeのBake
+ /// * MaterialMorphの適用
+ /// * Previewカメラのコントロール
+ /// * Previewライティングのコントロール
+ ///
+ /// </summary>
+ PreviewSceneManager m_scene;
+ protected PreviewSceneManager PreviewSceneManager
+ {
+ get { return m_scene; }
+ }
+
+ /// <summary>
+ /// Previewシーンに表示するPrefab
+ /// </summary>
+ GameObject m_prefab;
+ protected GameObject Prefab
+ {
+ get { return m_prefab; }
+ private set
+ {
+ if (m_prefab == value) return;
+
+ //Debug.LogFormat("Prefab = {0}", value);
+ m_prefab = value;
+
+ if (m_scene != null)
+ {
+ //Debug.LogFormat("OnDestroy");
+ GameObject.DestroyImmediate(m_scene.gameObject);
+ m_scene = null;
+ }
+
+ if (m_prefab != null)
+ {
+ m_scene = VRM.PreviewSceneManager.GetOrCreate(m_prefab);
+ if (m_scene != null)
+ {
+ m_scene.gameObject.SetActive(false);
+ }
+
+ Bake();
+ }
+ }
+ }
+
+ protected abstract PreviewSceneManager.BakeValue GetBakeValue();
+
+ /// <summary>
+ /// Preview シーンに BlendShape と MaterialValue を適用する
+ /// </summary>
+ protected void Bake()
+ {
+ if (m_scene != null)
+ {
+ //Debug.Log("Bake");
+ m_scene.Bake(GetBakeValue());
+ }
+ }
+
+ protected virtual GameObject GetPrefab()
+ {
+ var assetPath = AssetDatabase.GetAssetPath(target);
+ if (string.IsNullOrEmpty(assetPath))
+ {
+ return null;
+ }
+
+ var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
+ // search prefab if nothing
+ if (prefab == null && 0 < (target as BlendShapeAvatar).Clips.Count)
+ {
+ prefab = (target as BlendShapeAvatar).Clips[0].Prefab;
+ }
+ // once more, with string-based method
+ if (prefab == null)
+ {
+ var parent = UniGLTF.UnityPath.FromUnityPath(assetPath).Parent;
+ var prefabPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".prefab");
+ prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.Value);
+ }
+ return prefab;
+ }
+
+ protected virtual void OnEnable()
+ {
+ m_renderer = new PreviewFaceRenderer();
+
+ Prefab = GetPrefab();
+ }
+
+ protected virtual void OnDisable()
+ {
+ if (m_renderer != null)
+ {
+ m_renderer.Dispose();
+ m_renderer = null;
+ }
+ }
+
+ protected virtual void OnDestroy()
+ {
+ if (m_scene != null)
+ {
+ //Debug.LogFormat("OnDestroy");
+ m_scene.Clean();
+ GameObject.DestroyImmediate(m_scene.gameObject);
+ m_scene = null;
+ }
+ }
+
+ protected static void Separator()
+ {
+ EditorGUILayout.Space();
+ EditorGUILayout.BeginHorizontal();
+ //GUILayout.Space();
+ GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
+ EditorGUILayout.EndHorizontal();
+ EditorGUILayout.Space();
+ }
+
+ public override void OnInspectorGUI()
+ {
+ //base.OnInspectorGUI();
+
+ Prefab = (GameObject)EditorGUILayout.ObjectField("Preview Prefab", Prefab, typeof(GameObject), false);
+
+ //Separator();
+ }
+
+ private static int sliderHash = "Slider".GetHashCode();
+ float m_yaw = 180.0f;
+ float m_pitch;
+ Vector3 m_position = new Vector3(0, 0, -0.8f);
+
+ // very important to override this, it tells Unity to render an ObjectPreview at the bottom of the inspector
+ public override bool HasPreviewGUI() { return true; }
+
+ public RenderTexture PreviewTexture;
+
+ // the main ObjectPreview function... it's called constantly, like other IMGUI On*GUI() functions
+ public override void OnPreviewGUI(Rect r, GUIStyle background)
+ {
+ // if this is happening, you have bigger problems
+ if (!ShaderUtil.hardwareSupportsRectRenderTexture)
+ {
+ if (Event.current.type == EventType.Repaint)
+ {
+ EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
+ "Mesh preview requires\nrender texture support");
+ }
+ return;
+ }
+
+ var src = r;
+
+ var min = Mathf.Min(r.width, r.height);
+ r.width = min;
+ r.height = min;
+ r.x = src.x + (src.width - min) / 2;
+ r.y = src.y + (src.height - min) / 2;
+
+ //previewDir = Drag2D(previewDir, r);
+ {
+ int controlId = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
+ Event e = Event.current;
+ switch (e.GetTypeForControl(controlId))
+ {
+ case EventType.MouseDown:
+ if (r.Contains(e.mousePosition) && (double)r.width > 50.0)
+ {
+ GUIUtility.hotControl = controlId;
+ e.Use();
+ EditorGUIUtility.SetWantsMouseJumping(1);
+ break;
+ }
+ break;
+
+ case EventType.MouseUp:
+ if (GUIUtility.hotControl == controlId)
+ GUIUtility.hotControl = 0;
+ EditorGUIUtility.SetWantsMouseJumping(0);
+ break;
+
+ case EventType.MouseDrag:
+ if (GUIUtility.hotControl == controlId)
+ {
+ if (e.button == 2)
+ {
+ var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height);
+ m_position.x -= shift.x;
+ m_position.y += shift.y;
+ e.Use();
+ GUI.changed = true;
+ }
+ else if (
+ e.button == 0 ||
+ e.button == 1)
+ {
+ var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
+ m_yaw += shift.x;
+ m_pitch += shift.y;
+ m_pitch = Mathf.Clamp(m_pitch, -90f, 90f);
+ e.Use();
+ GUI.changed = true;
+ }
+ break;
+ }
+ break;
+
+ case EventType.ScrollWheel:
+ //Debug.LogFormat("wheel: {0}", current.delta);
+ if (r.Contains(e.mousePosition))
+ {
+ if (e.delta.y > 0)
+ {
+ m_position.z *= 1.1f;
+ Repaint();
+ }
+ else if (e.delta.y < 0)
+ {
+ m_position.z *= 0.9f;
+ Repaint();
+ }
+ }
+ break;
+ }
+ //return scrollPosition;
+ }
+ //Debug.LogFormat("{0}", previewDir);
+
+ if (Event.current.type != EventType.Repaint)
+ {
+ // if we don't need to update yet, then don't
+ return;
+ }
+
+ if (m_renderer != null && m_scene != null)
+ {
+ PreviewTexture = m_renderer.Render(r, background, m_scene, m_yaw, m_pitch, m_position) as RenderTexture;
+ if (PreviewTexture != null)
+ {
+ // draw the RenderTexture in the ObjectPreview pane
+ GUI.DrawTexture(r, PreviewTexture, ScaleMode.StretchToFill, false);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs.meta
new file mode 100644
index 00000000..49dab878
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewEditor.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 3eee31718196e8e41a491f9a4f650ac7
+timeCreated: 1523201293
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs
new file mode 100644
index 00000000..641de550
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs
@@ -0,0 +1,168 @@
+using System;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+ /// <summary>
+ /// based
+ ///
+ /// * https://gist.github.com/radiatoryang/a2282d44ba71848e498bb2e03da98991
+ /// </summary>
+
+ /// <summary>
+ /// PreviewRenderUtilityを管理する
+ /// PreviewSceneをレンダリングする
+ /// </summary>
+ public class PreviewFaceRenderer : IDisposable
+ {
+ PreviewRenderUtility m_previewUtility;
+ public Camera PreviewCamera
+ {
+ get
+ {
+#if UNITY_2017_1_OR_NEWER
+ return m_previewUtility.camera;
+#else
+ return m_previewUtility.m_Camera;
+#endif
+ }
+ }
+
+ public Light[] PreviewLights
+ {
+ get
+ {
+#if UNITY_2017_1_OR_NEWER
+ return m_previewUtility.lights;
+#else
+ return m_previewUtility.m_Light;
+#endif
+ }
+ }
+
+ public void SetAmbientColor(Color color)
+ {
+#if UNITY_2017_1_OR_NEWER
+ m_previewUtility.ambientColor = color;
+#else
+ // ?
+#endif
+ }
+
+ public PreviewFaceRenderer()
+ {
+ m_previewUtility = new PreviewRenderUtility();
+
+ foreach (var light in PreviewLights)
+ {
+ if (light == null) continue;
+ light.intensity = 0f;
+ }
+
+ if (PreviewLights.Length > 0 && PreviewLights[0] != null)
+ {
+ PreviewLights[0].intensity = 1f;
+ PreviewLights[0].transform.rotation = Quaternion.Euler(20f, 200f, 0);
+ PreviewLights[0].color = new Color(1f, 1f, 1f, 1f);
+ }
+
+ SetAmbientColor(new Color(0.1f, 0.1f, 0.1f, 1f));
+ }
+
+ class FogScope : IDisposable
+ {
+ bool fog;
+
+ public FogScope()
+ {
+ fog = RenderSettings.fog; // ... let's remember the current fog setting...
+ // we are technically rendering everything in the scene, so scene fog might affect it...
+ Unsupported.SetRenderSettingsUseFogNoDirty(false); // ... and then temporarily turn it off
+ }
+
+ public void Dispose()
+ {
+ Unsupported.SetRenderSettingsUseFogNoDirty(fog);
+ }
+ }
+
+ //const float FACTOR = 0.1f;
+
+ public Texture Render(Rect r, GUIStyle background, PreviewSceneManager scene,
+ float yaw, float pitch, Vector3 position)
+ {
+ if (scene == null) return null;
+
+ using (var fog = new FogScope())
+ {
+ m_previewUtility.BeginPreview(r, background); // set up the PreviewRenderUtility's mini internal scene
+
+ // setup the ObjectPreview's camera
+ scene.SetupCamera(PreviewCamera, scene.TargetPosition, yaw, pitch, position);
+
+ foreach (var item in scene.EnumRenderItems)
+ {
+ // now, actually render out the RenderTexture
+ //RenderMeshPreview(previewMesh, skinMeshRender.sharedMaterials);
+ // submesh support, in case the mesh is made of multiple parts
+ int subMeshCount = item.Mesh.subMeshCount;
+ for (int i = 0; i < subMeshCount; i++)
+ {
+ m_previewUtility.DrawMesh(item.Mesh,
+ item.Position, item.Rotation,
+ item.Materials[i], i);
+ }
+ }
+
+ // VERY IMPORTANT: this manually tells the camera to render and produce the render texture
+ PreviewCamera.Render();
+ //m_previewUtility.Render(false, false);
+
+ // reset the scene's fog from before
+ return m_previewUtility.EndPreview();
+ }
+ }
+
+ #region IDisposable Support
+ private bool disposedValue = false; // 重複する呼び出しを検出するには
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
+ if (this.m_previewUtility != null)
+ {
+ this.m_previewUtility.Cleanup();
+ this.m_previewUtility = null;
+ }
+ }
+
+ // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
+ // TODO: 大きなフィールドを null に設定します。
+
+ disposedValue = true;
+ }
+ }
+
+ // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
+ // ~PreviewFaceRenderer() {
+ // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
+ // Dispose(false);
+ // }
+
+ // このコードは、破棄可能なパターンを正しく実装できるように追加されました。
+ public void Dispose()
+ {
+ // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
+ Dispose(true);
+ // TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
+ // GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs.meta
new file mode 100644
index 00000000..4c49174e
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/PreviewFaceRenderer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b59705d1618086148b505c2c3d9f1992
+timeCreated: 1522931965
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs
new file mode 100644
index 00000000..17112291
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs
@@ -0,0 +1,309 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+namespace VRM
+{
+ public class SerializedBlendShapeEditor
+ {
+ BlendShapeClip m_targetObject;
+
+ SerializedObject m_serializedObject;
+
+ #region Properties
+ SerializedProperty m_thumbnail;
+ SerializedProperty m_blendShapeNameProp;
+ SerializedProperty m_presetProp;
+
+ SerializedProperty m_isBinaryProp;
+ #endregion
+
+ #region BlendShapeBind
+ public static int BlendShapeBindingHeight = 60;
+ ReorderableList m_ValuesList;
+
+ SerializedProperty m_valuesProp;
+ #endregion
+
+ #region MaterialValueBind
+ const int MaterialValueBindingHeight = 90;
+ ReorderableList m_MaterialValuesList;
+
+ SerializedProperty m_materialsProp;
+ #endregion
+
+ #region Editor values
+ //float m_previewSlider = 1.0f;
+
+ bool m_changed;
+
+ int m_mode;
+ static string[] MODES = new[]{
+ "BlendShape",
+ "BlendShape List",
+ "Material List"
+ };
+
+ MeshPreviewItem[] m_items;
+ #endregion
+
+ public SerializedBlendShapeEditor(SerializedObject serializedObject,
+ PreviewSceneManager previewSceneManager) : this(
+ serializedObject, (BlendShapeClip)serializedObject.targetObject, previewSceneManager)
+ { }
+
+ public SerializedBlendShapeEditor(BlendShapeClip blendShapeClip,
+ PreviewSceneManager previewSceneManager) : this(
+ new SerializedObject(blendShapeClip), blendShapeClip, previewSceneManager)
+ { }
+
+ public SerializedBlendShapeEditor(SerializedObject serializedObject, BlendShapeClip targetObject,
+ PreviewSceneManager previewSceneManager)
+ {
+ this.m_serializedObject = serializedObject;
+ this.m_targetObject = targetObject;
+
+ //m_thumbnail = serializedObject.FindProperty("Thumbnail");
+ m_blendShapeNameProp = serializedObject.FindProperty("BlendShapeName");
+ m_presetProp = serializedObject.FindProperty("Preset");
+ m_isBinaryProp = serializedObject.FindProperty("IsBinary");
+
+ m_valuesProp = serializedObject.FindProperty("Values");
+
+ m_ValuesList = new ReorderableList(serializedObject, m_valuesProp);
+ m_ValuesList.elementHeight = BlendShapeBindingHeight;
+ m_ValuesList.drawElementCallback =
+ (rect, index, isActive, isFocused) =>
+ {
+ var element = m_valuesProp.GetArrayElementAtIndex(index);
+ rect.height -= 4;
+ rect.y += 2;
+ if (BlendShapeClipEditorHelper.DrawBlendShapeBinding(rect, element, previewSceneManager))
+ {
+ m_changed = true;
+ }
+ };
+
+ m_materialsProp = serializedObject.FindProperty("MaterialValues");
+ m_MaterialValuesList = new ReorderableList(serializedObject, m_materialsProp);
+ m_MaterialValuesList.elementHeight = MaterialValueBindingHeight;
+ m_MaterialValuesList.drawElementCallback =
+ (rect, index, isActive, isFocused) =>
+ {
+ var element = m_materialsProp.GetArrayElementAtIndex(index);
+ rect.height -= 4;
+ rect.y += 2;
+ if (BlendShapeClipEditorHelper.DrawMaterialValueBinding(rect, element, previewSceneManager))
+ {
+ m_changed = true;
+ }
+ };
+
+ m_items = previewSceneManager.EnumRenderItems
+ .Where(x => x.SkinnedMeshRenderer != null)
+ .ToArray();
+ }
+
+ public struct DrawResult
+ {
+ public bool Changed;
+
+ public BlendShapeBinding[] BlendShapeBindings;
+
+ public MaterialValueBinding[] MaterialValueBindings;
+ }
+
+ public DrawResult Draw()
+ {
+ m_changed = false;
+
+ m_serializedObject.Update();
+
+ // Readonly のBlendShapeClip参照
+ GUI.enabled = false;
+ EditorGUILayout.ObjectField("Current clip",
+ m_targetObject, typeof(BlendShapeClip), false);
+ GUI.enabled = true;
+
+ EditorGUILayout.PropertyField(m_blendShapeNameProp, true);
+ EditorGUILayout.PropertyField(m_presetProp, true);
+
+ // v0.45 Added. Binary flag
+ EditorGUILayout.PropertyField(m_isBinaryProp, true);
+
+ EditorGUILayout.Space();
+ //m_mode = EditorGUILayout.Popup("SourceType", m_mode, MODES);
+ m_mode = GUILayout.Toolbar(m_mode, MODES);
+ switch (m_mode)
+ {
+ case 0:
+ {
+ ClipGUI();
+ }
+ break;
+
+ case 1:
+ {
+ if (GUILayout.Button("Clear"))
+ {
+ m_changed = true;
+ m_valuesProp.arraySize = 0;
+ }
+ m_ValuesList.DoLayoutList();
+ }
+ break;
+
+ case 2:
+ {
+ if (GUILayout.Button("Clear"))
+ {
+ m_changed = true;
+ m_materialsProp.arraySize = 0;
+ }
+ m_MaterialValuesList.DoLayoutList();
+ }
+ break;
+ }
+
+ m_serializedObject.ApplyModifiedProperties();
+
+ return new DrawResult
+ {
+ Changed = m_changed,
+ BlendShapeBindings = m_targetObject.Values,
+ MaterialValueBindings = m_targetObject.MaterialValues
+ };
+ }
+
+ void ClipGUI()
+ {
+ var changed = BlendShapeBindsGUI();
+ if (changed)
+ {
+ string maxWeightName;
+ var bindings = GetBindings(out maxWeightName);
+ m_valuesProp.ClearArray();
+ m_valuesProp.arraySize = bindings.Length;
+ for (int i = 0; i < bindings.Length; ++i)
+ {
+ var item = m_valuesProp.GetArrayElementAtIndex(i);
+
+ var endProperty = item.GetEndProperty();
+ while (item.NextVisible(true))
+ {
+ if (SerializedProperty.EqualContents(item, endProperty))
+ {
+ break;
+ }
+
+ switch (item.name)
+ {
+ case "RelativePath":
+ item.stringValue = bindings[i].RelativePath;
+ break;
+
+ case "Index":
+ item.intValue = bindings[i].Index;
+ break;
+
+ case "Weight":
+ item.floatValue = bindings[i].Weight;
+ break;
+
+ default:
+ throw new Exception();
+ }
+ }
+ }
+
+ m_changed = true;
+ }
+ }
+
+ List<bool> m_meshFolds = new List<bool>();
+ bool BlendShapeBindsGUI()
+ {
+ bool changed = false;
+ int foldIndex = 0;
+ // すべてのSkinnedMeshRendererを列挙する
+ foreach (var renderer in m_items.Select(x => x.SkinnedMeshRenderer))
+ {
+ var mesh = renderer.sharedMesh;
+ if (mesh != null && mesh.blendShapeCount > 0)
+ {
+ //var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
+ //EditorGUILayout.LabelField(m_target.name + "/" + item.Path);
+
+ if (foldIndex >= m_meshFolds.Count)
+ {
+ m_meshFolds.Add(false);
+ }
+ m_meshFolds[foldIndex] = EditorGUILayout.Foldout(m_meshFolds[foldIndex], renderer.name);
+ if (m_meshFolds[foldIndex])
+ {
+ //EditorGUI.indentLevel += 1;
+ for (int i = 0; i < mesh.blendShapeCount; ++i)
+ {
+ var src = renderer.GetBlendShapeWeight(i);
+ var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
+ if (dst != src)
+ {
+ renderer.SetBlendShapeWeight(i, dst);
+ changed = true;
+ }
+ }
+ //EditorGUI.indentLevel -= 1;
+ }
+ ++foldIndex;
+ }
+ }
+ return changed;
+ }
+
+ BlendShapeBinding[] GetBindings(out string _maxWeightName)
+ {
+ var maxWeight = 0.0f;
+ var maxWeightName = "";
+ // weightのついたblendShapeを集める
+ var values = m_items
+ .SelectMany(x =>
+ {
+ var mesh = x.SkinnedMeshRenderer.sharedMesh;
+
+ var relativePath = x.Path;
+
+ var list = new List<BlendShapeBinding>();
+ if (mesh != null)
+ {
+ for (int i = 0; i < mesh.blendShapeCount; ++i)
+ {
+ var weight = x.SkinnedMeshRenderer.GetBlendShapeWeight(i);
+ if (weight == 0)
+ {
+ continue;
+ }
+ var name = mesh.GetBlendShapeName(i);
+ if (weight > maxWeight)
+ {
+ maxWeightName = name;
+ maxWeight = weight;
+ }
+ list.Add(new BlendShapeBinding
+ {
+ Index = i,
+ RelativePath = relativePath,
+ Weight = weight
+ });
+ }
+ }
+ return list;
+ }).ToArray()
+ ;
+ _maxWeightName = maxWeightName;
+ return values;
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs.meta
new file mode 100644
index 00000000..209e1f5b
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/SerializedBlendShapeClipEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: eaafa3ff7bf991642b922e6af7ecbbc0
+timeCreated: 1541081003
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs
new file mode 100644
index 00000000..743bb5eb
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+using System.Linq;
+
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMBlendShapeProxy))]
+ public class VRMBlendShapeProxyEditor : Editor
+ {
+ VRMBlendShapeProxy m_target;
+ SkinnedMeshRenderer[] m_renderers;
+
+ public class BlendShapeSlider
+ {
+ VRMBlendShapeProxy m_target;
+ BlendShapeKey m_key;
+
+ public BlendShapeSlider(VRMBlendShapeProxy target, BlendShapeKey key)
+ {
+ m_target = target;
+ m_key = key;
+ }
+
+ public KeyValuePair<BlendShapeKey, float> Slider()
+ {
+ var oldValue = m_target.GetValue(m_key);
+ var enable = GUI.enabled;
+ GUI.enabled = Application.isPlaying;
+ var newValue = EditorGUILayout.Slider(m_key.ToString(), oldValue, 0, 1.0f);
+ GUI.enabled = enable;
+ return new KeyValuePair<BlendShapeKey, float>(m_key, newValue);
+ }
+ }
+ List<BlendShapeSlider> m_sliders;
+
+ void OnEnable()
+ {
+ m_target = (VRMBlendShapeProxy)target;
+ if (m_target.BlendShapeAvatar != null && m_target.BlendShapeAvatar.Clips != null)
+ {
+ m_sliders = m_target.BlendShapeAvatar.Clips
+ .Where(x => x != null)
+ .Select(x => new BlendShapeSlider(m_target, BlendShapeKey.CreateFromClip(x)))
+ .ToList()
+ ;
+ }
+ }
+
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+
+ if (!Application.isPlaying)
+ {
+ EditorGUILayout.HelpBox("Enable when playing", MessageType.Info);
+ }
+
+ if (m_target.BlendShapeAvatar == null)
+ {
+ return;
+ }
+
+ if (m_sliders != null)
+ {
+ m_target.SetValues(m_sliders.Select(x => x.Slider()));
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs.meta
new file mode 100644
index 00000000..7d4c2095
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyEditor.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 0f9ca53e9d292ce48a7e6449a03734a9
+timeCreated: 1517819499
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs
new file mode 100644
index 00000000..898aac57
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace VRM
+{
+ public static class VRMBlendShapeProxyValidator
+ {
+ public static IEnumerable<Validation> Validate(this VRMBlendShapeProxy p)
+ {
+ if (p == null)
+ {
+ yield return Validation.Error("VRMBlendShapeProxy is null");
+ yield break;
+ }
+
+ if (p.BlendShapeAvatar == null)
+ {
+ yield return Validation.Error("BlendShapeAvatar is null");
+ yield break;
+ }
+
+ // presetがユニークか
+ var used = new HashSet<BlendShapeKey>();
+ foreach (var c in p.BlendShapeAvatar.Clips)
+ {
+ var key = c.Key;
+ if (used.Contains(key))
+ {
+ yield return Validation.Error($"duplicated BlendShapeKey: {key}");
+ }
+ else
+ {
+ used.Add(key);
+ }
+ }
+
+ var materialNames = new HashSet<string>();
+ foreach (var r in p.GetComponentsInChildren<Renderer>(true))
+ {
+ foreach (var m in r.sharedMaterials)
+ {
+ if (m != null)
+ {
+ if (!materialNames.Contains(m.name))
+ {
+ materialNames.Add(m.name);
+ }
+ }
+ }
+ }
+
+ // 参照が生きているか
+ foreach (var c in p.BlendShapeAvatar.Clips)
+ {
+ for (int i = 0; i < c.Values.Length; ++i)
+ {
+ var v = c.Values[i];
+ var target = p.transform.Find(v.RelativePath);
+ if (target == null)
+ {
+ yield return Validation.Warning($"{c}.Values[{i}].RelativePath({v.RelativePath} is not found");
+ }
+ }
+
+ for (int i = 0; i < c.MaterialValues.Length; ++i)
+ {
+ var v = c.MaterialValues[i];
+ if (!materialNames.Contains(v.MaterialName))
+ {
+ yield return Validation.Warning($"{c}.MaterialValues[{i}].MaterialName({v.MaterialName} is not found");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs.meta
new file mode 100644
index 00000000..c17ae8d8
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/BlendShape/VRMBlendShapeProxyValidator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 50a28b39ccee4874b85d99c095180e5a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs
new file mode 100644
index 00000000..38c8ed01
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+
+namespace VRM.M17N
+{
+ /// <summary>
+ /// 多言語対応
+ /// </summary>
+ public enum Languages
+ {
+ ja,
+ en,
+ }
+
+ [System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
+ public class LangMsgAttribute : System.Attribute
+ {
+ public Languages Language;
+ public string Message;
+
+ public LangMsgAttribute(Languages language, string msg)
+ {
+ Language = language;
+ Message = msg;
+ }
+ }
+
+ public enum VRMExporterWizardMessages
+ {
+ [LangMsg(Languages.ja, "ExportRootをセットしてください")]
+ [LangMsg(Languages.en, "Please set up a ExportRoot for model export")]
+ ROOT_EXISTS,
+
+ [LangMsg(Languages.ja, "ExportRootに親はオブジェクトは持てません")]
+ [LangMsg(Languages.en, "ExportRoot must be topmost parent")]
+ NO_PARENT,
+
+ [LangMsg(Languages.ja, "ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください")]
+ [LangMsg(Languages.en, "ExportRoot's rotation and scaling are not allowed to change. Please set up rotation and scaling in child node")]
+ ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED,
+
+ [LangMsg(Languages.ja, "シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください")]
+ [LangMsg(Languages.en, "Prefab Asset cannot be exported. Prefab Asset has different behaviour with Scene GameObject. Please put the prefab into the scene")]
+ PREFAB_CANNOT_EXPORT,
+
+ [LangMsg(Languages.ja, "回転・拡大縮小を持つノードが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください")]
+ [LangMsg(Languages.en, " Normalization is required. There are nodes (child GameObject) where rotation and scaling are not default. Please enable PoseFreeze")]
+ ROTATION_OR_SCALEING_INCLUDED_IN_NODE,
+
+ [LangMsg(Languages.ja, "正規化済みです。Setting の PoseFreeze は不要です")]
+ [LangMsg(Languages.en, "Normalization has been done. PoseFreeze is not required")]
+ IS_POSE_FREEZE_DONE,
+
+ [LangMsg(Languages.ja, "ExportRootに Animator がありません")]
+ [LangMsg(Languages.en, "No Animator in ExportRoot")]
+ NO_ANIMATOR,
+
+ [LangMsg(Languages.ja, "Z+ 向きにしてください")]
+ [LangMsg(Languages.en, "The model needs to face the positive Z-axis")]
+ FACE_Z_POSITIVE_DIRECTION,
+
+ [LangMsg(Languages.ja, "ExportRootの Animator に Avatar がありません")]
+ [LangMsg(Languages.en, "No Avatar in ExportRoot's Animator")]
+ NO_AVATAR_IN_ANIMATOR,
+
+ [LangMsg(Languages.ja, "ExportRootの Animator.Avatar が不正です")]
+ [LangMsg(Languages.en, "Animator.avatar in ExportRoot is not valid")]
+ AVATAR_IS_NOT_VALID,
+
+ [LangMsg(Languages.ja, "ExportRootの Animator.Avatar がヒューマノイドではありません。FBX importer の Rig で設定してください")]
+ [LangMsg(Languages.en, "Animator.avatar is not humanoid. Please change model's AnimationType to humanoid")]
+ AVATAR_IS_NOT_HUMANOID,
+
+ [LangMsg(Languages.ja, "humanoid設定に顎が含まれている。FBX importer の rig 設定に戻って設定を解除することをおすすめします")]
+ [LangMsg(Languages.en, "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen")]
+ JAW_BONE_IS_INCLUDED,
+
+ [LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする")]
+ [LangMsg(Languages.en, "There are bones with the same name in the hierarchy. They will be automatically renamed after export")]
+ DUPLICATE_BONE_NAME_EXISTS,
+
+ [LangMsg(Languages.ja, "VRMBlendShapeProxyが必要です。先にVRMフォーマットに変換してください")]
+ [LangMsg(Languages.en, "VRMBlendShapeProxy is required. Please convert to VRM format first")]
+ NEEDS_VRM_BLENDSHAPE_PROXY,
+
+ [LangMsg(Languages.en, "This model contains vertex color")]
+ [LangMsg(Languages.ja, "ヒエラルキーに含まれる mesh に頂点カラーが含まれている")]
+ VERTEX_COLOR_IS_INCLUDED,
+
+ [LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていない")]
+ [LangMsg(Languages.en, "No active mesh")]
+ NO_ACTIVE_MESH,
+
+ [LangMsg(Languages.ja, "Standard, Unlit, MToon 以外のマテリアルは、Standard になります")]
+ [LangMsg(Languages.en, "It will export as `Standard` fallback")]
+ UNKNOWN_SHADER,
+
+ [LangMsg(Languages.ja, "名前が長すぎる。リネームしてください: ")]
+ [LangMsg(Languages.en, "FileName is too long: ")]
+ FILENAME_TOO_LONG,
+ }
+
+ static class MsgCache<T> where T : Enum
+ {
+ static Dictionary<Languages, Dictionary<T, string>> s_cache = new Dictionary<Languages, Dictionary<T, string>>();
+
+ static LangMsgAttribute GetAttribute(T value, Languages language)
+ {
+ var t = typeof(T);
+ var memberInfos = t.GetMember(value.ToString());
+ var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == t);
+ var attr = enumValueMemberInfo.GetCustomAttributes(typeof(LangMsgAttribute), false).Select(x => (LangMsgAttribute)x).ToArray();
+ if (attr == null || attr.Length == 0)
+ {
+ return null;
+ }
+ var match = attr.FirstOrDefault(x => x.Language == language);
+ if (match != null)
+ {
+ return match;
+ }
+ return attr.First();
+ }
+
+ public static string Get(Languages language, T key)
+ {
+ if (!s_cache.TryGetValue(language, out Dictionary<T, string> map))
+ {
+ map = new Dictionary<T, string>();
+
+ var t = typeof(T);
+ foreach (T value in Enum.GetValues(t))
+ {
+ var match = GetAttribute(value, language);
+ // Attribute。無かったら enum の ToString
+ map.Add(value, match != null ? match.Message : key.ToString());
+ }
+
+ s_cache.Add(language, map);
+ }
+ return map[key];
+ }
+ }
+ public static class Getter
+ {
+ const string LANG_KEY = "VRM_LANG";
+
+ static Languages? m_lang;
+
+ public static Languages Lang
+ {
+ get
+ {
+ if (!m_lang.HasValue)
+ {
+ m_lang = EnumUtil.TryParseOrDefault<Languages>(EditorPrefs.GetString(LANG_KEY, default(Languages).ToString()));
+ }
+ return m_lang.Value;
+ }
+ }
+
+ public static string Msg<T>(T key) where T : Enum
+ {
+ return M17N.MsgCache<T>.Get(Lang, key);
+ }
+
+ public static void OnGuiSelectLang()
+ {
+ var lang = (M17N.Languages)EditorGUILayout.EnumPopup("lang", Lang);
+ if (lang != Lang)
+ {
+ m_lang = lang;
+ EditorPrefs.SetString(LANG_KEY, M17N.Getter.Lang.ToString());
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs.meta
new file mode 100644
index 00000000..179168ee
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/EditorLanguages.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7829d135d35830f4bb9235eb10b3de1f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs
new file mode 100644
index 00000000..86c7445f
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs
@@ -0,0 +1,19 @@
+using System.Linq;
+using MeshUtility;
+using UnityEngine;
+
+namespace VRM
+{
+ public static class ExporterExtensions
+ {
+ public static bool EnableForExport(this Component mono)
+ {
+ if (mono.transform.Ancestors().Any(x => !x.gameObject.activeSelf))
+ {
+ // 自分か祖先に !activeSelf がいる
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs.meta
new file mode 100644
index 00000000..91082a7c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/ExporterExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 84a3f0f4ae5319a46839f08a76ae0edd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson.meta
new file mode 100644
index 00000000..9a822339
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f5e58f0fdf4d1c9488760d699fa491ef
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs
new file mode 100644
index 00000000..0e2e57cf
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs
@@ -0,0 +1,30 @@
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+ [CustomPropertyDrawer(typeof(VRMFirstPerson.RendererFirstPersonFlags))]
+ public class RendererFirstPersonFlagsDrawer : PropertyDrawer
+ {
+ static Rect LeftSide(Rect position, float width)
+ {
+ return new Rect(position.x, position.y, position.width - width, position.height);
+ }
+ static Rect RightSide(Rect position, float width)
+ {
+ return new Rect(position.x + (position.width - width), position.y, width, position.height);
+ }
+
+ public override void OnGUI(Rect position,
+ SerializedProperty property, GUIContent label)
+ {
+ var rendererProp = property.FindPropertyRelative("Renderer");
+ var flagProp = property.FindPropertyRelative("FirstPersonFlag");
+
+ const float WIDTH = 140.0f;
+ EditorGUI.PropertyField(LeftSide(position, WIDTH), rendererProp, new GUIContent(""), true);
+ EditorGUI.PropertyField(RightSide(position, WIDTH), flagProp, new GUIContent(""), true);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs.meta
new file mode 100644
index 00000000..b75e211c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/RendererFirstPersonFlagsDrawer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ddbc54a3a83438841a744bf50b34ebb9
+timeCreated: 1520848617
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs
new file mode 100644
index 00000000..5d478112
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs
@@ -0,0 +1,88 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMFirstPerson))]
+ class VRMFirstPersonEditor : Editor
+ {
+ VRMFirstPerson m_target;
+
+ void OnEnable()
+ {
+ m_target = target as VRMFirstPerson;
+ }
+
+ void OnDisable()
+ {
+
+ }
+
+ /// <summary>
+ /// SceneView gizmo
+ /// </summary>
+ void OnSceneGUI()
+ {
+ var head = m_target.FirstPersonBone;
+ if (head == null)
+ {
+ return;
+ }
+
+ EditorGUI.BeginChangeCheck();
+
+ var worldOffset = head.localToWorldMatrix.MultiplyPoint(m_target.FirstPersonOffset);
+ worldOffset = Handles.PositionHandle(worldOffset, head.rotation);
+
+ Handles.Label(worldOffset, "FirstPersonOffset");
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(m_target, "Changed FirstPerson");
+
+ m_target.FirstPersonOffset = head.worldToLocalMatrix.MultiplyPoint(worldOffset);
+ }
+ }
+
+ public static void Separator(int indentLevel = 0)
+ {
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Space(indentLevel * 15);
+ GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
+ EditorGUILayout.EndHorizontal();
+ }
+
+ public override void OnInspectorGUI()
+ {
+ VRMFirstPersonValidator.Hierarchy = m_target.transform.GetComponentsInChildren<Transform>(true);
+
+ // show vaildation
+ bool isValid = true;
+ for (int i = 0; i < m_target.Renderers.Count; ++i)
+ {
+ if (VRMFirstPersonValidator.IsValid(m_target.Renderers[i], $"Renderers[{i}]", out Validation v))
+ {
+ continue;
+ }
+ if (isValid)
+ {
+ EditorGUILayout.LabelField("Validation Errors");
+ }
+ v.DrawGUI();
+ isValid = false;
+ }
+ if (!isValid)
+ {
+ if (GUILayout.Button("reset renderers"))
+ {
+ m_target.TraverseRenderers();
+ }
+ GUILayout.Space(10);
+ Separator();
+ GUILayout.Space(10);
+ }
+
+ base.OnInspectorGUI();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs.meta
new file mode 100644
index 00000000..3af9542c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e68be7adce7f09d4287af62a2bac63d7
+timeCreated: 1545891764
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs
new file mode 100644
index 00000000..f7cd0a80
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace VRM
+{
+ public static class VRMFirstPersonValidator
+ {
+ public static Transform[] Hierarchy;
+
+ public static bool IsValid(this VRMFirstPerson.RendererFirstPersonFlags r, string name, out Validation validation)
+ {
+ if (r.Renderer == null)
+ {
+ validation = Validation.Error($"{name}.Renderer is null");
+ return false;
+ }
+
+ if (!Hierarchy.Contains(r.Renderer.transform))
+ {
+ validation = Validation.Error($"{name}.Renderer is out of hierarchy");
+ return false;
+ }
+
+ if (!r.Renderer.EnableForExport())
+ {
+ validation = Validation.Error($"{name}.Renderer is not active");
+ return false;
+ }
+
+ validation = default;
+ return true;
+ }
+
+ public static IEnumerable<Validation> Validate(this VRMFirstPerson self)
+ {
+ Hierarchy = self.GetComponentsInChildren<Transform>(true);
+
+ for (int i = 0; i < self.Renderers.Count; ++i)
+ {
+ if (!IsValid(self.Renderers[i], $"[VRMFirstPerson]{self.name}.Renderers[{i}]", out Validation v))
+ {
+ yield return v;
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs.meta
new file mode 100644
index 00000000..1c881b70
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/FirstPerson/VRMFirstPersonValidator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 48854dc6374b64d45a8a5e099bf5ced0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format.meta
new file mode 100644
index 00000000..bfe0a6a6
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 127df51bf554ee64cbe91b05e14a9266
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs
new file mode 100644
index 00000000..cc3f0557
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs
@@ -0,0 +1,26 @@
+using System;
+using UnityEditor;
+
+namespace VRM
+{
+ /// <summary>
+ /// UndoをGroupを開始して、DisposeでUndoする。
+ /// using で使うのを想定。
+ /// using ブロック内で Undo されるべき操作をする。
+ /// </summary>
+ public struct RecordDisposer : IDisposable
+ {
+ int _group;
+ public RecordDisposer(UnityEngine.Object[] objects, string msg)
+ {
+ Undo.IncrementCurrentGroup();
+ _group = Undo.GetCurrentGroup();
+ Undo.RecordObjects(objects, msg);
+ }
+
+ public void Dispose()
+ {
+ Undo.RevertAllDownToGroup(_group);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta
new file mode 100644
index 00000000..5cf6bf1e
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/RecordDisposer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b304ed2aeece5a54191a5a8b69d9d113
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs
new file mode 100644
index 00000000..3e2b64f7
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs
@@ -0,0 +1,311 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using UniGLTF;
+using UniJSON;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+ public static class VRMAOTMenu
+ {
+ /// <summary>
+ /// AOT向けにダミーのGenerics呼び出しを作成する
+ /// </summary>
+#if VRM_DEVELOP
+ [MenuItem(VRMVersion.MENU + "/GenerateAOTCall")]
+#endif
+ static void GenerateAOTCall()
+ {
+ var path = UnityPath.FromUnityPath("Assets/VRM/UniVRM/Scripts/AOTCall.g.cs");
+ var encoding = new UTF8Encoding(false);
+ using (var s = new MemoryStream())
+ {
+ using (var w = new StreamWriter(s, encoding))
+ {
+ w.WriteLine(@"
+using System;
+using UniJSON;
+using UniGLTF;
+using System.Collections.Generic;
+
+
+namespace VRM {
+ public static partial class VRMAOTCall {
+ static void glTF()
+ {
+ {
+ var f = new JsonFormatter();
+");
+
+ {
+ var excludes = new List<Type>
+ {
+ typeof(object),
+ };
+
+ foreach (var t in new Type[]
+ {
+ typeof(string),
+ typeof(bool),
+
+ typeof(byte),
+ typeof(ushort),
+ typeof(uint),
+ typeof(ulong),
+
+ typeof(sbyte),
+ typeof(short),
+ typeof(int),
+ typeof(long),
+
+ typeof(float),
+ typeof(double),
+ typeof(Vector2),
+ typeof(Vector3),
+ typeof(Vector4),
+ typeof(Quaternion),
+ typeof(glTF),
+ })
+ {
+ TraverseType("JsonValue", w, t, excludes);
+ }
+ }
+
+ w.WriteLine(@"}
+
+{
+ var f = new MsgPackFormatter();
+");
+
+ {
+ var excludes = new List<Type>
+ {
+ typeof(object),
+ };
+
+ foreach (var t in new Type[]
+ {
+ typeof(string),
+ typeof(bool),
+
+ typeof(byte),
+ typeof(ushort),
+ typeof(uint),
+ typeof(ulong),
+
+ typeof(sbyte),
+ typeof(short),
+ typeof(int),
+ typeof(long),
+
+ typeof(float),
+ typeof(double),
+ typeof(Vector2),
+ typeof(Vector3),
+ typeof(Vector4),
+ typeof(Quaternion),
+ })
+ {
+ TraverseType("MsgPackValue", w, t, excludes);
+ }
+ }
+
+ w.WriteLine(@"
+ }
+ }
+ }
+}
+");
+ }
+
+ var text = encoding.GetString(s.ToArray());
+ File.WriteAllText(path.FullPath, text.Replace("\r\n", "\n"), encoding);
+ }
+
+ path.ImportAsset();
+ }
+
+ static bool IsGenericList(Type t)
+ {
+ if (t.IsGenericType
+ && t.GetGenericTypeDefinition() == typeof(List<>))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static bool IsGenericDictionary(Type t)
+ {
+ if (t.IsGenericType
+ && t.GetGenericTypeDefinition() == typeof(Dictionary<,>)
+ && t.GetGenericArguments()[0] == typeof(string))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static IEnumerable<Type> GetNestedTypes(Type t)
+ {
+ if (t.DeclaringType == null)
+ {
+ yield break;
+ }
+
+ foreach(var x in GetNestedTypes(t.DeclaringType))
+ {
+ yield return x;
+ }
+
+ yield return t.DeclaringType;
+ }
+
+ static string GenericTypeName(Type t)
+ {
+ if (!t.IsGenericType)
+ {
+ return t.Name;
+ }
+ else
+ {
+ return t.Name.Split('`')[0]
+ + "<"
+ + string.Join(",", t.GetGenericArguments().Select(x => GenericTypeName(x)).ToArray())
+ + ">"
+ ;
+ }
+ }
+
+ static string GetTypeName(Type t)
+ {
+ var sb = new StringBuilder();
+ if (!string.IsNullOrEmpty(t.Name))
+ {
+ sb.Append(t.Namespace);
+ sb.Append(".");
+ }
+
+ foreach(var x in GetNestedTypes(t))
+ {
+ sb.Append(x.Name);
+ sb.Append(".");
+ }
+
+ sb.Append(GenericTypeName(t));
+
+ return sb.ToString();
+ }
+
+ static void TraverseType(string value, TextWriter w, Type t, List<Type> excludes)
+ {
+ if (excludes.Contains(t))
+ {
+ return;
+ }
+
+ w.WriteLine();
+ w.WriteLine("// $0".Replace("$0", t.Name));
+ excludes.Add(t);
+
+ if (t.IsArray)
+ {
+ var valueType = t.GetElementType();
+ w.WriteLine("f.Serialize(default($0[]));".Replace("$0", valueType.Name));
+ w.WriteLine(@"{
+var value = default($0[]);
+default(ListTreeNode<$2>).Deserialize(ref value);
+GenericDeserializer<$2, $0[]>.GenericArrayDeserializer<$0>(default(ListTreeNode<$2>));
+}"
+.Replace("$0", valueType.Name)
+.Replace("$2", value)
+);
+
+ return;
+ }
+
+ {
+ // list
+ if (IsGenericList(t))
+ {
+ var name = GetTypeName(t.GetGenericArguments()[0]);
+ w.WriteLine("f.Serialize(default(List<$0>));".Replace("$0", name));
+ w.WriteLine(@"{
+var value = default(List<$0>);
+default(ListTreeNode<$2>).Deserialize(ref value);
+GenericDeserializer<$2, List<$0>>.GenericListDeserializer<$0>(default(ListTreeNode<$2>));
+}"
+.Replace("$0", name)
+.Replace("$2", value)
+);
+
+ TraverseType(value, w, t.GetGenericArguments()[0], excludes);
+
+ return;
+ }
+ }
+
+ {
+ // dict
+ if (IsGenericDictionary(t))
+ {
+ var name = GetTypeName(t.GetGenericArguments()[1]);
+ w.WriteLine("f.Serialize(default(Dictionary<string, $0>));".Replace("$0", name));
+ w.WriteLine(@"{
+var value = default(Dictionary<string, $0>);
+default(ListTreeNode<$2>).Deserialize(ref value);
+GenericDeserializer<$2, Dictionary<string, $0>>.DictionaryDeserializer<$0>(default(ListTreeNode<$2>));
+}"
+.Replace("$0", name)
+.Replace("$2", value)
+);
+
+ TraverseType(value, w, t.GetGenericArguments()[1], excludes);
+ return;
+ }
+ }
+
+ {
+ var name = GetTypeName(t);
+ w.WriteLine("f.Serialize(default($0));".Replace("$0", name));
+ w.WriteLine(@"{
+var value = default($0);
+default(ListTreeNode<$2>).Deserialize(ref value);
+}"
+.Replace("$0", name)
+.Replace("$2", value)
+);
+ }
+
+ // object
+ //if (t.IsClass)
+ {
+ foreach (var fi in t.GetFields(BindingFlags.Public | BindingFlags.Instance))
+ {
+ var fieldTypeName = GetTypeName(fi.FieldType);
+ w.WriteLine(@"{
+JsonObjectValidator.GenericDeserializer<$2,$0>.DeserializeField<$1>(default(JsonSchema), default(ListTreeNode<$2>));
+}"
+.Replace("$0", GetTypeName(t))
+.Replace("$1", GetTypeName(fi.FieldType))
+.Replace("$2", value)
+);
+
+ TraverseType(value, w, fi.FieldType, excludes);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs.meta
new file mode 100644
index 00000000..1fd39a6d
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAOTMenu.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 400503f201487874f93b0efdc892cbf3
+timeCreated: 1550040672
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs
new file mode 100644
index 00000000..c1a359a0
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs
@@ -0,0 +1,214 @@
+#if false
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UniGLTF;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+ public static class VRMAssetWriter
+ {
+ interface ISubAssetWriter
+ {
+ void WriteIfHas(GameObject go);
+ }
+
+ class SubAssetWriter<T>: ISubAssetWriter where T : UnityEngine.Object
+ {
+ HashSet<T> m_set = new HashSet<T>();
+
+ String m_assetPath;
+ public delegate IEnumerable<T> GetterFunc(GameObject go);
+ GetterFunc m_getter;
+ public SubAssetWriter(string assetPath, GetterFunc getter)
+ {
+ m_assetPath = assetPath;
+ m_getter = getter;
+ }
+
+ public void WriteIfHas(GameObject go)
+ {
+ foreach(var o in m_getter(go))
+ {
+ if (!m_set.Contains(o))
+ {
+ AssetDatabase.AddObjectToAsset(o, m_assetPath);
+ m_set.Add(o);
+ }
+ }
+ }
+ }
+
+ static IEnumerable<Mesh> GetMeshs(GameObject go)
+ {
+ var skinnedMesh = go.GetComponent<SkinnedMeshRenderer>();
+ if (skinnedMesh != null)
+ {
+ yield return skinnedMesh.sharedMesh;
+ }
+
+ var filter = go.GetComponent<MeshFilter>();
+ if (filter != null)
+ {
+ yield return filter.sharedMesh;
+ }
+ }
+
+ static IEnumerable<Material> GetMaterials(GameObject go)
+ {
+ var renderer = go.GetComponent<Renderer>();
+ if (renderer != null)
+ {
+ return renderer.sharedMaterials;
+ }
+ else
+ {
+ return Enumerable.Empty<Material>();
+ }
+ }
+
+ static IEnumerable<Texture2D> GetTextures(GameObject go)
+ {
+ foreach (var m in GetMaterials(go))
+ {
+ foreach(Texture2D x in m.GetTextures())
+ {
+ if (x != null)
+ {
+ yield return x;
+ }
+ }
+ }
+ }
+
+ static IEnumerable<Avatar> GetAvatars(GameObject go)
+ {
+ var animator = go.GetComponent<Animator>();
+ if(animator!=null && animator.avatar != null)
+ {
+ yield return animator.avatar;
+ }
+ }
+
+ static IEnumerable<BlendShapeClip> GetBlendShapeClips(GameObject go)
+ {
+ var proxy = go.GetComponent<VRMBlendShapeProxy>();
+ if (proxy != null && proxy.BlendShapeAvatar != null)
+ {
+ return proxy.BlendShapeAvatar.Clips;
+ }
+ else
+ {
+ return Enumerable.Empty<BlendShapeClip>();
+ }
+ }
+
+ static IEnumerable<BlendShapeAvatar> GetBlendShapeAvatars(GameObject go)
+ {
+ var proxy = go.GetComponent<VRMBlendShapeProxy>();
+ if (proxy != null && proxy.BlendShapeAvatar != null)
+ {
+ yield return proxy.BlendShapeAvatar;
+ }
+ }
+
+ static IEnumerable<UniHumanoid.AvatarDescription> GetAvatarDecriptions(GameObject go)
+ {
+ var humanoid = go.GetComponent<VRMHumanoidDescription>();
+ if (humanoid!=null && humanoid.Description != null)
+ {
+ yield return humanoid.Description;
+ }
+ else
+ {
+ var animator = go.GetComponent<Animator>();
+ if(animator!=null && animator.avatar)
+ {
+ var description= UniHumanoid.AvatarDescription.CreateFrom(animator.avatar);
+ if (description != null)
+ {
+ description.name = "AvatarDescription";
+ yield return description;
+ }
+ }
+ }
+ }
+
+ static IEnumerable<Texture2D> GetThumbnails(GameObject go)
+ {
+ var meta = go.GetComponent<VRMMetaInformation>();
+ if (meta != null && meta.Thumbnail != null)
+ {
+ yield return meta.Thumbnail;
+ }
+ }
+
+ static IEnumerable<UnityEngine.Object> GetSubAssets(String prefabPath)
+ {
+ return AssetDatabase.LoadAllAssetsAtPath(prefabPath);
+ }
+
+ public static void SaveAsPrefab(GameObject root, String path)
+ {
+ var prefabPath = path.ToUnityRelativePath();
+ Debug.LogFormat("SaveAsPrefab: {0}", prefabPath);
+
+ // clear subassets
+ if (File.Exists(prefabPath))
+ {
+ //Debug.LogFormat("Exist: {0}", m_prefabPath);
+
+ // clear subassets
+ foreach (var x in GetSubAssets(prefabPath))
+ {
+ if (x is Transform
+ || x is GameObject)
+ {
+ continue;
+ }
+ GameObject.DestroyImmediate(x, true);
+ }
+ }
+
+ // add subassets
+ var writers = new ISubAssetWriter[]{
+ new SubAssetWriter<Texture2D>(prefabPath, GetTextures),
+ new SubAssetWriter<Material>(prefabPath, GetMaterials),
+ new SubAssetWriter<Mesh>(prefabPath, GetMeshs),
+ new SubAssetWriter<Avatar>(prefabPath, GetAvatars),
+ // VRM Objects
+ new SubAssetWriter<BlendShapeClip>(prefabPath, GetBlendShapeClips),
+ new SubAssetWriter<BlendShapeAvatar>(prefabPath, GetBlendShapeAvatars),
+ new SubAssetWriter<UniHumanoid.AvatarDescription>(prefabPath, GetAvatarDecriptions),
+ new SubAssetWriter<Texture2D>(prefabPath, GetThumbnails),
+ };
+ foreach (var x in root.transform.Traverse())
+ {
+ foreach (var writer in writers)
+ {
+ writer.WriteIfHas(x.gameObject);
+ }
+ }
+
+ ///
+ /// create prefab, after subasset AssetDatabase.AddObjectToAsset
+ ///
+ if (File.Exists(prefabPath))
+ {
+ //Debug.LogFormat("ReplacePrefab: {0}", m_prefabPath);
+ var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
+ PrefabUtility.ReplacePrefab(root, prefab, ReplacePrefabOptions.ConnectToPrefab);
+ }
+ else
+ {
+ //Debug.LogFormat("CreatePrefab: {0}", m_prefabPath);
+ PrefabUtility.CreatePrefab(prefabPath, root, ReplacePrefabOptions.ConnectToPrefab);
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs.meta
new file mode 100644
index 00000000..264ff80e
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMAssetWriter.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 8b409aa5d363b9948995cc00f7f1a0d0
+timeCreated: 1520241647
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs
new file mode 100644
index 00000000..a3726090
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs
@@ -0,0 +1,240 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using UniGLTF;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ public static class VRMEditorExporter
+ {
+ /// <summary>
+ /// Editor向けのエクスポート処理
+ /// </summary>
+ /// <param name="path">出力先</param>
+ /// <param name="settings">エクスポート設定</param>
+ public static void Export(string path, GameObject exportRoot, VRMMetaObject meta, VRMExportSettings settings, IReadOnlyList<MeshExportInfo> info)
+ {
+ List<GameObject> destroy = new List<GameObject>();
+ try
+ {
+ Export(path, exportRoot, meta, settings, info, destroy);
+ }
+ finally
+ {
+ foreach (var x in destroy)
+ {
+ Debug.LogFormat("destroy: {0}", x.name);
+ GameObject.DestroyImmediate(x);
+ }
+ }
+ }
+
+ static bool IsPrefab(GameObject go)
+ {
+ return !go.scene.IsValid();
+ }
+
+ /// <summary>
+ /// DeepCopy
+ /// </summary>
+ /// <param name="src"></param>
+ /// <returns></returns>
+ static BlendShapeAvatar CopyBlendShapeAvatar(BlendShapeAvatar src, bool removeUnknown)
+ {
+ var avatar = GameObject.Instantiate(src);
+ avatar.Clips = new List<BlendShapeClip>();
+ foreach (var clip in src.Clips)
+ {
+ if (removeUnknown && clip.Preset == BlendShapePreset.Unknown)
+ {
+ continue;
+ }
+ avatar.Clips.Add(GameObject.Instantiate(clip));
+ }
+ return avatar;
+ }
+
+ /// <summary>
+ /// 使用されない BlendShape を間引いた Mesh を作成して置き換える
+ /// </summary>
+ /// <param name="mesh"></param>
+ /// <returns></returns>
+ static void ReplaceMesh(GameObject target, SkinnedMeshRenderer smr, BlendShapeAvatar copyBlendShapeAvatar)
+ {
+ Mesh mesh = smr.sharedMesh;
+ if (mesh == null) return;
+ if (mesh.blendShapeCount == 0) return;
+
+ // Mesh から BlendShapeClip からの参照がある blendShape の index を集める
+ var usedBlendshapeIndexArray = copyBlendShapeAvatar.Clips
+ .SelectMany(clip => clip.Values)
+ .Where(val => target.transform.Find(val.RelativePath) == smr.transform)
+ .Select(val => val.Index)
+ .Distinct()
+ .ToArray();
+
+ var copyMesh = MeshUtility.MeshExtensions.Copy(mesh, copyBlendShape: false);
+ // 使われている BlendShape だけをコピーする
+ foreach (var i in usedBlendshapeIndexArray)
+ {
+ var name = mesh.GetBlendShapeName(i);
+ var vCount = mesh.vertexCount;
+ var vertices = new Vector3[vCount];
+ var normals = new Vector3[vCount];
+ var tangents = new Vector3[vCount];
+ mesh.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents);
+
+ copyMesh.AddBlendShapeFrame(name, 100f, vertices, normals, tangents);
+ }
+
+ // BlendShapeClip の BlendShapeIndex を更新する(前に詰める)
+ var indexMapper = usedBlendshapeIndexArray
+ .Select((x, i) => new { x, i })
+ .ToDictionary(pair => pair.x, pair => pair.i);
+ foreach (var clip in copyBlendShapeAvatar.Clips)
+ {
+ for (var i = 0; i < clip.Values.Length; ++i)
+ {
+ var value = clip.Values[i];
+ if (target.transform.Find(value.RelativePath) != smr.transform) continue;
+ value.Index = indexMapper[value.Index];
+ clip.Values[i] = value;
+ }
+ }
+
+ // mesh を置き換える
+ smr.sharedMesh = copyMesh;
+ }
+
+ static void ForceUniqueName(Transform transform, Dictionary<string, int> nameCount)
+ {
+ for (int i = 2; i < 5000; ++i)
+ {
+ var sb = new StringBuilder();
+ sb.Append(transform.name);
+ sb.Append('_');
+ sb.Append(i);
+ var newName = sb.ToString();
+ if (!nameCount.ContainsKey(newName))
+ {
+ Debug.LogWarningFormat("force rename {0} => {1}", transform.name, newName);
+ transform.name = newName;
+ nameCount.Add(newName, 1);
+ return;
+ }
+ }
+ throw new Exception("?");
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="path"></param>
+ /// <param name="settings"></param>
+ /// <param name="destroy">作業が終わったらDestoryするべき一時オブジェクト</param>
+ static void Export(string path, GameObject exportRoot, VRMMetaObject meta,
+ VRMExportSettings settings, IReadOnlyList<UniGLTF.MeshExportInfo> info,
+ List<GameObject> destroy)
+ {
+ var target = exportRoot;
+
+ // 常にコピーする。シーンを変化させない
+ target = GameObject.Instantiate(target);
+ destroy.Add(target);
+
+ var metaBehaviour = target.GetComponent<VRMMeta>();
+ if (metaBehaviour == null)
+ {
+ metaBehaviour = target.AddComponent<VRMMeta>();
+ metaBehaviour.Meta = meta;
+ }
+ if (metaBehaviour.Meta == null)
+ {
+ // 来ないはず
+ throw new Exception("meta required");
+ }
+
+ {
+ // copy元
+ var animator = exportRoot.GetComponent<Animator>();
+ var beforeTransforms = exportRoot.GetComponentsInChildren<Transform>();
+ // copy先
+ var afterTransforms = target.GetComponentsInChildren<Transform>();
+ // copy先のhumanoidBoneのリストを得る
+ var bones = (HumanBodyBones[])Enum.GetValues(typeof(HumanBodyBones));
+ var humanTransforms = bones
+ .Where(x => x != HumanBodyBones.LastBone)
+ .Select(x => animator.GetBoneTransform(x))
+ .Where(x => x != null)
+ .Select(x => afterTransforms[Array.IndexOf(beforeTransforms, x)]) // copy 先を得る
+ .ToArray();
+
+ var nameCount = target.GetComponentsInChildren<Transform>()
+ .GroupBy(x => x.name)
+ .ToDictionary(x => x.Key, x => x.Count());
+ foreach (var t in target.GetComponentsInChildren<Transform>())
+ {
+ if (humanTransforms.Contains(t))
+ {
+ // keep original name
+ continue;
+ }
+
+ if (nameCount[t.name] > 1)
+ {
+ // 重複するボーン名をリネームする
+ ForceUniqueName(t, nameCount);
+ }
+ }
+ }
+
+ // 正規化
+ if (settings.PoseFreeze)
+ {
+ // BoneNormalizer.Execute は Copy を作って正規化する。UNDO無用
+ target = VRMBoneNormalizer.Execute(target, settings.ForceTPose, false);
+ destroy.Add(target);
+ }
+
+ // 元のBlendShapeClipに変更を加えないように複製
+ var proxy = target.GetComponent<VRMBlendShapeProxy>();
+ if (proxy != null)
+ {
+ var copyBlendShapeAvatar = CopyBlendShapeAvatar(proxy.BlendShapeAvatar, settings.ReduceBlendshapeClip);
+ proxy.BlendShapeAvatar = copyBlendShapeAvatar;
+
+ // BlendShape削減
+ if (settings.ReduceBlendshape)
+ {
+ foreach (SkinnedMeshRenderer smr in target.GetComponentsInChildren<SkinnedMeshRenderer>())
+ {
+ // 未使用のBlendShapeを間引く
+ ReplaceMesh(target, smr, copyBlendShapeAvatar);
+ }
+ }
+ }
+
+ // 出力
+ var sw = System.Diagnostics.Stopwatch.StartNew();
+ var gltf = new UniGLTF.glTF();
+ using (var exporter = new VRMExporter(gltf))
+ {
+ exporter.Prepare(target);
+ exporter.Export(settings.MeshExportSettings);
+ }
+ var bytes = gltf.ToGlbBytes();
+ File.WriteAllBytes(path, bytes);
+ Debug.LogFormat("Export elapsed {0}", sw.Elapsed);
+
+ if (path.StartsWithUnityAssetPath())
+ {
+ // 出力ファイルのインポートを発動
+ AssetDatabase.ImportAsset(path.ToUnityRelativePath());
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta
new file mode 100644
index 00000000..f6863a95
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMEditorExporter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cea266830a7f57843bb928d0ea37bcbc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs
new file mode 100644
index 00000000..00a4dc74
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace VRM
+{
+ /// <summary>
+ /// Export時にMeshを一覧する。
+ ///
+ /// Mesh関連の Validation する。
+ /// Meshのエクスポートサイズを試算する。
+ /// </summary>
+ [Serializable]
+ public class VRMExportMeshes : ScriptableObject
+ {
+ static Mesh GetMesh(Renderer r)
+ {
+ if (r is SkinnedMeshRenderer smr)
+ {
+ return smr.sharedMesh;
+ }
+ if (r is MeshRenderer)
+ {
+ MeshFilter f = r.GetComponent<MeshFilter>();
+ if (f != null)
+ {
+ return f.sharedMesh;
+ }
+ }
+ return null;
+ }
+
+ static bool ClipsContainsName(IReadOnlyList<BlendShapeClip> clips, bool onlyPreset, BlendShapeBinding binding)
+ {
+ foreach (var c in clips)
+ {
+ if (onlyPreset)
+ {
+ if (c.Preset == BlendShapePreset.Unknown)
+ {
+ continue;
+ }
+ }
+
+ foreach (var b in c.Values)
+ {
+ if (b.RelativePath == binding.RelativePath && b.Index == binding.Index)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public List<UniGLTF.MeshExportInfo> Meshes = new List<UniGLTF.MeshExportInfo>();
+
+ public int ExpectedExportByteSize => Meshes.Where(x => x.IsRendererActive).Sum(x => x.ExportByteSize);
+
+ List<Validation> m_validations = new List<Validation>();
+ public IEnumerable<Validation> Validations => m_validations;
+
+ public static void CalcMeshSize(ref UniGLTF.MeshExportInfo info,
+ string relativePath, VRMExportSettings settings, IReadOnlyList<BlendShapeClip> clips)
+ {
+ var sb = new StringBuilder();
+ if (!info.IsRendererActive)
+ {
+ sb.Append("[NotActive]");
+ }
+
+ info.VertexCount = info.Mesh.vertexCount;
+ info.ExportVertexSize = 0;
+ info.TotalBlendShapeCount = 0;
+ info.ExportBlendShapeCount = 0;
+
+ // float4 x 3
+ // vertices
+ sb.Append($"(Pos");
+ if (info.HasNormal)
+ {
+ sb.Append("+Nom");
+ info.ExportVertexSize += 4 * 3;
+ }
+ if (info.HasUV)
+ {
+ sb.Append("+UV");
+ info.ExportVertexSize += 4 * 2;
+ }
+ if (info.HasVertexColor)
+ {
+ sb.Append("+Col");
+ info.ExportVertexSize += 4 * 4;
+ }
+ if (info.HasSkinning)
+ {
+ // short, float x 4 weights
+ sb.Append("+Skin");
+ info.ExportVertexSize += (2 + 4) * 4;
+ }
+ // indices
+ info.IndexCount = info.Mesh.triangles.Length;
+
+ // postion + normal ?. always tangent is ignored
+ info.TotalBlendShapeCount = info.Mesh.blendShapeCount;
+ info.ExportBlendShapeVertexSize = settings.OnlyBlendshapePosition ? 4 * 3 : 4 * (3 + 3);
+ for (var i = 0; i < info.Mesh.blendShapeCount; ++i)
+ {
+ // var name = Mesh.GetBlendShapeName(i);
+ if (settings.ReduceBlendshape)
+ {
+ if (!ClipsContainsName(clips, settings.ReduceBlendshapeClip, new BlendShapeBinding
+ {
+ Index = i,
+ RelativePath = relativePath,
+ }))
+ {
+ // skip
+ continue;
+ }
+ }
+
+ ++info.ExportBlendShapeCount;
+ }
+
+ if (info.ExportBlendShapeCount > 0)
+ {
+ sb.Append($"+Morph x {info.ExportBlendShapeCount}");
+ }
+ sb.Append($") x {info.Mesh.vertexCount}");
+ switch (info.VertexColor)
+ {
+ case UniGLTF.MeshExportInfo.VertexColorState.ExistsAndIsUsed:
+ case UniGLTF.MeshExportInfo.VertexColorState.ExistsAndMixed: // エクスポートする
+ sb.Insert(0, "[use vcolor]");
+ break;
+ case UniGLTF.MeshExportInfo.VertexColorState.ExistsButNotUsed:
+ sb.Insert(0, "[remove vcolor]");
+ break;
+ }
+ if (info.ExportBlendShapeCount > 0 && !info.HasSkinning)
+ {
+ sb.Insert(0, "[morph without skin]");
+ }
+
+ // total bytes
+ sb.Insert(0, $"{info.ExportByteSize:#,0} Bytes = ");
+ info.Summary = sb.ToString();
+ }
+
+ bool TryGetMeshInfo(GameObject root, Renderer renderer, IReadOnlyList<BlendShapeClip> clips, VRMExportSettings settings, out UniGLTF.MeshExportInfo info)
+ {
+ info = default;
+ if (root == null)
+ {
+ info.Summary = "";
+ return false;
+ }
+ if (renderer == null)
+ {
+ info.Summary = "no Renderer";
+ return false;
+ }
+ info.Renderer = renderer;
+
+ if (renderer is SkinnedMeshRenderer smr)
+ {
+ info.Skinned = true;
+ info.Mesh = smr.sharedMesh;
+ info.IsRendererActive = smr.EnableForExport();
+ }
+ else if (renderer is MeshRenderer mr)
+ {
+ var filter = mr.GetComponent<MeshFilter>();
+ if (filter != null)
+ {
+ info.Mesh = filter.sharedMesh;
+ }
+ info.IsRendererActive = mr.EnableForExport();
+ }
+ else
+ {
+ info.Summary = "no Mesh";
+ return false;
+ }
+
+ info.VertexColor = UniGLTF.MeshExportInfo.DetectVertexColor(info.Mesh, info.Renderer.sharedMaterials);
+
+ var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, root.transform);
+ CalcMeshSize(ref info, relativePath, settings, clips);
+
+ return true;
+ }
+
+ public void SetRoot(GameObject ExportRoot, VRMExportSettings settings)
+ {
+ m_validations.Clear();
+ Meshes.Clear();
+ if (ExportRoot == null)
+ {
+ return;
+ }
+
+ var clips = new List<BlendShapeClip>();
+ var proxy = ExportRoot.GetComponent<VRMBlendShapeProxy>();
+ if (proxy != null)
+ {
+ // Export サイズ の 計算
+ if (proxy.BlendShapeAvatar != null)
+ {
+ clips.AddRange(proxy.BlendShapeAvatar.Clips);
+ }
+ }
+
+ foreach (var renderer in ExportRoot.GetComponentsInChildren<Renderer>(true))
+ {
+ if (TryGetMeshInfo(ExportRoot, renderer, clips, settings, out UniGLTF.MeshExportInfo info))
+ {
+ Meshes.Add(info);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs.meta
new file mode 100644
index 00000000..d045660f
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7961eaa3060a80d43b2bcd80961bbd29
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs
new file mode 100644
index 00000000..f650385c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs
@@ -0,0 +1,53 @@
+
+using System;
+using UnityEditor;
+using UnityEngine;
+using VRM.M17N;
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMExportMeshes))]
+ public class VRMExportMeshesEditor : Editor
+ {
+ VRMExportMeshes m_target;
+
+ private void OnEnable()
+ {
+ m_target = target as VRMExportMeshes;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ for (int i = 0; i < m_target.Meshes.Count; ++i)
+ {
+ DrawElement(i, m_target.Meshes[i]);
+ }
+ }
+
+ static (Rect, Rect) LeftRight(float x, float y, float left, float right, float height)
+ {
+ return (
+ new Rect(x, y, left, height),
+ new Rect(x + left, y, right, height)
+ );
+ }
+
+ void DrawElement(int i, UniGLTF.MeshExportInfo info)
+ {
+ var r = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(EditorGUIUtility.singleLineHeight * 3 + 20));
+ var col0 = 32;
+ var (left, right) = LeftRight(r.x, r.y, col0, r.width - col0, EditorGUIUtility.singleLineHeight);
+ EditorGUI.LabelField(left, $"{i,3}");
+
+ GUI.enabled = false;
+ EditorGUI.ObjectField(right, info.Renderer, info.Renderer.GetType(), true);
+
+ right.y += EditorGUIUtility.singleLineHeight;
+ EditorGUI.ObjectField(right, info.Mesh, info.Renderer.GetType(), true);
+ GUI.enabled = true;
+
+ right.y += EditorGUIUtility.singleLineHeight;
+ EditorGUI.LabelField(right, info.Summary);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs.meta
new file mode 100644
index 00000000..bf6e8d24
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportMeshesEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 64237fa04d62bfa48a479f54155467c6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
new file mode 100644
index 00000000..35205b54
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace VRM
+{
+ [Serializable]
+ public class VRMExportSettings : ScriptableObject
+ {
+ /// <summary>
+ /// エクスポート時に強制的にT-Pose化する
+ /// </summary>
+ [Tooltip("Option")]
+ public bool ForceTPose = false;
+
+ /// <summary>
+ /// エクスポート時にヒエラルキーの正規化を実施する
+ /// </summary>
+ [Tooltip("Require only first time")]
+ public bool PoseFreeze = true;
+
+ /// <summary>
+ /// BlendShapeのシリアライズにSparseAccessorを使う
+ /// </summary>
+ [Tooltip("Use sparse accessor for blendshape. This may reduce vrm size")]
+ public bool UseSparseAccessor = false;
+
+ /// <summary>
+ /// BlendShapeのPositionのみをエクスポートする
+ /// </summary>
+ [Tooltip("UniVRM-0.54 or later can load it. Otherwise fail to load")]
+ public bool OnlyBlendshapePosition = false;
+
+ /// <summary>
+ /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する
+ /// </summary>
+ [Tooltip("Remove blendshape that is not used from BlendShapeClip")]
+ public bool ReduceBlendshape = false;
+
+ /// <summary>
+ /// skip if BlendShapeClip.Preset == Unknown
+ /// </summary>
+ [Tooltip("Remove blendShapeClip that preset is Unknown")]
+ public bool ReduceBlendshapeClip = false;
+
+ public UniGLTF.MeshExportSettings MeshExportSettings => new UniGLTF.MeshExportSettings
+ {
+ UseSparseAccessorForMorphTarget = UseSparseAccessor,
+ ExportOnlyBlendShapePosition = OnlyBlendshapePosition,
+ };
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs.meta
new file mode 100644
index 00000000..d80644aa
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettings.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: f1bcfcc2d4692ef41b0c8f0f9ec3df14
+timeCreated: 1532066746
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs
new file mode 100644
index 00000000..2f9a1c3e
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs
@@ -0,0 +1,151 @@
+
+using System;
+using UnityEditor;
+using UnityEngine;
+using VRM.M17N;
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMExportSettings))]
+ public class VRMExportSettingsEditor : Editor
+ {
+ class CheckBoxProp
+ {
+ public SerializedProperty Property;
+ public Func<string> Description;
+
+ public CheckBoxProp(SerializedProperty property, Func<string> desc)
+ {
+ Property = property;
+ Description = desc;
+ }
+
+ public CheckBoxProp(SerializedProperty property, Options desc) : this(property, () => Msg(desc))
+ {
+ }
+
+ public CheckBoxProp(SerializedProperty property, string desc) : this(property, () => desc)
+ {
+ }
+
+ public void Draw()
+ {
+ EditorGUILayout.PropertyField(Property);
+ EditorGUILayout.HelpBox(Description(), MessageType.None);
+ EditorGUILayout.Space();
+ }
+ }
+
+ /// <summary>
+ /// エクスポート時に強制的にT-Pose化する
+ /// </summary>
+ [Tooltip("Option")]
+ public bool ForceTPose = false;
+
+ /// <summary>
+ /// エクスポート時にヒエラルキーの正規化を実施する
+ /// </summary>
+ [Tooltip("Require only first time")]
+ public bool PoseFreeze = true;
+
+ /// <summary>
+ /// エクスポート時に新しいJsonSerializerを使う
+ /// </summary>
+ [Tooltip("Use new JSON serializer")]
+ public bool UseExperimentalExporter = false;
+
+ /// <summary>
+ /// BlendShapeのシリアライズにSparseAccessorを使う
+ /// </summary>
+ [Tooltip("Use sparse accessor for blendshape. This may reduce vrm size")]
+ public bool UseSparseAccessor = false;
+
+ /// <summary>
+ /// BlendShapeのPositionのみをエクスポートする
+ /// </summary>
+ [Tooltip("UniVRM-0.54 or later can load it. Otherwise fail to load")]
+ public bool OnlyBlendshapePosition = false;
+
+ /// <summary>
+ /// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する
+ /// </summary>
+ [Tooltip("Remove blendshape that is not used from BlendShapeClip")]
+ public bool ReduceBlendshape = false;
+
+ /// <summary>
+ /// skip if BlendShapeClip.Preset == Unknown
+ /// </summary>
+ [Tooltip("Remove blendShapeClip that preset is Unknown")]
+ public bool ReduceBlendshapeClip = false;
+
+ CheckBoxProp m_forceTPose;
+ CheckBoxProp m_poseFreeze;
+ CheckBoxProp m_useSparseAccessor;
+ CheckBoxProp m_onlyBlendShapePosition;
+ CheckBoxProp m_reduceBlendShape;
+ CheckBoxProp m_reduceBlendShapeClip;
+
+ static string Msg(Options key)
+ {
+ return M17N.Getter.Msg(key);
+ }
+
+ enum Options
+ {
+ [LangMsg(Languages.ja, "エクスポート時に強制的にT-Pose化する。これを使わずに手動でT-Poseを作っても問題ありません")]
+ [LangMsg(Languages.en, "Force T-Pose before export. Manually making T-Pose for model without enabling this is ok")]
+ FORCE_T_POSE,
+
+ [LangMsg(Languages.ja, "エクスポート時に正規化(ヒエラルキーから回転と拡大縮小を取り除くためにベイク)する")]
+ [LangMsg(Languages.en, "Model's normalization (bake to remove roation and scaling from the hierarchy)")]
+ NORMALIZE,
+
+ [LangMsg(Languages.ja, "エクスポート時に新しいJsonSerializerを使う")]
+ [LangMsg(Languages.en, "The new version of JsonSerializer for model export")]
+ USE_GENERATED_SERIALIZER,
+
+ [LangMsg(Languages.ja, "BlendShapeの容量を GLTF の Sparse Accessor 機能で削減する。修正中: UniGLTF以外でロードできません")]
+ [LangMsg(Languages.en, "BlendShape size can be reduced by using Sparse Accessor")]
+ BLENDSHAPE_USE_SPARSE,
+
+ [LangMsg(Languages.ja, "BlendShapeClipのエクスポートに法線とTangentを含めない。UniVRM-0.53 以前ではロードがエラーになるのに注意してください")]
+ [LangMsg(Languages.en, "BlendShape's Normal and Tangent will not be exported. Be aware that errors may occur during import if the model is made by UniVRM-0.53 or earlier versions")]
+ BLENDSHAPE_EXCLUDE_NORMAL_AND_TANGENT,
+
+ [LangMsg(Languages.ja, "BlendShapeClipから参照されないBlendShapeをエクスポートに含めない")]
+ [LangMsg(Languages.en, "BlendShapes that are not referenced by BlendShapeClips will not be exported")]
+ BLENDSHAPE_ONLY_CLIP_USE,
+
+ [LangMsg(Languages.ja, "BlendShapeClip.Preset == Unknown のBlendShapeClipをエクスポートに含めない")]
+ [LangMsg(Languages.en, "BlendShapeClip will not be exported if BlendShapeClip.Preset == Unknown")]
+ BLENDSHAPE_EXCLUDE_UNKNOWN,
+
+ [LangMsg(Languages.ja, "エクスポートに頂点カラーを含めない")]
+ [LangMsg(Languages.en, "Vertex color will not be exported")]
+ REMOVE_VERTEX_COLOR,
+ }
+
+ private void OnEnable()
+ {
+ m_forceTPose = new CheckBoxProp(serializedObject.FindProperty(nameof(ForceTPose)), Options.FORCE_T_POSE);
+ m_poseFreeze = new CheckBoxProp(serializedObject.FindProperty(nameof(PoseFreeze)), Options.NORMALIZE);
+ m_useSparseAccessor = new CheckBoxProp(serializedObject.FindProperty(nameof(UseSparseAccessor)), Options.BLENDSHAPE_USE_SPARSE);
+ m_onlyBlendShapePosition = new CheckBoxProp(serializedObject.FindProperty(nameof(OnlyBlendshapePosition)), Options.BLENDSHAPE_EXCLUDE_NORMAL_AND_TANGENT);
+ m_reduceBlendShape = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshape)), Options.BLENDSHAPE_ONLY_CLIP_USE);
+ m_reduceBlendShapeClip = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshapeClip)), Options.BLENDSHAPE_EXCLUDE_UNKNOWN);
+ }
+
+ public override void OnInspectorGUI()
+ {
+ EditorGUIUtility.labelWidth = 160;
+ serializedObject.Update();
+ m_forceTPose.Draw();
+ m_poseFreeze.Draw();
+ m_useSparseAccessor.Draw();
+ m_onlyBlendShapePosition.Draw();
+ m_reduceBlendShape.Draw();
+ m_reduceBlendShapeClip.Draw();
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExportSettingsEditor.cs.meta
new file mode 100644
index 00000000..a7311540
--- /dev/null
+++ b/Assets/ThirdParty/VRM/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/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs
new file mode 100644
index 00000000..14d3c813
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs
@@ -0,0 +1,327 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ public class VRMExporterValidator
+ {
+ // Allows you to enable and disable the wizard create button, so that the user can not click it.
+ public bool IsValid
+ {
+ get
+ {
+ var hasError = m_validations.Any(x => !x.CanExport);
+ return !hasError && !MetaHasError;
+ }
+ }
+
+ bool MetaHasError = false;
+
+ List<Validation> m_validations = new List<Validation>();
+ public IEnumerable<Validation> Validations => m_validations;
+
+ /// <summary>
+ /// ボーン名の重複を確認
+ /// </summary>
+ /// <returns></returns>
+ bool DuplicateBoneNameExists(GameObject ExportRoot)
+ {
+ if (ExportRoot == null)
+ {
+ return false;
+ }
+ var bones = ExportRoot.transform.GetComponentsInChildren<Transform>();
+ 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;
+ }
+
+ public static bool HasRotationOrScale(GameObject root)
+ {
+ foreach (var t in root.GetComponentsInChildren<Transform>())
+ {
+ 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 == null || r == null)
+ {
+ return Vector3.zero;
+ }
+ var lr = (r.position - l.position).normalized;
+ return Vector3.Cross(lr, Vector3.up);
+ }
+
+ static string Msg(VRMExporterWizardMessages key)
+ {
+ return M17N.Getter.Msg(key);
+ }
+
+ /// <summary>
+ /// ExportDialogを表示する前に確認する。
+ /// </summary>
+ /// <param name="ExportRoot"></param>
+ /// <param name="m_settings"></param>
+ /// <returns></returns>
+ public bool RootAndHumanoidCheck(GameObject ExportRoot, VRMExportSettings m_settings, IReadOnlyList<UniGLTF.MeshExportInfo> info)
+ {
+ //
+ // root
+ //
+ if (ExportRoot == null)
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.ROOT_EXISTS)).DrawGUI();
+ return false;
+ }
+ if (ExportRoot.transform.parent != null)
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.NO_PARENT)).DrawGUI();
+ return false;
+ }
+
+ var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
+ if (renderers.All(x => !x.EnableForExport()))
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.NO_ACTIVE_MESH)).DrawGUI();
+ return false;
+ }
+
+ if (HasRotationOrScale(ExportRoot) || info.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning))
+ {
+ // 正規化必用
+ if (m_settings.PoseFreeze)
+ {
+ // する
+ EditorGUILayout.HelpBox("PoseFreeze checked. OK", MessageType.Info);
+ }
+ else
+ {
+ // しない
+ Validation.Warning(Msg(VRMExporterWizardMessages.ROTATION_OR_SCALEING_INCLUDED_IN_NODE)).DrawGUI();
+ }
+ }
+ else
+ {
+ // 不要
+ if (m_settings.PoseFreeze)
+ {
+ // する
+ Validation.Warning(Msg(VRMExporterWizardMessages.IS_POSE_FREEZE_DONE)).DrawGUI();
+ }
+ else
+ {
+ // しない
+ EditorGUILayout.HelpBox("Root OK", MessageType.Info);
+ }
+ }
+
+ //
+ // animator
+ //
+ var animator = ExportRoot.GetComponent<Animator>();
+ if (animator == null)
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.NO_ANIMATOR)).DrawGUI();
+ return false;
+ }
+
+ var avatar = animator.avatar;
+ if (avatar == null)
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.NO_AVATAR_IN_ANIMATOR)).DrawGUI();
+ return false;
+ }
+ if (!avatar.isValid)
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.AVATAR_IS_NOT_VALID)).DrawGUI();
+ return false;
+ }
+ if (!avatar.isHuman)
+ {
+ Validation.Error(Msg(VRMExporterWizardMessages.AVATAR_IS_NOT_HUMANOID)).DrawGUI();
+ return false;
+ }
+ {
+ 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(Msg(VRMExporterWizardMessages.FACE_Z_POSITIVE_DIRECTION)).DrawGUI();
+ return false;
+ }
+ }
+ var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw);
+ if (jaw != null)
+ {
+ Validation.Warning(Msg(VRMExporterWizardMessages.JAW_BONE_IS_INCLUDED)).DrawGUI();
+ }
+ else
+ {
+ EditorGUILayout.HelpBox("Animator OK", MessageType.Info);
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// エクスポート可能か検証する。
+ /// </summary>
+ /// <returns></returns>
+ public void Validate(GameObject ExportRoot, VRMExportSettings m_settings, VRMMetaObject meta)
+ {
+ m_validations.Clear();
+ if (ExportRoot == null)
+ {
+ return;
+ }
+ var proxy = ExportRoot.GetComponent<VRMBlendShapeProxy>();
+
+ m_validations.AddRange(_Validate(ExportRoot, m_settings));
+ m_validations.AddRange(VRMSpringBoneValidator.Validate(ExportRoot));
+ var firstPerson = ExportRoot.GetComponent<VRMFirstPerson>();
+ if (firstPerson != null)
+ {
+ m_validations.AddRange(firstPerson.Validate());
+ }
+ if (proxy != null)
+ {
+ m_validations.AddRange(proxy.Validate());
+ }
+ MetaHasError = meta.Validate().Any();
+ }
+
+ IEnumerable<Validation> _Validate(GameObject ExportRoot, VRMExportSettings m_settings)
+ {
+ if (ExportRoot == null)
+ {
+ yield break;
+ }
+
+ if (DuplicateBoneNameExists(ExportRoot))
+ {
+ yield return Validation.Warning(Msg(VRMExporterWizardMessages.DUPLICATE_BONE_NAME_EXISTS));
+ }
+
+ if (m_settings.ReduceBlendshape && ExportRoot.GetComponent<VRMBlendShapeProxy>() == null)
+ {
+ yield return Validation.Error(Msg(VRMExporterWizardMessages.NEEDS_VRM_BLENDSHAPE_PROXY));
+ }
+
+ var renderers = ExportRoot.GetComponentsInChildren<Renderer>();
+ foreach (var r in renderers)
+ {
+ for (int i = 0; i < r.sharedMaterials.Length; ++i)
+ if (r.sharedMaterials[i] == null)
+ {
+ yield return Validation.Error($"Renderer: {r.name}.Materials[{i}] is null. please fix it");
+ }
+ }
+
+ var materials = renderers.SelectMany(x => x.sharedMaterials).Where(x => x != null).Distinct();
+ foreach (var material in materials)
+ {
+ if (material == null)
+ {
+ continue;
+ }
+
+ 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($"Material: {material.name}. Unknown Shader: \"{material.shader.name}\" is used. {Msg(VRMExporterWizardMessages.UNKNOWN_SHADER)}");
+ }
+
+ foreach (var material in materials)
+ {
+ if (IsFileNameLengthTooLong(material.name))
+ yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + material.name);
+ }
+
+ var textureNameList = new List<string>();
+ 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(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + textureName);
+ }
+
+ var vrmMeta = ExportRoot.GetComponent<VRMMeta>();
+ if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null)
+ {
+ var thumbnailName = vrmMeta.Meta.Thumbnail.name;
+ if (IsFileNameLengthTooLong(thumbnailName))
+ yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + thumbnailName);
+ }
+
+ var meshFilters = ExportRoot.GetComponentsInChildren<MeshFilter>();
+ var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct();
+ foreach (var meshName in meshesName)
+ {
+ if (IsFileNameLengthTooLong(meshName))
+ yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + meshName);
+ }
+
+ var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren<SkinnedMeshRenderer>();
+ var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct();
+ foreach (var skinnedmeshName in skinnedmeshesName)
+ {
+ if (IsFileNameLengthTooLong(skinnedmeshName))
+ yield return Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + skinnedmeshName);
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs.meta
new file mode 100644
index 00000000..c28dfe80
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterVaildator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bc233cbadb897eb4886de9927bee9fc2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
new file mode 100644
index 00000000..3b4984ea
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs
@@ -0,0 +1,391 @@
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ /// <summary>
+ /// エクスポートダイアログ
+ /// </summary>
+ public class VRMExporterWizard : EditorWindow
+ {
+ const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export humanoid";
+
+ [MenuItem(CONVERT_HUMANOID_KEY, false, 1)]
+ private static void ExportFromMenu()
+ {
+ VRMExporterWizard.CreateWizard();
+ }
+
+ enum Tabs
+ {
+ Meta,
+ Mesh,
+ ExportSettings,
+ }
+ Tabs _tab;
+
+ GUIStyle TabButtonStyle => "LargeButton";
+
+ // GUI.ToolbarButtonSize.FitToContentsも設定できる
+ GUI.ToolbarButtonSize TabButtonSize => GUI.ToolbarButtonSize.Fixed;
+ const string EXTENSION = ".vrm";
+
+ private static string m_lastExportDir;
+
+
+ GameObject ExportRoot;
+
+ VRMExportSettings m_settings;
+ VRMExportMeshes m_meshes;
+
+ VRMMetaObject m_meta;
+ VRMMetaObject Meta
+ {
+ get { return m_meta; }
+ set
+ {
+ if (m_meta == value)
+ {
+ return;
+ }
+ m_requireValidation = true;
+ if (m_metaEditor != null)
+ {
+ UnityEditor.Editor.DestroyImmediate(m_metaEditor);
+ m_metaEditor = null;
+ }
+ m_meta = value;
+ }
+ }
+
+ void UpdateRoot(GameObject root)
+ {
+ if (root == ExportRoot)
+ {
+ return;
+ }
+ m_requireValidation = true;
+ ExportRoot = root;
+ UnityEditor.Editor.DestroyImmediate(m_metaEditor);
+ m_metaEditor = null;
+
+ if (ExportRoot == null)
+ {
+ Meta = null;
+ }
+ else
+ {
+ // do validation
+ Validate();
+
+ // default setting
+ m_settings.PoseFreeze =
+ VRMExporterValidator.HasRotationOrScale(ExportRoot)
+ || m_meshes.Meshes.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning)
+ ;
+
+ var meta = ExportRoot.GetComponent<VRMMeta>();
+ if (meta != null)
+ {
+ Meta = meta.Meta;
+ }
+ else
+ {
+ Meta = null;
+ }
+ }
+ }
+
+ void Validate()
+ {
+ if (!m_requireValidation)
+ {
+ return;
+ }
+ m_validator.Validate(ExportRoot, m_settings, Meta != null ? Meta : m_tmpMeta);
+ m_requireValidation = false;
+ m_meshes.SetRoot(ExportRoot, m_settings);
+ }
+
+ VRMMetaObject m_tmpMeta;
+
+ Editor m_metaEditor;
+ Editor m_settingsInspector;
+ Editor m_meshesInspector;
+
+ VRMExporterValidator m_validator = new VRMExporterValidator();
+ bool m_requireValidation = true;
+
+ private Vector2 m_ScrollPosition;
+
+ void OnEnable()
+ {
+ // Debug.Log("OnEnable");
+ Undo.willFlushUndoRecord += OnWizardUpdate;
+ Selection.selectionChanged += OnWizardUpdate;
+
+ m_tmpMeta = ScriptableObject.CreateInstance<VRMMetaObject>();
+
+ m_settings = ScriptableObject.CreateInstance<VRMExportSettings>();
+ m_settingsInspector = Editor.CreateEditor(m_settings);
+
+ m_meshes = ScriptableObject.CreateInstance<VRMExportMeshes>();
+ m_meshesInspector = Editor.CreateEditor(m_meshes);
+ }
+
+ void OnDisable()
+ {
+ ExportRoot = null;
+
+ // Debug.Log("OnDisable");
+ Selection.selectionChanged -= OnWizardUpdate;
+ Undo.willFlushUndoRecord -= OnWizardUpdate;
+
+ // m_metaEditor
+ UnityEditor.Editor.DestroyImmediate(m_metaEditor);
+ m_metaEditor = null;
+ // m_settingsInspector
+ UnityEditor.Editor.DestroyImmediate(m_settingsInspector);
+ m_settingsInspector = null;
+ // m_meshesInspector
+ UnityEditor.Editor.DestroyImmediate(m_meshesInspector);
+ m_meshesInspector = null;
+ // Meta
+ Meta = null;
+ ScriptableObject.DestroyImmediate(m_tmpMeta);
+ m_tmpMeta = null;
+ // m_settings
+ ScriptableObject.DestroyImmediate(m_settings);
+ m_settings = null;
+ // m_meshes
+ ScriptableObject.DestroyImmediate(m_meshes);
+ m_meshes = null;
+ }
+
+ 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 == 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;
+ }
+ }
+
+ private void OnGUI()
+ {
+ if (m_tmpMeta == null)
+ {
+ // OnDisable
+ return;
+ }
+
+ EditorGUIUtility.labelWidth = 150;
+
+ // lang
+ M17N.Getter.OnGuiSelectLang();
+
+ EditorGUILayout.LabelField("ExportRoot");
+ {
+ var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true);
+ UpdateRoot(root);
+ }
+
+ // ArgumentException: Getting control 1's position in a group with only 1 controls when doing repaint Aborting
+ // Validation により GUI の表示項目が変わる場合があるので、
+ // EventType.Layout と EventType.Repaint 間で内容が変わらないようしている。
+ if (Event.current.type == EventType.Layout)
+ {
+ Validate();
+ }
+
+ //
+ // Humanoid として適正か? ここで失敗する場合は Export UI を表示しない
+ //
+ if (!m_validator.RootAndHumanoidCheck(ExportRoot, m_settings, m_meshes.Meshes))
+ {
+ return;
+ }
+
+ EditorGUILayout.HelpBox($"Mesh size: {m_meshes.ExpectedExportByteSize / 1000000.0f:0.0} MByte", MessageType.Info);
+ _tab = TabBar.OnGUI(_tab, TabButtonStyle, TabButtonSize);
+
+ // Render contents using Generic Inspector GUI
+ m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box");
+ GUIUtility.GetControlID(645789, FocusType.Passive);
+
+ //
+ // VRM の Validation
+ //
+ foreach (var v in m_validator.Validations)
+ {
+ v.DrawGUI();
+ }
+ foreach (var meshInfo in m_meshes.Meshes)
+ {
+ switch (meshInfo.VertexColor)
+ {
+ case UniGLTF.MeshExportInfo.VertexColorState.ExistsAndMixed:
+ Validation.Warning($"{meshInfo.Renderer}: Both vcolor.multiply and not multiply unlit materials exist").DrawGUI();
+ break;
+ }
+ }
+
+ bool modified = DrawWizardGUI();
+ EditorGUILayout.EndScrollView();
+
+ // Create and Other Buttons
+ {
+ // errors
+ GUILayout.BeginVertical();
+ // GUILayout.FlexibleSpace();
+
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+ GUI.enabled = m_validator.IsValid;
+
+ if (GUILayout.Button("Export", GUILayout.MinWidth(100)))
+ {
+ OnWizardCreate();
+ Close();
+ GUIUtility.ExitGUI();
+ }
+ GUI.enabled = true;
+
+ GUILayout.EndHorizontal();
+ }
+ GUILayout.EndVertical();
+ }
+
+ GUILayout.Space(8);
+
+ if (modified)
+ {
+ m_requireValidation = true;
+ Repaint();
+ }
+ }
+
+ bool DrawWizardGUI()
+ {
+ if (m_tmpMeta == null)
+ {
+ // disabled
+ return false;
+ }
+
+ // tabbar
+ 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;
+
+ case Tabs.ExportSettings:
+ m_settingsInspector.OnInspectorGUI();
+ break;
+
+ case Tabs.Mesh:
+ m_meshesInspector.OnInspectorGUI();
+ break;
+ }
+
+ return true;
+ }
+
+ // Creates a wizard.
+ public static VRMExporterWizard DisplayWizard()
+ {
+ VRMExporterWizard wizard = CreateInstance<VRMExporterWizard>();
+ wizard.titleContent = new GUIContent("VRM Exporter");
+ if (wizard != null)
+ {
+ wizard.InvokeWizardUpdate();
+ wizard.ShowUtility();
+ }
+ return wizard;
+ }
+
+ public static void CreateWizard()
+ {
+ var wiz = VRMExporterWizard.DisplayWizard();
+ var go = Selection.activeObject as GameObject;
+
+ // update checkbox
+ wiz.UpdateRoot(go);
+
+ if (go != null)
+ {
+ wiz.m_settings.PoseFreeze = VRMExporterValidator.HasRotationOrScale(go);
+ }
+
+ wiz.OnWizardUpdate();
+ }
+
+ void OnWizardCreate()
+ {
+ string directory;
+ if (string.IsNullOrEmpty(m_lastExportDir))
+ directory = Directory.GetParent(Application.dataPath).ToString();
+ else
+ directory = m_lastExportDir;
+
+ // save dialog
+ var path = EditorUtility.SaveFilePanel(
+ "Save vrm",
+ directory,
+ ExportRoot.name + EXTENSION,
+ EXTENSION.Substring(1));
+ if (string.IsNullOrEmpty(path))
+ {
+ return;
+ }
+ m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
+
+ // export
+ VRMEditorExporter.Export(path, ExportRoot, Meta != null ? Meta : m_tmpMeta, m_settings, m_meshes.Meshes);
+ }
+
+ void OnWizardUpdate()
+ {
+ UpdateRoot(ExportRoot);
+ m_requireValidation = true;
+ Repaint();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta
new file mode 100644
index 00000000..ee2067f0
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: a8e41aa30fcc76e43ad8588aef8572ea
+timeCreated: 1520491195
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs
new file mode 100644
index 00000000..e9e521f5
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs
@@ -0,0 +1,80 @@
+using VRM.M17N;
+
+namespace VRM
+{
+ public enum VRMExporterWizardMessages
+ {
+ [LangMsg(Languages.ja, "ExportRootをセットしてください")]
+ [LangMsg(Languages.en, "Please set up a ExportRoot for model export")]
+ ROOT_EXISTS,
+
+
+ [LangMsg(Languages.ja, "ExportRootに親はオブジェクトは持てません")]
+ [LangMsg(Languages.en, "ExportRoot must be topmost parent")]
+ NO_PARENT,
+
+ [LangMsg(Languages.ja, "ExportRootに回転・拡大縮小は持てません。子階層で回転・拡大縮小してください")]
+ [LangMsg(Languages.en, "ExportRoot's rotation and scaling are not allowed to change. Please set up rotation and scaling in child node")]
+ ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED,
+
+ [LangMsg(Languages.ja, "シーンに出していない Prefab はエクスポートできません(細かい挙動が違い、想定外の動作をところがあるため)。シーンに展開してからエクスポートしてください")]
+ [LangMsg(Languages.en, "Prefab Asset cannot be exported. Prefab Asset has different behaviour with Scene GameObject. Please put the prefab into the scene")]
+ PREFAB_CANNOT_EXPORT,
+
+ [LangMsg(Languages.ja, "回転・拡大縮小もしくはWeightの無いBlendShapeが含まれています。正規化が必用です。Setting の PoseFreeze を有効にしてください")]
+ [LangMsg(Languages.en, " Normalization is required. There are nodes (child GameObject) where rotation and scaling or blendshape without bone weight are not default. Please enable PoseFreeze")]
+ ROTATION_OR_SCALEING_INCLUDED_IN_NODE,
+
+ [LangMsg(Languages.ja, "正規化済みです。Setting の PoseFreeze は不要です")]
+ [LangMsg(Languages.en, "Normalization has been done. PoseFreeze is not required")]
+ IS_POSE_FREEZE_DONE,
+
+ [LangMsg(Languages.ja, "ExportRootに Animator がありません")]
+ [LangMsg(Languages.en, "No Animator in ExportRoot")]
+ NO_ANIMATOR,
+
+ [LangMsg(Languages.ja, "Z+ 向きにしてください")]
+ [LangMsg(Languages.en, "The model needs to face the positive Z-axis")]
+ FACE_Z_POSITIVE_DIRECTION,
+
+ [LangMsg(Languages.ja, "ExportRootの Animator に Avatar がありません")]
+ [LangMsg(Languages.en, "No Avatar in ExportRoot's Animator")]
+ NO_AVATAR_IN_ANIMATOR,
+
+ [LangMsg(Languages.ja, "ExportRootの Animator.Avatar が不正です")]
+ [LangMsg(Languages.en, "Animator.avatar in ExportRoot is not valid")]
+ AVATAR_IS_NOT_VALID,
+
+ [LangMsg(Languages.ja, "ExportRootの Animator.Avatar がヒューマノイドではありません。FBX importer の Rig で設定してください")]
+ [LangMsg(Languages.en, "Animator.avatar is not humanoid. Please change model's AnimationType to humanoid")]
+ AVATAR_IS_NOT_HUMANOID,
+
+ [LangMsg(Languages.ja, "humanoid設定に顎が含まれている。FBX importer の rig 設定で顎ボーンの割り当てを確認できます")]
+ [LangMsg(Languages.en, "Jaw bone is included. It may not what you intended. Please check the humanoid avatar setting screen")]
+ JAW_BONE_IS_INCLUDED,
+
+ [LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする")]
+ [LangMsg(Languages.en, "There are bones with the same name in the hierarchy. They will be automatically renamed after export")]
+ DUPLICATE_BONE_NAME_EXISTS,
+
+ [LangMsg(Languages.ja, "VRMBlendShapeProxyが必要です。先にVRMフォーマットに変換してください")]
+ [LangMsg(Languages.en, "VRMBlendShapeProxy is required. Please convert to VRM format first")]
+ NEEDS_VRM_BLENDSHAPE_PROXY,
+
+ [LangMsg(Languages.en, "This model contains vertex color")]
+ [LangMsg(Languages.ja, "ヒエラルキーに含まれる mesh に頂点カラーが含まれている")]
+ VERTEX_COLOR_IS_INCLUDED,
+
+ [LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていない")]
+ [LangMsg(Languages.en, "No active mesh")]
+ NO_ACTIVE_MESH,
+
+ [LangMsg(Languages.ja, "Standard, Unlit, MToon 以外のマテリアルは、Standard になります")]
+ [LangMsg(Languages.en, "It will export as `Standard` fallback")]
+ UNKNOWN_SHADER,
+
+ [LangMsg(Languages.ja, "名前が長すぎる。リネームしてください: ")]
+ [LangMsg(Languages.en, "FileName is too long: ")]
+ FILENAME_TOO_LONG,
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs.meta
new file mode 100644
index 00000000..e6728223
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMExporterWizardMessages.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 515984b15b5b5bf4baca4fa2d6344fcf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
new file mode 100644
index 00000000..6dbb8bc2
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs
@@ -0,0 +1,55 @@
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using UniGLTF;
+
+
+namespace VRM
+{
+ public static class VRMHumanoidNormalizerMenu
+ {
+ const string MENU_KEY = VRMVersion.MENU + "/Freeze T-Pose";
+ [MenuItem(MENU_KEY, true, 1)]
+ private static bool ExportValidate()
+ {
+ var root = Selection.activeObject as GameObject;
+ if (root == null)
+ {
+ return false;
+ }
+
+ var animator = root.GetComponent<Animator>();
+ if (animator == null)
+ {
+ return false;
+ }
+
+ var avatar = animator.avatar;
+ if (avatar == null)
+ {
+ return false;
+ }
+
+ if (!avatar.isValid)
+ {
+ return false;
+ }
+
+ if (!avatar.isHuman)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ [MenuItem(MENU_KEY, false, 1)]
+ private static void ExportFromMenu()
+ {
+ var go = Selection.activeObject as GameObject;
+
+ // BoneNormalizer.Execute はコピーを正規化する。UNDO無用
+ Selection.activeGameObject = VRMBoneNormalizer.Execute(go, true, false);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs.meta
new file mode 100644
index 00000000..619ee096
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMHumanoidNormalizerMenu.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b3e1aacd7b7e1fb468b3378de03e5629
+timeCreated: 1520488809
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs
new file mode 100644
index 00000000..59d4430a
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs
@@ -0,0 +1,67 @@
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+using UniGLTF;
+
+
+namespace VRM
+{
+ public static class VRMImporterMenu
+ {
+ [MenuItem(VRMVersion.MENU + "/Import", priority = 1)]
+ static void ImportMenu()
+ {
+ var path = EditorUtility.OpenFilePanel("open vrm", "", "vrm");
+ if (string.IsNullOrEmpty(path))
+ {
+ return;
+ }
+
+ if (Application.isPlaying)
+ {
+ // load into scene
+ var context = new VRMImporterContext();
+ context.Load(path);
+ context.ShowMeshes();
+ context.EnableUpdateWhenOffscreen();
+ Selection.activeGameObject = context.Root;
+ }
+ else
+ {
+ if (path.StartsWithUnityAssetPath())
+ {
+ Debug.LogWarningFormat("disallow import from folder under the Assets");
+ return;
+ }
+
+ var assetPath = EditorUtility.SaveFilePanel("save prefab", "Assets", Path.GetFileNameWithoutExtension(path), "prefab");
+ if (string.IsNullOrEmpty(path))
+ {
+ return;
+ }
+
+ if (!assetPath.StartsWithUnityAssetPath())
+ {
+ Debug.LogWarningFormat("out of asset path: {0}", assetPath);
+ return;
+ }
+
+ // import as asset
+ var prefabPath = UnityPath.FromUnityPath(assetPath);
+ var context = new VRMImporterContext();
+ context.ParseGlb(File.ReadAllBytes(path));
+ context.ExtractImages(prefabPath);
+
+ EditorApplication.delayCall += () =>
+ {
+ //
+ // after textures imported
+ //
+ context.Load();
+ context.SaveAsAsset(prefabPath);
+ context.EditorDestroyRoot();
+ };
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs.meta
new file mode 100644
index 00000000..921acdc6
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMImporterMenu.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: da5decfeae53a6444977caf85d09bc88
+timeCreated: 1517153624
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs
new file mode 100644
index 00000000..a2d05726
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs
@@ -0,0 +1,144 @@
+using System.IO;
+using System.Text;
+using UnityEditor;
+using UnityEngine;
+
+namespace VRM
+{
+ public class VRMVersionMenu : EditorWindow
+ {
+ const string VersionPath = "Assets/VRM/UniVRM/Scripts/Format/VRMVersion.cs";
+ const string VersionTemplate = @"
+namespace VRM
+{{
+ public static partial class VRMVersion
+ {{
+ public const int MAJOR = {0};
+ public const int MINOR = {1};
+ public const int PATCH = {2};
+ public const string VERSION = ""{0}.{1}.{2}"";
+ }}
+}}
+";
+
+ const string VRMShadersPackagePath = "Assets/VRMShaders/package.json";
+ const string VRMShadersPackageTemplate = @"{{
+ ""name"": ""com.vrmc.vrmshaders"",
+ ""version"": ""{0}.{1}.{2}"",
+ ""displayName"": ""VRM Shaders"",
+ ""description"": ""VRM Shaders"",
+ ""unity"": ""2018.4"",
+ ""keywords"": [
+ ""vrm"",
+ ""shader""
+ ],
+ ""author"": {{
+ ""name"": ""VRM Consortium""
+ }}
+}}
+";
+
+ const string MeshUtilityPath = "Assets/MeshUtility/package.json";
+ const string MeshUtilityTemplate = @"{{
+ ""name"": ""com.vrmc.meshutility"",
+ ""version"": ""{0}.{1}.{2}"",
+ ""displayName"": ""MeshUtility"",
+ ""unity"": ""2018.4"",
+ ""description"": ""MeshUtility is a package for mesh separation, etc. \n\nCheck out the latest information here: <https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility>"",
+ ""keywords"": [
+ ""mesh""
+ ],
+ ""author"": {{
+ ""name"": ""VRM Consortium""
+ }}
+}}
+";
+
+ const string VRMPackagePath = "Assets/VRM/package.json";
+ const string VRMPackageTemplate = @"{{
+ ""name"": ""com.vrmc.univrm"",
+ ""version"": ""{0}.{1}.{2}"",
+ ""displayName"": ""VRM"",
+ ""description"": ""VRM importer"",
+ ""unity"": ""2018.4"",
+ ""keywords"": [
+ ""vrm"",
+ ""importer"",
+ ""avatar"",
+ ""vr""
+ ],
+ ""author"": {{
+ ""name"": ""VRM Consortium""
+ }},
+ ""dependencies"": {{
+ ""com.vrmc.vrmshaders"": ""{0}.{1}.{2}"",
+ ""com.vrmc.meshutility"": ""{0}.{1}.{2}""
+ }}
+}}
+";
+
+ [SerializeField]
+ string m_version;
+
+ void OnGUI()
+ {
+ GUILayout.Label($"Current version: {VRMVersion.VERSION}");
+
+ m_version = EditorGUILayout.TextField("Major.Minor.Patch", m_version);
+
+ if (GUILayout.Button("Apply"))
+ {
+ if (string.IsNullOrEmpty(m_version))
+ {
+ return;
+ }
+ var splitted = m_version.Split('.');
+ if (splitted.Length != 3)
+ {
+ Debug.LogWarning($"InvalidFormat: {m_version}");
+ return;
+ }
+ var values = new int[3];
+ for (int i = 0; i < 3; ++i)
+ {
+ values[i] = int.Parse(splitted[i]);
+ }
+
+ // generate
+ var utf8 = new UTF8Encoding(false);
+ File.WriteAllText(VersionPath, string.Format(VersionTemplate,
+ values[0],
+ values[1],
+ values[2]), utf8);
+ File.WriteAllText(VRMShadersPackagePath, string.Format(VRMShadersPackageTemplate,
+ values[0],
+ values[1],
+ values[2]), utf8);
+ File.WriteAllText(MeshUtilityPath, string.Format(MeshUtilityTemplate,
+ values[0],
+ values[1],
+ values[2]), utf8);
+ File.WriteAllText(VRMPackagePath, string.Format(VRMPackageTemplate,
+ values[0],
+ values[1],
+ values[2]), utf8);
+ AssetDatabase.Refresh();
+ }
+
+ if (GUILayout.Button("Close"))
+ {
+ Close();
+ }
+ }
+
+#if VRM_DEVELOP
+ [MenuItem(VRMVersion.MENU + "/VersionDialog")]
+#endif
+ static void ShowVersionDialog()
+ {
+ var window = ScriptableObject.CreateInstance<VRMVersionMenu>();
+ window.m_version = VRMVersion.VERSION;
+ window.ShowUtility();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs.meta
new file mode 100644
index 00000000..9d7c9e56
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/VRMVersionMenu.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 0c69abb555d67f647a7d3175e35e37d8
+timeCreated: 1522130550
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs
new file mode 100644
index 00000000..0bb5d097
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs
@@ -0,0 +1,58 @@
+using System;
+using System.IO;
+using System.Linq;
+using UniGLTF;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+#if !VRM_STOP_ASSETPOSTPROCESSOR
+ public class vrmAssetPostprocessor : AssetPostprocessor
+ {
+ static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
+ {
+ foreach (string path in importedAssets)
+ {
+ if (UnityPath.FromUnityPath(path).IsStreamingAsset)
+ {
+ Debug.LogFormat("Skip StreamingAssets: {0}", path);
+ continue;
+ }
+
+ var ext = Path.GetExtension(path).ToLower();
+ if (ext == ".vrm")
+ {
+ ImportVrm(UnityPath.FromUnityPath(path));
+ }
+ }
+ }
+
+ static void ImportVrm(UnityPath path)
+ {
+ if (!path.IsUnderAssetsFolder)
+ {
+ throw new Exception();
+ }
+ var context = new VRMImporterContext();
+ context.ParseGlb(File.ReadAllBytes(path.FullPath));
+
+ var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");
+
+ // save texture assets !
+ context.ExtractImages(prefabPath);
+
+ EditorApplication.delayCall += () =>
+ {
+ //
+ // after textures imported
+ //
+ context.Load();
+ context.SaveAsAsset(prefabPath);
+ context.EditorDestroyRoot();
+ };
+ }
+ }
+#endif
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs.meta
new file mode 100644
index 00000000..162103e6
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Format/vrmAssetPostprocessor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 561392fdfe88bf14e83b6e89a075cc52
+timeCreated: 1517119659
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt.meta
new file mode 100644
index 00000000..62aecba1
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 11125e99ded1b2941b4e6742fca5be7b
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs
new file mode 100644
index 00000000..9fa73a5c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs
@@ -0,0 +1,185 @@
+using UnityEditor;
+using UnityEngine;
+using UniGLTF;
+using System.Linq;
+
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMLookAtHead))]
+ public class VRMLookAtHeadEditor : Editor
+ {
+ VRMLookAtHead m_target;
+ PreviewRenderUtility m_previewRenderUtility;
+
+#if UNITY_2017_1_OR_NEWER
+ struct Item
+ {
+ public Transform Transform;
+ public SkinnedMeshRenderer SkinnedMeshRenderer;
+ public Mesh Mesh;
+ public Material[] Materials;
+
+ public Mesh Baked()
+ {
+ if (SkinnedMeshRenderer != null)
+ {
+ if (Mesh == null)
+ {
+ Mesh = new Mesh();
+ }
+ SkinnedMeshRenderer.BakeMesh(Mesh);
+ }
+ return Mesh;
+ }
+ }
+ Item[] m_items;
+
+ void SetupItems()
+ {
+ m_items = m_target.transform.Traverse().Select(x =>
+ {
+ var meshFilter = x.GetComponent<MeshFilter>();
+ var meshRenderer = x.GetComponent<MeshRenderer>();
+ var skinnedMeshRenderer = x.GetComponent<SkinnedMeshRenderer>();
+ if (meshFilter != null && meshRenderer != null)
+ {
+ return new Item
+ {
+ Mesh = meshFilter.sharedMesh,
+ Transform = x.transform,
+ Materials = meshRenderer.sharedMaterials,
+ };
+ }
+ else if (skinnedMeshRenderer != null)
+ {
+ return new Item
+ {
+ //Mesh = skinnedMeshRenderer.sharedMesh,
+ SkinnedMeshRenderer = skinnedMeshRenderer,
+ Transform = x.transform,
+ Materials = skinnedMeshRenderer.sharedMaterials
+ };
+ }
+ else
+ {
+ return default(Item);
+ }
+ })
+ .Where(x => x.Transform != null)
+ .ToArray();
+ }
+#endif
+
+ void OnEnable()
+ {
+ m_target = (VRMLookAtHead)target;
+ m_previewRenderUtility = new PreviewRenderUtility(true);
+
+#if UNITY_2017_1_OR_NEWER
+ SetupItems();
+#endif
+ }
+
+ private void OnDisable()
+ {
+ m_previewRenderUtility.Cleanup();
+ m_previewRenderUtility = null;
+ }
+
+ static void SetPreviewCamera(Camera camera, Vector3 target, Vector3 forward)
+ {
+ camera.fieldOfView = 30f;
+ camera.farClipPlane = 100;
+ camera.nearClipPlane = 0.1f;
+
+ camera.transform.position = target + forward * 0.8f;
+ camera.transform.LookAt(target);
+ }
+
+ public override bool HasPreviewGUI()
+ {
+ return true;
+ }
+
+ public override void OnPreviewGUI(Rect r, GUIStyle background)
+ {
+ m_previewRenderUtility.BeginPreview(r, background);
+ var target = m_target.Head;
+ if (target != null)
+ {
+#if UNITY_2017_1_OR_NEWER
+ SetPreviewCamera(
+ m_previewRenderUtility.camera,
+ target.position + new Vector3(0, 0.1f, 0),
+ target.forward
+ );
+ for(int j=0; j<m_items.Length; ++j)
+ {
+ ref var x = ref m_items[j];
+ var mesh = x.Baked();
+ for(int i=0; i<x.Materials.Length; ++i)
+ {
+ m_previewRenderUtility.DrawMesh(mesh, x.Transform.position, x.Transform.rotation,
+ x.Materials[i], i);
+ }
+ }
+
+ m_previewRenderUtility.Render();
+#else
+ SetPreviewCamera(
+ m_previewRenderUtility.m_Camera,
+ target.position + new Vector3(0, 0.1f, 0),
+ target.forward
+ );
+ m_previewRenderUtility.m_Camera.Render();
+#endif
+ }
+ m_previewRenderUtility.EndAndDrawPreview(r);
+ }
+
+ const float RADIUS = 0.5f;
+
+ void OnSceneGUI()
+ {
+ if (m_target.Head == null) return;
+ if (!m_target.DrawGizmo) return;
+
+ if (m_target.Target != null)
+ {
+ {
+ EditorGUI.BeginChangeCheck();
+ var newTargetPosition = Handles.PositionHandle(m_target.Target.position, Quaternion.identity);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(m_target.Target, "Change Look At Target Position");
+ m_target.Target.position = newTargetPosition;
+ }
+ }
+
+ Handles.color = new Color(1, 1, 1, 0.6f);
+ Handles.DrawDottedLine(m_target.Head.position, m_target.Target.position, 4.0f);
+ }
+
+ Handles.matrix = m_target.Head.localToWorldMatrix;
+ Handles.Label(Vector3.zero, string.Format("Yaw: {0:0.}degree\nPitch: {1:0.}degree",
+ m_target.Yaw,
+ m_target.Pitch));
+
+ Handles.color = new Color(0, 1, 0, 0.2f);
+ Handles.DrawSolidArc(Vector3.zero,
+ Matrix4x4.identity.GetColumn(1),
+ Matrix4x4.identity.GetColumn(2),
+ m_target.Yaw,
+ RADIUS);
+
+ Handles.matrix = m_target.Head.localToWorldMatrix * m_target.YawMatrix;
+ Handles.color = new Color(1, 0, 0, 0.2f);
+ Handles.DrawSolidArc(Vector3.zero,
+ Matrix4x4.identity.GetColumn(0),
+ Matrix4x4.identity.GetColumn(2),
+ -m_target.Pitch,
+ RADIUS);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs.meta
new file mode 100644
index 00000000..637bbdae
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/LookAt/VRMLookAtHeadEditor.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 6c4b1a599166c48488a860a0017c884a
+timeCreated: 1518293126
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta.meta
new file mode 100644
index 00000000..9f0aa7bd
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 04a204174b44d7a499298d6ca0cde1cc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs
new file mode 100644
index 00000000..fac50451
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs
@@ -0,0 +1,29 @@
+using UnityEditor;
+
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMMeta))]
+ public class VRMMetaEditor : Editor
+ {
+ Editor m_Inspector;
+ SerializedProperty m_VRMMetaObjectProp;
+
+ private void OnDestroy()
+ {
+ UnityEditor.Editor.DestroyImmediate(m_Inspector);
+ }
+
+ private void OnEnable()
+ {
+ m_VRMMetaObjectProp = serializedObject.FindProperty("Meta");
+ m_Inspector = Editor.CreateEditor(m_VRMMetaObjectProp.objectReferenceValue);
+ }
+
+ public override void OnInspectorGUI()
+ {
+ EditorGUILayout.PropertyField(m_VRMMetaObjectProp);
+ m_Inspector.OnInspectorGUI();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs.meta
new file mode 100644
index 00000000..63aaa988
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ca21d8c33b34adc4db45afd930049fc1
+timeCreated: 1522989712
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs
new file mode 100644
index 00000000..206ce5a2
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs
@@ -0,0 +1,250 @@
+using UnityEditor;
+using UnityEngine;
+using VRM.M17N;
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMMetaObject))]
+ public class VRMMetaObjectEditor : Editor
+ {
+ class ValidateProperty
+ {
+ public SerializedProperty m_prop;
+
+ public delegate (string, MessageType) Validator(SerializedProperty prop);
+ Validator m_validator;
+
+ public ValidateProperty(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;
+ }
+ }
+
+ VRMMetaObject m_target;
+ SerializedProperty m_Script;
+ SerializedProperty m_exporterVersion;
+ SerializedProperty m_thumbnail;
+ ValidateProperty m_title;
+ ValidateProperty m_version;
+ ValidateProperty m_author;
+ ValidateProperty m_contact;
+ ValidateProperty m_reference;
+
+ SerializedProperty m_AllowedUser;
+ SerializedProperty m_ViolentUssage;
+ SerializedProperty m_SexualUssage;
+ SerializedProperty m_CommercialUssage;
+ SerializedProperty m_OtherPermissionUrl;
+
+ SerializedProperty m_LicenseType;
+ SerializedProperty m_OtherLicenseUrl;
+
+ static string RequiredMessage(string name)
+ {
+ switch (M17N.Getter.Lang)
+ {
+ case M17N.Languages.ja:
+ return $"必須項目。{name} を入力してください";
+
+ case M17N.Languages.en:
+ return $"{name} is required";
+
+ default:
+ throw new System.NotImplementedException();
+ }
+ }
+
+ private void OnEnable()
+ {
+ m_target = (VRMMetaObject)target;
+
+ m_Script = serializedObject.FindProperty("m_Script");
+ m_exporterVersion = serializedObject.FindProperty(nameof(m_target.ExporterVersion));
+ m_thumbnail = serializedObject.FindProperty(nameof(m_target.Thumbnail));
+ m_title = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Title)), prop =>
+ {
+ if (string.IsNullOrEmpty(prop.stringValue))
+ {
+ return (RequiredMessage(prop.name), MessageType.Error);
+ }
+ return ("", MessageType.None);
+ });
+ m_version = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Version)), prop =>
+ {
+ if (string.IsNullOrEmpty(prop.stringValue))
+ {
+ return (RequiredMessage(prop.name), MessageType.Error);
+ }
+ return ("", MessageType.None);
+ });
+ m_author = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Author)), prop =>
+ {
+ if (string.IsNullOrEmpty(prop.stringValue))
+ {
+ return (RequiredMessage(prop.name), MessageType.Error);
+ }
+ return ("", MessageType.None);
+ });
+ m_contact = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.ContactInformation)), prop =>
+ {
+ return ("", MessageType.None);
+ });
+ m_reference = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Reference)), prop =>
+ {
+ return ("", MessageType.None);
+ });
+
+ m_AllowedUser = serializedObject.FindProperty(nameof(m_target.AllowedUser));
+ m_ViolentUssage = serializedObject.FindProperty(nameof(m_target.ViolentUssage));
+ m_SexualUssage = serializedObject.FindProperty(nameof(m_target.SexualUssage));
+ m_CommercialUssage = serializedObject.FindProperty(nameof(m_target.CommercialUssage));
+ m_OtherPermissionUrl = serializedObject.FindProperty(nameof(m_target.OtherLicenseUrl));
+
+ m_LicenseType = serializedObject.FindProperty(nameof(m_target.LicenseType));
+ m_OtherLicenseUrl = serializedObject.FindProperty(nameof(m_target.OtherLicenseUrl));
+ }
+
+ enum MessageKeys
+ {
+ [LangMsg(Languages.ja, "アバターの人格に関する許諾範囲")]
+ [LangMsg(Languages.en, "Personation / Characterization Permission")]
+ PERSONATION,
+
+ [LangMsg(Languages.ja, "アバターに人格を与えることの許諾範囲")]
+ [LangMsg(Languages.en, "A person who can perform with this avatar")]
+ ALLOWED_USER,
+
+ [LangMsg(Languages.ja, "このアバターを用いて暴力表現を演じることの許可")]
+ [LangMsg(Languages.en, "Violent acts using this avatar")]
+ VIOLENT_USAGE,
+
+ [LangMsg(Languages.ja, "このアバターを用いて性的表現を演じることの許可")]
+ [LangMsg(Languages.en, "Sexuality acts using this avatar")]
+ SEXUAL_USAGE,
+
+ [LangMsg(Languages.ja, "商用利用の許可")]
+ [LangMsg(Languages.en, "For commercial use")]
+ COMMERCIAL_USAGE,
+
+ [LangMsg(Languages.ja, "再配布・改変に関する許諾範囲")]
+ [LangMsg(Languages.en, "Redistribution / Modifications License")]
+ REDISTRIBUTION_MODIFICATIONS,
+
+ // [LangMsg(Languages.ja, "")]
+ // [LangMsg(Languages.en, "")]
+ }
+
+ static string Msg(MessageKeys key)
+ {
+ return M17N.Getter.Msg(key);
+ }
+
+ bool m_foldoutInfo = true;
+ bool m_foldoutPermission = true;
+ bool m_foldoutDistribution = true;
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ if (VRMVersion.IsNewer(m_exporterVersion.stringValue))
+ {
+ EditorGUILayout.HelpBox("Check UniVRM new version. https://github.com/dwango/UniVRM/releases", MessageType.Warning);
+ }
+
+ // texture
+ EditorGUILayout.BeginHorizontal();
+ {
+ EditorGUILayout.BeginVertical();
+ GUI.enabled = false;
+ EditorGUILayout.PropertyField(m_exporterVersion);
+ GUI.enabled = true;
+ EditorGUILayout.PropertyField(m_thumbnail);
+ EditorGUILayout.EndVertical();
+ m_thumbnail.objectReferenceValue = TextureField("", (Texture2D)m_thumbnail.objectReferenceValue, 100);
+ }
+ EditorGUILayout.EndHorizontal();
+
+ m_foldoutInfo = EditorGUILayout.Foldout(m_foldoutInfo, "Information");
+ if (m_foldoutInfo)
+ {
+ m_title.OnGUI();
+ m_version.OnGUI();
+ m_author.OnGUI();
+ m_contact.OnGUI();
+ m_reference.OnGUI();
+ }
+ // EditorGUILayout.LabelField("License ", EditorStyles.boldLabel);
+ m_foldoutPermission = EditorGUILayout.Foldout(m_foldoutPermission, Msg(MessageKeys.PERSONATION));
+ if (m_foldoutPermission)
+ {
+ var backup = EditorGUIUtility.labelWidth;
+ RightFixedPropField(m_AllowedUser, Msg(MessageKeys.ALLOWED_USER));
+ RightFixedPropField(m_ViolentUssage, Msg(MessageKeys.VIOLENT_USAGE));
+ RightFixedPropField(m_SexualUssage, Msg(MessageKeys.SEXUAL_USAGE));
+ RightFixedPropField(m_CommercialUssage, Msg(MessageKeys.COMMERCIAL_USAGE));
+ EditorGUILayout.PropertyField(m_OtherPermissionUrl, new GUIContent("Other License Url"));
+ EditorGUIUtility.labelWidth = backup;
+ }
+
+ m_foldoutDistribution = EditorGUILayout.Foldout(m_foldoutDistribution, Msg(MessageKeys.REDISTRIBUTION_MODIFICATIONS));
+ if (m_foldoutDistribution)
+ {
+ var licenseType = m_LicenseType;
+ EditorGUILayout.PropertyField(licenseType);
+ if ((LicenseType)licenseType.intValue == LicenseType.Other)
+ {
+ EditorGUILayout.PropertyField(m_OtherLicenseUrl);
+ }
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ static (Rect, Rect) FixedRight(Rect r, int width)
+ {
+ if (width > r.width)
+ {
+ width = (int)r.width;
+ }
+ return (
+ new Rect(r.x, r.y, r.width - width, r.height),
+ new Rect(r.x + r.width - width, r.y, width, r.height)
+ );
+ }
+
+ static void RightFixedPropField(SerializedProperty prop, string label)
+ {
+ var r = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(EditorGUIUtility.singleLineHeight));
+ var (left, right) = FixedRight(r, 64);
+ // Debug.Log($"{left}, {right}");
+ EditorGUI.LabelField(left, label);
+ EditorGUI.PropertyField(right, prop, new GUIContent(""), false);
+ }
+
+ 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/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Meta/VRMMetaObjectEditor.cs.meta
new file mode 100644
index 00000000..b14c584e
--- /dev/null
+++ b/Assets/ThirdParty/VRM/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:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility.meta
new file mode 100644
index 00000000..45dc7e07
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 19a2ce0e7783c634691240378ed54c47
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs
new file mode 100644
index 00000000..9eb266a1
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UniGLTF;
+using UnityEditor;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+
+namespace VRM
+{
+ /// <summary>
+ /// 複数のメッシュをまとめて、BlendShapeClipに変更を反映する
+ /// </summary>
+ [DisallowMultipleComponent]
+ public static class MeshIntegratorEditor
+ {
+ const string MENU_KEY = "Mesh Utility/MeshIntegrator";
+ const string ASSET_SUFFIX = ".mesh.asset";
+
+ [MenuItem(MENU_KEY, true)]
+ private static bool ExportValidate()
+ {
+ return Selection.activeObject != null &&
+ Selection.activeObject is GameObject &&
+ SkinnedMeshUtility.IsPrefab(Selection.activeObject);
+ }
+
+ [MenuItem(MENU_KEY, priority = 1)]
+ private static void ExportFromMenu()
+ {
+ var go = Selection.activeObject as GameObject;
+
+ Integrate(go);
+ }
+
+ //[MenuItem("Assets/fooa", false)]
+ //private static void Foo()
+ //{
+ // var go = Selection.activeObject as GameObject;
+
+ // Debug.Log(SkinnedMeshUtility.IsPrefab(go));
+ //}
+
+
+ public static List<MeshUtility.MeshIntegrationResult> Integrate(GameObject prefab)
+ {
+ Undo.RecordObject(prefab, "Mesh Integration");
+ var instance = SkinnedMeshUtility.InstantiatePrefab(prefab);
+
+
+ var clips = new List<BlendShapeClip>();
+ var proxy = instance.GetComponent<VRMBlendShapeProxy>();
+ if (proxy != null && proxy.BlendShapeAvatar != null)
+ {
+ clips = proxy.BlendShapeAvatar.Clips;
+ }
+ foreach (var clip in clips)
+ {
+ Undo.RecordObject(clip, "Mesh Integration");
+ }
+
+ // Backup Exists
+ BackupVrmPrefab(prefab);
+
+ // Execute
+ var results = VRMMeshIntegratorUtility.Integrate(instance, clips);
+
+ foreach (var res in results)
+ {
+ if (res.IntegratedRenderer == null) continue;
+
+ SaveMeshAsset(res.IntegratedRenderer.sharedMesh, instance, res.IntegratedRenderer.gameObject.name);
+ Undo.RegisterCreatedObjectUndo(res.IntegratedRenderer.gameObject, "Integrate Renderers");
+ }
+
+ // destroy source renderers
+ foreach (var res in results)
+ {
+ foreach (var renderer in res.SourceSkinnedMeshRenderers)
+ {
+ Undo.RecordObject(renderer.gameObject, "Deactivate old renderer");
+ renderer.gameObject.SetActive(false);
+ }
+
+ foreach (var renderer in res.SourceMeshRenderers)
+ {
+ Undo.RecordObject(renderer.gameObject, "Deactivate old renderer");
+ renderer.gameObject.SetActive(false);
+ }
+ }
+
+ // Apply to Prefab
+ SkinnedMeshUtility.ApplyChangesToPrefab(instance);
+ Object.DestroyImmediate(instance);
+
+ return results;
+ }
+
+ private static void BackupVrmPrefab(GameObject rootPrefab)
+ {
+ var proxy = rootPrefab.GetComponent<VRMBlendShapeProxy>();
+
+ var srcAvatar = proxy.BlendShapeAvatar;
+ var dstAvatar = (BlendShapeAvatar)BackupAsset(srcAvatar, rootPrefab);
+
+ var clipMapper = srcAvatar.Clips.ToDictionary(x => x, x => (BlendShapeClip)BackupAsset(x, rootPrefab));
+ dstAvatar.Clips = clipMapper.Values.ToList();
+
+ var dstPrefab = BackupAsset(rootPrefab, rootPrefab);
+ var dstInstance = SkinnedMeshUtility.InstantiatePrefab(dstPrefab);
+ dstInstance.GetComponent<VRMBlendShapeProxy>().BlendShapeAvatar = dstAvatar;
+ SkinnedMeshUtility.ApplyChangesToPrefab(dstInstance);
+ Object.DestroyImmediate(dstInstance);
+ }
+
+ private static T BackupAsset<T>(T asset, GameObject rootPrefab) where T : UnityEngine.Object
+ {
+ var srcAssetPath = UnityPath.FromAsset(asset);
+ var assetName = srcAssetPath.FileName;
+
+ var backupDir = "MeshIntegratorBackup";
+ var backupPath = UnityPath.FromAsset(rootPrefab).Parent.Child(backupDir);
+ backupPath.EnsureFolder();
+ var dstAssetPath = backupPath.Child(assetName);
+
+ AssetDatabase.CopyAsset(srcAssetPath.Value, dstAssetPath.Value);
+ return dstAssetPath.LoadAsset<T>();
+ }
+
+ private static string GetRootPrefabPath(GameObject go)
+ {
+ var prefab = SkinnedMeshUtility.IsPrefab(go) ? go : SkinnedMeshUtility.GetPrefab(go);
+ var assetPath = "";
+ if (prefab != null)
+ {
+ var prefabPath = AssetDatabase.GetAssetPath(prefab);
+ assetPath = string.Format("{0}/", Path.GetDirectoryName(prefabPath));
+ }
+ else
+ {
+ assetPath = string.Format("Assets/");
+ }
+ assetPath = assetPath.Replace(@"\", @"/");
+ return assetPath;
+ }
+
+ private static void SaveMeshAsset(Mesh mesh, GameObject go, string name)
+ {
+ var assetPath = Path.Combine(GetRootPrefabPath(go), string.Format("{0}{1}",
+ name,
+ ASSET_SUFFIX
+ ));
+
+ Debug.LogFormat("CreateAsset: {0}", assetPath);
+ AssetDatabase.CreateAsset(mesh, assetPath);
+ }
+
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs.meta
new file mode 100644
index 00000000..f87becc3
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorEditor.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 648920943c26bde4b920119bdbb38f70
+timeCreated: 1518194696
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs
new file mode 100644
index 00000000..f931b281
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs
@@ -0,0 +1,170 @@
+#pragma warning disable 0414, 0649
+using UnityEditor;
+using UnityEngine;
+using System.Linq;
+using System;
+using System.Collections.Generic;
+
+
+namespace VRM
+{
+ public class MeshIntegratorWizard : ScriptableWizard
+ {
+ const string MENU_KEY = "Assets/UnityEditorScripts/MeshIntegratorWizard";
+
+ [SerializeField]
+ GameObject m_root;
+
+ [SerializeField]
+ Material[] m_uniqueMaterials;
+
+ [Serializable]
+ struct MaterialKey
+ {
+ public string Shader;
+ public KeyValuePair<string, object>[] Properties;
+
+ public override bool Equals(object obj)
+ {
+ if(!(obj is MaterialKey))
+ {
+ return base.Equals(obj);
+ }
+
+ var key = (MaterialKey)obj;
+
+ return Shader == key.Shader
+ && Properties.SequenceEqual(key.Properties)
+ ;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+ }
+
+ [Serializable]
+ struct MaterialList
+ {
+ public Material[] Materials;
+
+ public MaterialList(Material[] list)
+ {
+ Materials = list;
+ }
+ }
+ [SerializeField]
+ MaterialList[] m_duplicateMaterials;
+
+ [Header("Result")]
+ public MeshUtility.MeshIntegrationResult[] integrationResults;
+
+ [MenuItem(MENU_KEY)]
+ static void CreateWizard()
+ {
+ ScriptableWizard.DisplayWizard<MeshIntegratorWizard>("MeshIntegrator", "Integrate and close window", "Integrate");
+ }
+
+ private void OnEnable()
+ {
+ m_root = Selection.activeGameObject;
+ OnValidate();
+ }
+
+ static object GetPropertyValue(Shader shader, int i, Material m)
+ {
+ var propType = ShaderUtil.GetPropertyType(shader, i);
+ switch (propType)
+ {
+ case ShaderUtil.ShaderPropertyType.Color:
+ return m.GetColor(ShaderUtil.GetPropertyName(shader, i));
+
+ case ShaderUtil.ShaderPropertyType.Range:
+ case ShaderUtil.ShaderPropertyType.Float:
+ return m.GetFloat(ShaderUtil.GetPropertyName(shader, i));
+
+ case ShaderUtil.ShaderPropertyType.Vector:
+ return m.GetVector(ShaderUtil.GetPropertyName(shader, i));
+
+ case ShaderUtil.ShaderPropertyType.TexEnv:
+ return m.GetTexture(ShaderUtil.GetPropertyName(shader, i));
+
+ default:
+ throw new NotImplementedException(propType.ToString());
+ }
+ }
+
+ static MaterialKey GetMaterialKey(Material m)
+ {
+ var key = new MaterialKey
+ {
+ Shader = m.shader.name,
+ };
+
+ key.Properties = Enumerable.Range(0, ShaderUtil.GetPropertyCount(m.shader))
+ .Select(x => new KeyValuePair<string, object>(
+ ShaderUtil.GetPropertyName(m.shader, x),
+ GetPropertyValue(m.shader, x, m))
+ )
+ .OrderBy(x => x.Key)
+ .ToArray()
+ ;
+
+ return key;
+ }
+
+ void OnValidate()
+ {
+ Debug.Log("OnValidate");
+ if (m_root == null)
+ {
+ m_uniqueMaterials = new Material[] { };
+ m_duplicateMaterials = new MaterialList[] { };
+ return;
+ }
+
+ m_uniqueMaterials = MeshUtility.MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, false)
+ .SelectMany(x => x.sharedMaterials)
+ .Distinct()
+ .ToArray();
+
+ m_duplicateMaterials = m_uniqueMaterials
+ .GroupBy(x => GetMaterialKey(x), x => x)
+ .Select(x => new MaterialList(x.ToArray()))
+ .Where(x => x.Materials.Length > 1)
+ .ToArray()
+ ;
+ }
+
+ void OnWizardUpdate()
+ {
+ helpString = "select target mesh root";
+ }
+
+ void Integrate()
+ {
+ if (m_root == null)
+ {
+ Debug.LogWarning("no root object");
+ return;
+ }
+
+ integrationResults = MeshIntegratorEditor.Integrate(m_root).ToArray();
+ }
+
+ void OnWizardCreate()
+ {
+ Debug.Log("OnWizardCreate");
+ Integrate();
+
+ // close
+ }
+
+ void OnWizardOtherButton()
+ {
+ Debug.Log("OnWizardOtherButton");
+ Integrate();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs.meta
new file mode 100644
index 00000000..19c7bdf9
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/MeshIntegratorWizard.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 744f66c7013807b4cae1d38d2ecfff38
+timeCreated: 1518503829
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs
new file mode 100644
index 00000000..20c05a31
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs
@@ -0,0 +1,59 @@
+using System;
+using UnityEditor;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+namespace VRM
+{
+ public static class SkinnedMeshUtility
+ {
+ public const string MENU_KEY = "GameObject/UnityEditorScripts/";
+ public const int MENU_PRIORITY = 11;
+
+ public static Object GetPrefab(GameObject instance)
+ {
+#if UNITY_2018_2_OR_NEWER
+ return PrefabUtility.GetCorrespondingObjectFromSource(instance);
+#else
+ return PrefabUtility.GetPrefabParent(go);
+#endif
+ }
+
+ public static bool IsPrefab(Object instance)
+ {
+ if (instance == null)
+ {
+ return false;
+ }
+ if (PrefabUtility.GetPrefabAssetType(instance) != PrefabAssetType.Regular)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public static void ApplyChangesToPrefab(GameObject instance)
+ {
+ var prefab = GetPrefab(instance);
+ if (prefab == null)
+ {
+ return;
+ }
+
+ var path = AssetDatabase.GetAssetPath(prefab);
+ if (string.IsNullOrEmpty(path))
+ {
+ return;
+ }
+
+ PrefabUtility.SaveAsPrefabAssetAndConnect(instance, path, InteractionMode.AutomatedAction);
+ }
+
+ public static GameObject InstantiatePrefab(GameObject prefab)
+ {
+ if (!IsPrefab(prefab)) return null;
+
+ return (GameObject)PrefabUtility.InstantiatePrefab(prefab);
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs.meta
new file mode 100644
index 00000000..6cda243d
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SkinnedMeshUtility/SkinnedMeshUtility.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: d66a91f4b51e42a6b165fe39fdb48d64
+timeCreated: 1559541677 \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone.meta
new file mode 100644
index 00000000..82f0a5ba
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 89f2a80d3b112504eaa38231bd7e1a71
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs
new file mode 100644
index 00000000..89f5192f
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs
@@ -0,0 +1,81 @@
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace VRM
+{
+ [CustomEditor(typeof(VRMSpringBoneColliderGroup))]
+ public class VRMSpringBoneColliderGroupEditor : Editor
+ {
+ VRMSpringBoneColliderGroup m_target;
+
+ private void OnEnable()
+ {
+ m_target = (VRMSpringBoneColliderGroup)target;
+ }
+
+ private void OnSceneGUI()
+ {
+ Undo.RecordObject(m_target, "VRMSpringBoneColliderGroupEditor");
+
+ Handles.matrix = m_target.transform.localToWorldMatrix;
+ Gizmos.color = Color.green;
+
+ bool changed = false;
+
+ foreach (var x in m_target.Colliders)
+ {
+ var offset = Handles.PositionHandle(x.Offset, Quaternion.identity);
+ if (offset != x.Offset)
+ {
+ changed = true;
+ x.Offset = offset;
+ }
+ }
+
+ if (changed)
+ {
+ EditorUtility.SetDirty(m_target);
+ }
+ }
+
+ [MenuItem("CONTEXT/VRMSpringBoneColliderGroup/X Mirror")]
+ private static void InvertOffsetX(MenuCommand command)
+ {
+ var target = command.context as VRMSpringBoneColliderGroup;
+ if (target == null) return;
+
+ Undo.RecordObject(target, "X Mirror");
+
+ foreach (var sphereCollider in target.Colliders)
+ {
+ var offset = sphereCollider.Offset;
+ offset.x *= -1f;
+ sphereCollider.Offset = offset;
+ }
+ }
+
+ [MenuItem("CONTEXT/VRMSpringBoneColliderGroup/Sort Colliders by Radius")]
+ private static void SortByRadius(MenuCommand command)
+ {
+ var target = command.context as VRMSpringBoneColliderGroup;
+ if (target == null) return;
+
+ Undo.RecordObject(target, "Sort Colliders by Radius");
+
+ target.Colliders = target.Colliders.OrderBy(x => -x.Radius).ToArray();
+ }
+
+ [MenuItem("CONTEXT/VRMSpringBoneColliderGroup/Sort Colliders by Offset Y")]
+ private static void SortByOffsetY(MenuCommand command)
+ {
+ var target = command.context as VRMSpringBoneColliderGroup;
+ if (target == null) return;
+
+ Undo.RecordObject(target, "Sort Colliders by Offset Y");
+
+ target.Colliders = target.Colliders.OrderBy(x => -x.Offset.y).ToArray();
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta
new file mode 100644
index 00000000..dab2d9cd
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 1f8b723890cc43042b070277cb3b73ef
+timeCreated: 1519735770
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs
new file mode 100644
index 00000000..ee6968c9
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs
@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace VRM
+{
+ static class VRMSpringBoneValidator
+ {
+ // https://github.com/vrm-c/UniVRM/issues/474
+ public static IEnumerable<Validation> Validate(GameObject root)
+ {
+ if (root == null)
+ {
+ yield break;
+ }
+
+ var hierarchy = root.GetComponentsInChildren<Transform>(true);
+
+ Dictionary<Transform, List<VRMSpringBone>> rootMap = new Dictionary<Transform, List<VRMSpringBone>>();
+
+ foreach (var sb in root.GetComponentsInChildren<VRMSpringBone>())
+ {
+ for (int i = 0; i < sb.RootBones.Count; ++i)
+ {
+ var springRoot = sb.RootBones[i];
+ if (springRoot == null)
+ {
+ yield return Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is null");
+ continue;
+ }
+ if (!hierarchy.Contains(springRoot))
+ {
+ yield return Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is out of hierarchy");
+ continue;
+ }
+ if (!springRoot.transform.EnableForExport())
+ {
+ yield return Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is not active");
+ continue;
+ }
+
+ if (!rootMap.TryGetValue(springRoot, out List<VRMSpringBone> list))
+ {
+ list = new List<VRMSpringBone>();
+ rootMap.Add(springRoot, list);
+ }
+ list.Add(sb);
+ }
+
+ for (int i = 0; i < sb.ColliderGroups.Length; ++i)
+ {
+ var c = sb.ColliderGroups[i];
+ if (c == null)
+ {
+ yield return Validation.Error($"{sb.name}.ColliderGroups[{i}] is null");
+ continue;
+ }
+ if (!hierarchy.Contains(c.transform))
+ {
+ yield return Validation.Error($"{sb.name}.ColliderGroups[{i}] is out of hierarchy");
+ continue;
+ }
+ }
+ }
+
+ foreach (var kv in rootMap)
+ {
+ if (kv.Value.Count > 1)
+ {
+ // * GameObjectが複数回ルートとして指定されてる(SpringBoneが同じでも別でも)
+ var list = string.Join(", ", kv.Value.Select(x => string.IsNullOrEmpty(x.m_comment) ? x.name : x.m_comment));
+ yield return Validation.Warning($"{kv.Key} found multiple. {list}");
+ }
+
+ var rootInRootMap = new Dictionary<Transform, List<Transform>>();
+ foreach (var child in kv.Key.GetComponentsInChildren<Transform>())
+ {
+ // * Rootから子をだどって、別のRootが現れる
+ if (child == kv.Key)
+ {
+ continue;
+ }
+
+ if (!rootMap.ContainsKey(child))
+ {
+ continue;
+ }
+
+ if (!rootInRootMap.TryGetValue(kv.Key, out List<Transform> rootInRoot))
+ {
+ rootInRoot = new List<Transform>();
+ rootInRootMap.Add(kv.Key, rootInRoot);
+ }
+ rootInRoot.Add(child);
+ }
+ foreach (var rootList in rootInRootMap)
+ {
+ var list = string.Join(", ", rootList.Value.Select(x => x.name));
+ yield return Validation.Warning($"{rootList.Key} hierarchy contains other root: {list}");
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta
new file mode 100644
index 00000000..1a9e37b0
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dc588ccd280c0e2429d1d3cc3db5d950
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/TabBar.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/TabBar.cs
new file mode 100644
index 00000000..76a1c748
--- /dev/null
+++ b/Assets/ThirdParty/VRM/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 t, GUIStyle buttonStyle, GUI.ToolbarButtonSize buttonSize) where T : Enum
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ // タブを描画する
+ var value = GUILayout.Toolbar((int)(object)t, TabCache<T>.TabToggles, buttonStyle, buttonSize);
+ GUILayout.FlexibleSpace();
+ return (T)(object)value;
+ }
+ }
+ static class TabCache<T> 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/ThirdParty/VRM/VRM/UniVRM/Editor/TabBar.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/TabBar.cs.meta
new file mode 100644
index 00000000..ee1d88d4
--- /dev/null
+++ b/Assets/ThirdParty/VRM/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/ThirdParty/VRM/VRM/UniVRM/Editor/Tests.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests.meta
new file mode 100644
index 00000000..9acbf73c
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 0903c33afe5ef6346ad9526e78055293
+folderAsset: yes
+timeCreated: 1521799604
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs
new file mode 100644
index 00000000..7d5b4d6d
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+using UnityEngine;
+
+
+namespace VRM
+{
+ public class EnumUtilTest
+ {
+ [Test]
+ public void EnumUtilTestSimplePasses()
+ {
+ Assert.AreEqual(default(HumanBodyBones), EnumUtil.TryParseOrDefault<HumanBodyBones>("xxx"));
+
+#if UNITY_5_6_OR_NEWER
+ Assert.AreEqual(HumanBodyBones.UpperChest, EnumUtil.TryParseOrDefault<HumanBodyBones>("upperchest"));
+#else
+ Assert.AreEqual(default(HumanBodyBones), EnumUtil.TryParseOrDefault<HumanBodyBones>("upperchest"));
+#endif
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs.meta
new file mode 100644
index 00000000..7082ee76
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/EnumUtilTest.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: d4a5f44a23cb4884b9667b622f262497
+timeCreated: 1523088686
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs
new file mode 100644
index 00000000..003027f5
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs
@@ -0,0 +1,48 @@
+using NUnit.Framework;
+using System.Linq;
+using System.IO;
+
+namespace VRM
+{
+ public class InvalidFileNameTest
+ {
+ [Test]
+ [TestCase("VRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMV", true)]
+ [TestCase("VRMFormatVRMFormatVRMFormatVRMFormatVRMFormatVRMFormatVRMFormat", false)]
+ [TestCase("UniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRM", true)]
+ [TestCase("UniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniV", false)]
+ [TestCase("AliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAlicia", true)]
+ public void DetectFileNameLength(string fileName, bool isIllegal)
+ {
+ var result = VRMExporterValidator.IsFileNameLengthTooLong(fileName);
+ Assert.AreEqual(result, isIllegal);
+ }
+
+ [Test]
+ [TestCase("\u0000\u0042\u0062", true)]
+ [TestCase("\u0045\u0046\u0047\u0065\u0068\u0036", false)]
+ [TestCase("\u0043\u0045\u0047\u007F", true)]
+ [TestCase("\u0000\u0042\u0062", true)]
+ [TestCase("\u003A\u0039\u005C\u0060\u0074", false)]
+ [TestCase("\u005D\u006F\u001C\u007A\u0036\u0049", true)]
+ public void DetectControlCharacters(string fileName, bool isIllegal)
+ {
+ var result = fileName.Any(x => char.IsControl(x));
+ Assert.AreEqual(result, isIllegal);
+ }
+
+ [Test]
+ [TestCase("VRM|Alicia?VRM", true)]
+ [TestCase("UniVRMUniVRM:UniVRM", true)]
+ [TestCase("VRMIsVRFileFormat", false)]
+ [TestCase("Alicia<Alicia>Alicia", true)]
+ [TestCase("UniVRMIsVRMImplementationInUnityPlatform", false)]
+ [TestCase("Avator*Avator/Avator", true)]
+ public void DetectInvalidCharacters(string fileName, bool isIllegal)
+ {
+ char[] invalidPathChars = Path.GetInvalidFileNameChars();
+ var result = fileName.Any(x => invalidPathChars.Contains(x));
+ Assert.AreEqual(result, isIllegal);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta
new file mode 100644
index 00000000..aa5f1146
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8511ed091b59bca4da4fd280693b7c82
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs
new file mode 100644
index 00000000..6be712b0
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs
@@ -0,0 +1,63 @@
+using NUnit.Framework;
+using UnityEngine;
+using System.Linq;
+
+namespace VRM
+{
+ public class MeshTests
+ {
+ public static void MeshEquals(Mesh l, Mesh r)
+ {
+#if UNITY_2017_3_OR_NEWER
+ Assert.AreEqual(l.indexFormat, r.indexFormat);
+#endif
+ Assert.True(l.vertices.SequenceEqual(r.vertices));
+ Assert.True(l.normals.SequenceEqual(r.normals));
+ Assert.True(l.tangents.SequenceEqual(r.tangents));
+ Assert.True(l.uv.SequenceEqual(r.uv));
+ Assert.True(l.uv2.SequenceEqual(r.uv2));
+ Assert.True(l.uv3.SequenceEqual(r.uv3));
+ Assert.True(l.uv4.SequenceEqual(r.uv4));
+ Assert.True(l.colors.SequenceEqual(r.colors));
+ Assert.True(l.boneWeights.SequenceEqual(r.boneWeights));
+ Assert.True(l.bindposes.SequenceEqual(r.bindposes));
+
+ Assert.AreEqual(l.subMeshCount, r.subMeshCount);
+ for (int i = 0; i < l.subMeshCount; ++i)
+ {
+ Assert.True(l.GetIndices(i).SequenceEqual(r.GetIndices(i)));
+ }
+
+ Assert.AreEqual(l.blendShapeCount, r.blendShapeCount);
+ for (int i = 0; i < l.blendShapeCount; ++i)
+ {
+ Assert.AreEqual(l.GetBlendShapeName(i), r.GetBlendShapeName(i));
+ Assert.AreEqual(l.GetBlendShapeFrameCount(i), r.GetBlendShapeFrameCount(i));
+ Assert.AreEqual(l.GetBlendShapeFrameWeight(i, 0), r.GetBlendShapeFrameWeight(i, 0));
+
+ var lv = l.vertices;
+ var ln = l.vertices;
+ var lt = l.vertices;
+ var rv = r.vertices;
+ var rn = r.vertices;
+ var rt = r.vertices;
+ l.GetBlendShapeFrameVertices(i, 0, lv, ln, lt);
+ r.GetBlendShapeFrameVertices(i, 0, rv, rn, rt);
+ Assert.True(lv.SequenceEqual(rv));
+ Assert.True(ln.SequenceEqual(rn));
+ Assert.True(lt.SequenceEqual(rt));
+ }
+ }
+
+ [Test]
+ public void MeshCopyTest()
+ {
+ var src = new Mesh();
+ src.AddBlendShapeFrame("blendShape", 100.0f, null, null, null);
+
+ var dst = MeshUtility.MeshExtensions.Copy(src, true);
+
+ MeshEquals(src, dst);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs.meta
new file mode 100644
index 00000000..2b91dd68
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/MeshTests.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 0fa06e6e83b995a49a1357924b9ef23e
+timeCreated: 1533274120
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs
new file mode 100644
index 00000000..1126b647
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs
@@ -0,0 +1,89 @@
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using UnityEngine;
+
+namespace VRM
+{
+ public class NormalizeTests
+ {
+ class BoneMap
+ {
+ public List<Transform> SrcBones = new List<Transform>();
+ public List<Transform> DstBones = new List<Transform>();
+ public Dictionary<Transform, Transform> Map = new Dictionary<Transform, Transform>();
+
+ public void Add(GameObject src, GameObject dst)
+ {
+ SrcBones.Add(src?.transform);
+ if (dst != null)
+ {
+ DstBones.Add(dst.transform);
+ }
+ if (src != null)
+ {
+ Map.Add(src?.transform, dst?.transform);
+ }
+ }
+
+ public IEnumerable<BoneWeight> CreateBoneWeight(int vertexCount)
+ {
+ int j = 0;
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ yield return new BoneWeight
+ {
+ boneIndex0 = j++,
+ boneIndex1 = j++,
+ boneIndex2 = j++,
+ boneIndex3 = j++,
+ weight0 = 0.25f,
+ weight1 = 0.25f,
+ weight2 = 0.25f,
+ weight3 = 0.25f,
+ };
+ }
+ }
+ }
+
+ [Test]
+ public void MapBoneWeightTest()
+ {
+ {
+ var map = new BoneMap();
+ map.Add(new GameObject("a"), new GameObject("A"));
+ map.Add(new GameObject("b"), new GameObject("B"));
+ map.Add(new GameObject("c"), new GameObject("C"));
+ map.Add(new GameObject("d"), new GameObject("D"));
+ map.Add(null, new GameObject("null"));
+ // map.Add(new GameObject("c"), null); // ありえないので Exception にしてある
+ var boneWeights = map.CreateBoneWeight(64).ToArray();
+ var newBoneWeight = MeshUtility.BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
+ map.SrcBones.ToArray(), map.DstBones.ToArray());
+
+ // 正常系
+ // exception が出なければよい
+ }
+
+ {
+ var map = new BoneMap();
+ map.Add(new GameObject("a"), new GameObject("A"));
+ map.Add(new GameObject("b"), new GameObject("B"));
+ map.Add(new GameObject("c"), new GameObject("C"));
+ map.Add(new GameObject("d"), new GameObject("D"));
+ map.Add(null, new GameObject("null"));
+ // map.Add(new GameObject("c"), null); // ありえないので Exception にしてある
+ var boneWeights = map.CreateBoneWeight(64).ToArray();
+ var newBoneWeight = MeshUtility.BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
+ map.SrcBones.ToArray(), map.DstBones.ToArray());
+
+ // 4 つめが 0 になる
+ Assert.AreEqual(0, newBoneWeight[1].boneIndex0);
+ Assert.AreEqual(0, newBoneWeight[1].weight0);
+ // 5 つめ以降が 0 になる。out of range
+ Assert.AreEqual(0, newBoneWeight[1].boneIndex1);
+ Assert.AreEqual(0, newBoneWeight[1].weight1);
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs.meta
new file mode 100644
index 00000000..b3822bbd
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/NormalizeTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4014814a6ed5bf84faa36c7b99d6d4b8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef
new file mode 100644
index 00000000..d10983bc
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef
@@ -0,0 +1,21 @@
+{
+ "name": "UniVRM.Editor.Tests",
+ "references": [
+ "VRM",
+ "UniJSON",
+ "UniVRM.Editor",
+ "MeshUtility"
+ ],
+ "optionalUnityReferences": [
+ "TestAssemblies"
+ ],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": []
+} \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef.meta
new file mode 100644
index 00000000..704a813b
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: ea02711c84d022f46bcbae5d1cd38c16
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs
new file mode 100644
index 00000000..8b1caac2
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs
@@ -0,0 +1,687 @@
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UniJSON;
+using UnityEngine;
+
+namespace VRM
+{
+ public class UniVRMSerializeTests
+ {
+ [Test]
+ public void MaterialValueBindTest()
+ {
+ var model = new glTF_VRM_MaterialValueBind();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_MaterialValueBind>().Serialize(model, c);
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void BlendShapeBindTest()
+ {
+ var model = new glTF_VRM_BlendShapeBind()
+ {
+ mesh = 1,
+ weight = 2,
+ index = 3,
+ };
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""mesh"":1,""index"":3,""weight"":2}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_BlendShapeBind>().Serialize(model, c);
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void BlendShapeBindTestError()
+ {
+ var model = new glTF_VRM_BlendShapeBind();
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_BlendShapeBind>().Serialize(model, c)
+ );
+ Assert.AreEqual("[mesh.String] minimum: ! -1>=0", ex.Message);
+ }
+
+ [Test]
+ public void BlendShapeGroupTest()
+ {
+ var model = new glTF_VRM_BlendShapeGroup()
+ {
+ presetName = "neutral",
+ };
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""presetName"":""neutral"",""isBinary"":false,""binds"":[],""materialValues"":[]}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_BlendShapeGroup>().Serialize(model, c);
+ Assert.AreEqual(@"{""presetName"":""neutral"",""binds"":[],""materialValues"":[],""isBinary"":false}", json2);
+ }
+
+ [Test]
+ public void BlendShapeGroupTestError()
+ {
+ var model = new glTF_VRM_BlendShapeGroup()
+ {
+ presetName = "aaaaaaaaaaaa_not_exists_",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_BlendShapeGroup>().Serialize(model, c)
+ );
+ Assert.AreEqual("[presetName.String] aaaaaaaaaaaa_not_exists_ is not valid enum", ex.Message);
+ }
+
+ [Test]
+ public void DegreeMapTest()
+ {
+ var model = new glTF_VRM_DegreeMap();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""xRange"":90,""yRange"":10}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_DegreeMap>().Serialize(model, c);
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void MeshAnnotationTest()
+ {
+ var model = new glTF_VRM_MeshAnnotation();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""mesh"":0}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_MeshAnnotation>().Serialize(model, c);
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void FirstPersonTest()
+ {
+ var model = new glTF_VRM_Firstperson();
+
+ var json = model.ToJson();
+ Assert.AreEqual(
+ @"{""firstPersonBone"":-1,""firstPersonBoneOffset"":{""x"":0,""y"":0,""z"":0},""meshAnnotations"":[],""lookAtTypeName"":""Bone"",""lookAtHorizontalInner"":{""xRange"":90,""yRange"":10},""lookAtHorizontalOuter"":{""xRange"":90,""yRange"":10},""lookAtVerticalDown"":{""xRange"":90,""yRange"":10},""lookAtVerticalUp"":{""xRange"":90,""yRange"":10}}",
+ json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_Firstperson>().Serialize(model, c);
+ Assert.AreEqual(
+ @"{""firstPersonBoneOffset"":{""x"":0,""y"":0,""z"":0},""meshAnnotations"":[],""lookAtTypeName"":""Bone"",""lookAtHorizontalInner"":{""xRange"":90,""yRange"":10},""lookAtHorizontalOuter"":{""xRange"":90,""yRange"":10},""lookAtVerticalDown"":{""xRange"":90,""yRange"":10},""lookAtVerticalUp"":{""xRange"":90,""yRange"":10}}",
+ json2);
+ }
+
+ [Test]
+ public void HumanoidBoneTest()
+ {
+ var model = new glTF_VRM_HumanoidBone()
+ {
+ bone = "hips", // NOTE: This field must not be null?
+ node = 0,
+ };
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""bone"":""hips"",""node"":0,""useDefaultValues"":true}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_HumanoidBone>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(
+ @"{""bone"":""hips"",""node"":0,""useDefaultValues"":true,""min"":{""x"":0,""y"":0,""z"":0},""max"":{""x"":0,""y"":0,""z"":0},""center"":{""x"":0,""y"":0,""z"":0},""axisLength"":0}",
+ json2);
+ }
+
+ [Test]
+ public void HumanoidBoneTestError()
+ {
+ var model = new glTF_VRM_HumanoidBone()
+ {
+ bone = "hips", // NOTE: This field must not be null?
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_HumanoidBone>().Serialize(model, c)
+ );
+ Assert.AreEqual("[node.String] minimum: ! -1>=0", ex.Message);
+ }
+
+ [Test]
+ public void HumanoidTest()
+ {
+ var model = new glTF_VRM_Humanoid();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""humanBones"":[],""armStretch"":0.05,""legStretch"":0.05,""upperArmTwist"":0.5,""lowerArmTwist"":0.5,""upperLegTwist"":0.5,""lowerLegTwist"":0.5,""feetSpacing"":0,""hasTranslationDoF"":false}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_Humanoid>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void MaterialTest()
+ {
+ var model = new glTF_VRM_Material
+ {
+ floatProperties = new Dictionary<string, float>
+ {
+ {"float", 1.0f}
+ },
+ vectorProperties = new Dictionary<string, float[]>
+ {
+ {"vector", new float[]{0, 1, 2, 3 }}
+ },
+ textureProperties = new Dictionary<string, int>
+ {
+ {"texture", 0}
+ },
+ keywordMap = new Dictionary<string, bool>
+ {
+ {"keyword", true}
+ },
+ tagMap = new Dictionary<string, string>
+ {
+ {"tag", "map"}
+ },
+ };
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""renderQueue"":-1,""floatProperties"":{""float"":1},""vectorProperties"":{""vector"":[0,1,2,3]},""textureProperties"":{""texture"":0},""keywordMap"":{""keyword"":true},""tagMap"":{""tag"":""map""}}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_Material>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(json, json2);
+
+ // deserialize
+ var deserialized = default(glTF_VRM_Material);
+ json.ParseAsJson().Deserialize(ref deserialized);
+ Assert.AreEqual(1, deserialized.floatProperties.Count);
+ }
+
+ [Test]
+ public void MetaTest()
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""texture"":-1,""allowedUserName"":""OnlyAuthor"",""violentUssageName"":""Disallow"",""sexualUssageName"":""Disallow"",""commercialUssageName"":""Disallow"",""licenseName"":""CC0""}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(@"{""allowedUserName"":""OnlyAuthor"",""violentUssageName"":""Disallow"",""sexualUssageName"":""Disallow"",""commercialUssageName"":""Disallow"",""licenseName"":""CC0""}", json2);
+ }
+
+ [Test]
+ public void MetaTestError()
+ {
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = null,
+ violentUssageName = null,
+ sexualUssageName = null,
+ commercialUssageName = null,
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[allowedUserName.String] null", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ //licenseName = "CC0",
+ licenseName = null,
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[licenseName.String] null", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ licenseName = "_INVALID_SOME_THING_",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[licenseName.String] _INVALID_SOME_THING_ is not valid enum", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ // allowedUserName = "OnlyAuthor",
+ allowedUserName = null,
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[allowedUserName.String] null", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "_INVALID_SOME_THING_",
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[allowedUserName.String] _INVALID_SOME_THING_ is not valid enum", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ //violentUssageName = "Disallow",
+ violentUssageName = null,
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[violentUssageName.String] null", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "_INVALID_SOME_THING_",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[violentUssageName.String] _INVALID_SOME_THING_ is not valid enum", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ //sexualUssageName = "Disallow",
+ sexualUssageName = null,
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[sexualUssageName.String] null", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ sexualUssageName = "_INVALID_SOME_THING_",
+ commercialUssageName = "Disallow",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[sexualUssageName.String] _INVALID_SOME_THING_ is not valid enum", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ //commercialUssageName = "Disallow",
+ commercialUssageName = null,
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[commercialUssageName.String] null", ex.Message);
+ }
+
+ {
+ var model = new glTF_VRM_Meta()
+ {
+ allowedUserName = "OnlyAuthor",
+ violentUssageName = "Disallow",
+ sexualUssageName = "Disallow",
+ commercialUssageName = "_INVALID_SOME_THING_",
+ licenseName = "CC0",
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_Meta>().Serialize(model, c)
+ );
+ Assert.AreEqual("[commercialUssageName.String] _INVALID_SOME_THING_ is not valid enum", ex.Message);
+ }
+ }
+
+ // TODO: Move to another suitable location
+ [Test]
+ public void MetaDeserializeTest()
+ {
+ var json = @"{}";
+
+ var model = deserialize<glTF_VRM_Meta>(json);
+
+ Assert.AreEqual(-1, model.texture);
+ }
+
+ [Test]
+ public void SecondaryAnimationColliderTest()
+ {
+ var model = new glTF_VRM_SecondaryAnimationCollider()
+ {
+ offset = new Vector3(1, 2, 3),
+ radius = 42,
+ };
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""offset"":{""x"":1,""y"":2,""z"":3},""radius"":42}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_SecondaryAnimationCollider>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void SecondaryAnimationColliderGroupTest()
+ {
+ var model = new glTF_VRM_SecondaryAnimationColliderGroup();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""node"":0,""colliders"":[]}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_SecondaryAnimationColliderGroup>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void SecondaryAnimationColliderGroupTestError()
+ {
+ var model = new glTF_VRM_SecondaryAnimationColliderGroup()
+ {
+ node = -1,
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_SecondaryAnimationColliderGroup>().Serialize(model, c)
+ );
+ Assert.AreEqual("[node.String] minimum: ! -1>=0", ex.Message);
+ }
+
+ [Test]
+ public void SecondaryAnimationGroupTest()
+ {
+ var model = new glTF_VRM_SecondaryAnimationGroup();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""stiffiness"":0,""gravityPower"":0,""gravityDir"":{""x"":0,""y"":0,""z"":0},""dragForce"":0,""center"":0,""hitRadius"":0,""bones"":[],""colliderGroups"":[]}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_SecondaryAnimationGroup>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void SecondaryAnimationGroupTestErrorBones()
+ {
+ var model = new glTF_VRM_SecondaryAnimationGroup()
+ {
+ bones = new int[] { -1 }
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_SecondaryAnimationGroup>().Serialize(model, c)
+ );
+ Assert.AreEqual("[bones.String] minimum: ! -1>=0", ex.Message);
+ }
+
+ [Test]
+ public void SecondaryAnimationGroupTestErrorColliderGroups()
+ {
+ var model = new glTF_VRM_SecondaryAnimationGroup()
+ {
+ colliderGroups = new int[] { -1 }
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var ex = Assert.Throws<JsonSchemaValidationException>(
+ () => JsonSchema.FromType<glTF_VRM_SecondaryAnimationGroup>().Serialize(model, c)
+ );
+ Assert.AreEqual("[colliderGroups.String] minimum: ! -1>=0", ex.Message);
+ }
+
+ [Test]
+ public void SecondaryAnimationTest()
+ {
+ var model = new glTF_VRM_SecondaryAnimation();
+
+ var json = model.ToJson();
+ Assert.AreEqual(@"{""boneGroups"":[],""colliderGroups"":[]}", json);
+ Debug.Log(json);
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_SecondaryAnimation>().Serialize(model, c);
+ // NOTE: New serializer outputs values which will not be used...
+ Assert.AreEqual(json, json2);
+ }
+
+ [Test]
+ public void ExtensionsTest()
+ {
+ var model = new glTF_VRM_extensions()
+ {
+ meta = null,
+ humanoid = null,
+ firstPerson = null,
+ blendShapeMaster = null,
+ secondaryAnimation = null,
+ materialProperties = null,
+ };
+
+ var c = new JsonSchemaValidationContext("")
+ {
+ EnableDiagnosisForNotRequiredFields = true,
+ };
+ var json2 = JsonSchema.FromType<glTF_VRM_extensions>().Serialize(model, c);
+ var expected =
+ String.Format(@"{{""exporterVersion"":""{0}"",""specVersion"":""0.0""}}", VRMVersion.VRM_VERSION);
+ Assert.AreEqual(expected, json2);
+ }
+
+ // TODO: Move to another suitable location
+ T deserialize<T>(string json)
+ {
+ return JsonUtility.FromJson<T>(json);
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs.meta
new file mode 100644
index 00000000..60e8389f
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/UniVRMSerializeTests.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 06e8ebf6af2b4e19a0c72ad986e88444
+timeCreated: 1547719804 \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs
new file mode 100644
index 00000000..a3aed506
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs
@@ -0,0 +1,58 @@
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+
+namespace VRM
+{
+ public class VRMBlendShapeKeyTest
+ {
+ static BlendShapeKey CreateBlendShapeKey(string name, BlendShapePreset preset)
+ {
+ var argTypes = new Type[] {typeof(string), typeof(BlendShapePreset)};
+ // private constructor
+ var constructor = typeof(BlendShapeKey).GetConstructor(
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null, argTypes, null);
+ return (BlendShapeKey) constructor.Invoke(new object[] {name, preset});
+ }
+
+ [Test]
+ public void KeyTest()
+ {
+ var key = BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink);
+ Assert.AreEqual(key, BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink));
+ Assert.AreEqual(key, CreateBlendShapeKey("Blink", BlendShapePreset.Blink));
+ Assert.AreEqual(key, CreateBlendShapeKey("xxx", BlendShapePreset.Blink));
+ Assert.AreEqual(key.Name, "Blink");
+
+ var dict = new Dictionary<BlendShapeKey, float>();
+ dict[key] = 1.0f;
+
+ Assert.IsTrue(dict.ContainsKey(CreateBlendShapeKey("Blink", BlendShapePreset.Blink)));
+ Assert.IsTrue(dict.ContainsKey(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink)));
+ Assert.IsTrue(dict.ContainsKey(CreateBlendShapeKey("xxx", BlendShapePreset.Blink)));
+
+ dict.Clear();
+
+ var key2 = BlendShapeKey.CreateUnknown("Blink"); // Name: Blink, Preset: Unknown
+ dict[key2] = 1.0f;
+
+ Assert.AreEqual(key2, CreateBlendShapeKey("Blink", BlendShapePreset.Unknown));
+ Assert.AreNotEqual(key2, BlendShapeKey.CreateUnknown("blink"));
+ Assert.AreNotEqual(key2, CreateBlendShapeKey("Blink", BlendShapePreset.Blink));
+ Assert.AreNotEqual(key2, BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink));
+ Assert.AreEqual(key2.Name, "Blink");
+
+ Assert.IsFalse(dict.ContainsKey(BlendShapeKey.CreateUnknown("blink")));
+ Assert.IsFalse(dict.ContainsKey(CreateBlendShapeKey("Blink", BlendShapePreset.Blink)));
+ Assert.IsFalse(dict.ContainsKey(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink)));
+
+ var key3 = CreateBlendShapeKey("xxx", BlendShapePreset.Blink); // Unknown 以外は独自の名前を持てない
+ Assert.AreEqual(key3, BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink));
+ Assert.AreNotEqual(key3, CreateBlendShapeKey("xxx", BlendShapePreset.Unknown));
+ Assert.AreEqual(key3.Name, "Blink");
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs.meta
new file mode 100644
index 00000000..cb7c673a
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VRMBlendShapeKeyTest.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ca2bf37b88b7c3a45adbeebb8a17559d
+timeCreated: 1521799592
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs
new file mode 100644
index 00000000..d45c9ac6
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs
@@ -0,0 +1,58 @@
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UniJSON;
+using UnityEngine;
+
+namespace VRM
+{
+ public class UniVRMVersionTests
+ {
+ [Test]
+ [TestCase(VRMVersion.VERSION, false)]
+ [TestCase("0.99", true)]
+ [TestCase("0.99.0", true)]
+ [TestCase("1.0.0", true)]
+ public void IsNewerTest(string newer, bool isNewer)
+ {
+ Assert.AreEqual(isNewer, VRMVersion.IsNewer(newer));
+ }
+
+ [Test]
+ [TestCase("0.50", "0.50", false)]
+ [TestCase("0.50", "0.51.0", false)]
+ [TestCase("0.51.0", "0.50", true)]
+ [TestCase("0.51.0", "0.51.0", false)]
+ [TestCase("0.51.1", "0.51.0", true)]
+ [TestCase("0.51.0", "0.51.0-a", false)]
+ [TestCase("0.51.0-b", "0.51.0-a", true)]
+ [TestCase("1.0.0-a", "0.51.0", true)]
+ [TestCase("1.0.0", "0.51.0", true)]
+ public void IsNewerTest(string newer, string older, bool isNewer)
+ {
+ Assert.AreEqual(isNewer, VRMVersion.IsNewer(newer, older));
+ }
+
+ [Test]
+ [TestCase("0.50", true, 0, 50, 0, "")]
+ [TestCase("0.51.0", true, 0, 51, 0, "")]
+ [TestCase("0.51.1", true, 0, 51, 1, "")]
+ [TestCase("0.51.2-a", true, 0, 51, 2, "a")]
+ [TestCase("0.51.10-a1", true, 0, 51, 10, "a1")]
+ [TestCase("aaaaa", false, 0, 0, 0, "")]
+ public void ParseVersionTest(string version, bool canBeParsed, int major, int minor, int patch, string pre)
+ {
+ VRMVersion.Version v;
+ var res = VRMVersion.ParseVersion(version, out v);
+ Assert.AreEqual(canBeParsed, res);
+ if (res)
+ {
+ Assert.AreEqual(major, v.Major);
+ Assert.AreEqual(minor, v.Minor);
+ Assert.AreEqual(patch, v.Patch);
+ Assert.AreEqual(pre, v.Pre);
+ }
+ }
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs.meta
new file mode 100644
index 00000000..f417c0c5
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/Tests/VersionTests.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 40fe68d2a0dd7fa40bb4c5d05da70a21
+timeCreated: 1551255796
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef
new file mode 100644
index 00000000..b6be87e8
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef
@@ -0,0 +1,20 @@
+{
+ "name": "UniVRM.Editor",
+ "references": [
+ "VRM",
+ "UniJSON",
+ "UniHumanoid",
+ "MeshUtility",
+ "UniUnlit"
+ ],
+ "optionalUnityReferences": [],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": []
+} \ No newline at end of file
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef.meta
new file mode 100644
index 00000000..13cb618b
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/UniVRM.Editor.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f7b2dd4e5e1e7264089dc065c45db910
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs
new file mode 100644
index 00000000..7effc76f
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs
@@ -0,0 +1,30 @@
+using System;
+using UnityEngine;
+using VRM;
+
+
+public static class VRMMonoBehaviourComparator
+{
+ public static bool AssertAreEquals(GameObject l, GameObject r)
+ {
+ return
+ AssertAreEquals<VRMMeta>(l, r,
+ (x, y) => x[0].Meta.Equals(y[0].Meta))
+ ;
+ }
+
+ public static bool AssertAreEquals<T>(GameObject l, GameObject r, Func<T[], T[], bool> pred) where T : Component
+ {
+ var ll = l.GetComponents<T>();
+ var rr = r.GetComponents<T>();
+ if (ll.Length != rr.Length)
+ {
+ return false;
+ }
+ if (ll.Length == 0)
+ {
+ return true;
+ }
+ return pred(ll, rr);
+ }
+}
diff --git a/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs.meta b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs.meta
new file mode 100644
index 00000000..c0a76a6d
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniVRM/Editor/VRMMonoBehaviourComparator.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 557330772b8af474c9342f543cd61550
+timeCreated: 1521106869
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: