diff options
author | chai <chaifix@163.com> | 2020-10-11 20:00:58 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2020-10-11 20:00:58 +0800 |
commit | 8b384dffa0d9c63c7a657c6e567c2ddefbf046cd (patch) | |
tree | 3f4d669b73b6622aa49627c4ccb3c78d51a82bde /Assets/ThirdParty/VRM/VRM/UniVRM/Editor | |
parent | cd3aee8d640f6abcc82802ca7abbcdfa031c20d3 (diff) |
+Saionji show off scene
Diffstat (limited to 'Assets/ThirdParty/VRM/VRM/UniVRM/Editor')
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: |