diff --git a/Assets/UniGLTF/Runtime/Extensions/ArrayExtensions.cs b/Assets/UniGLTF/Runtime/Extensions/ArrayExtensions.cs index 8b0f2774bc..2687025e7d 100644 --- a/Assets/UniGLTF/Runtime/Extensions/ArrayExtensions.cs +++ b/Assets/UniGLTF/Runtime/Extensions/ArrayExtensions.cs @@ -132,4 +132,29 @@ public static void Assign(this List dst, T[] src, Func pred) dst.AddRange(src.Select(pred)); } } + + public static class ArraySegmentExtensions + { + public static ArraySegment Slice(this ArraySegment self, int start, int length) + { + if (start + length > self.Count) + { + throw new ArgumentOutOfRangeException(); + } + return new ArraySegment( + self.Array, + self.Offset + start, + length + ); + } + + public static ArraySegment Slice(this ArraySegment self, int start) + { + if (start > self.Count) + { + throw new ArgumentOutOfRangeException(); + } + return self.Slice(start, self.Count - start); + } + } } diff --git a/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs b/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs index 05b5582ce7..526706773c 100644 --- a/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs +++ b/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs @@ -438,11 +438,11 @@ public static byte[] ToGlbBytes(this glTF self) var f = new JsonFormatter(); GltfSerializer.Serialize(f, self); + // remove unused extenions var json = f.ToString().ParseAsJson().ToString(" "); - self.RemoveUnusedExtensions(json); - - return Glb.ToBytes(json, self.buffers[0].GetBytes()); + + return Glb.Create(json, self.buffers[0].GetBytes()).ToBytes(); } public static (string, List) ToGltf(this glTF self, string gltfPath) diff --git a/Assets/UniGLTF/Runtime/UniGLTF/Format/glbTypes.cs b/Assets/UniGLTF/Runtime/UniGLTF/Format/glbTypes.cs index a876b541bd..2b8e0bf4ab 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/Format/glbTypes.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/Format/glbTypes.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; namespace UniGLTF @@ -12,14 +14,39 @@ public enum GlbChunkType : uint public struct GlbHeader { + + public static readonly byte[] GLB_MAGIC = new byte[] { 0x67, 0x6C, 0x54, 0x46 }; // "glTF" + public static readonly byte[] GLB_VERSION = new byte[] { 2, 0, 0, 0 }; + public static void WriteTo(Stream s) { - s.WriteByte((Byte)'g'); - s.WriteByte((Byte)'l'); - s.WriteByte((Byte)'T'); - s.WriteByte((Byte)'F'); - var bytes = BitConverter.GetBytes((UInt32)2); - s.Write(bytes, 0, bytes.Length); + s.Write(GLB_MAGIC, 0, GLB_MAGIC.Length); + s.Write(GLB_VERSION, 0, GLB_VERSION.Length); + } + + public static int TryParse(ArraySegment bytes, out int pos, out Exception message) + { + pos = 0; + + if (!bytes.Slice(0, 4).SequenceEqual(GLB_MAGIC)) + { + message = new FormatException("invalid magic"); + return 0; + } + pos += 4; + + if (!bytes.Slice(pos, 4).SequenceEqual(GLB_VERSION)) + { + message = new FormatException("invalid magic"); + return 0; + } + pos += 4; + + var totalLength = BitConverter.ToInt32(bytes.Array, bytes.Offset + pos); + pos += 4; + + message = null; + return totalLength; } } @@ -48,6 +75,21 @@ public GlbChunk(GlbChunkType type, ArraySegment bytes) Bytes = bytes; } + public static GlbChunk CreateJson(string json) + { + return CreateJson(new ArraySegment(Encoding.UTF8.GetBytes(json))); + } + + public static GlbChunk CreateJson(ArraySegment bytes) + { + return new GlbChunk(GlbChunkType.JSON, bytes); + } + + public static GlbChunk CreateBin(ArraySegment bytes) + { + return new GlbChunk(GlbChunkType.BIN, bytes); + } + byte GetPaddingByte() { // chunk type @@ -100,7 +142,7 @@ public int WriteTo(Stream s) // 4byte align var pad = GetPaddingByte(); - for(int i=0; i + /// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification + /// + public struct Glb { - public static byte[] ToBytes(string json, ArraySegment body) + public readonly GlbChunk Json; + public readonly GlbChunk Binary; + + public Glb(GlbChunk json, GlbChunk binary) + { + if (json.ChunkType != GlbChunkType.JSON) throw new ArgumentException(); + Json = json; + if (binary.ChunkType != GlbChunkType.BIN) throw new ArgumentException(); + Binary = binary; + } + + public static Glb Create(ArraySegment json, ArraySegment bin) + { + return new Glb(GlbChunk.CreateJson(json), GlbChunk.CreateBin(bin)); + } + + public static Glb Create(string json, ArraySegment bin) + { + return new Glb(GlbChunk.CreateJson(json), GlbChunk.CreateBin(bin)); + } + + public byte[] ToBytes() { using (var s = new MemoryStream()) { @@ -123,12 +189,10 @@ public static byte[] ToBytes(string json, ArraySegment body) int size = 12; { - var chunk = new GlbChunk(json); - size += chunk.WriteTo(s); + size += Json.WriteTo(s); } { - var chunk = new GlbChunk(body); - size += chunk.WriteTo(s); + size += Binary.WriteTo(s); } s.Position = pos; @@ -138,5 +202,91 @@ public static byte[] ToBytes(string json, ArraySegment body) return s.ToArray(); } } + + public static GlbChunkType ToChunkType(string src) + { + switch (src) + { + case "BIN": + return GlbChunkType.BIN; + + case "JSON": + return GlbChunkType.JSON; + + default: + throw new FormatException("unknown chunk type: " + src); + } + } + + public static Glb Parse(Byte[] bytes) + { + return Parse(new ArraySegment(bytes)); + } + + public static Glb Parse(ArraySegment bytes) + { + if (TryParse(bytes, out Glb glb, out Exception ex)) + { + return glb; + } + else + { + throw ex; + } + } + + public static bool TryParse(Byte[] bytes, out Glb glb, out Exception ex) + { + return TryParse(new ArraySegment(bytes), out glb, out ex); + } + + public static bool TryParse(ArraySegment bytes, out Glb glb, out Exception ex) + { + glb = default(Glb); + if (bytes.Count == 0) + { + ex = new Exception("empty bytes"); + return false; + } + + var length = GlbHeader.TryParse(bytes, out int pos, out ex); + if (length == 0) + { + return false; + } + bytes = bytes.Slice(0, length); + + try + { + var chunks = new List(); + while (pos < bytes.Count) + { + var chunkDataSize = BitConverter.ToInt32(bytes.Array, bytes.Offset + pos); + pos += 4; + + //var type = (GlbChunkType)BitConverter.ToUInt32(bytes, pos); + var chunkTypeBytes = bytes.Slice(pos, 4).Where(x => x != 0).ToArray(); + var chunkTypeStr = Encoding.ASCII.GetString(chunkTypeBytes); + var type = ToChunkType(chunkTypeStr); + pos += 4; + + chunks.Add(new GlbChunk + { + ChunkType = type, + Bytes = bytes.Slice(pos, chunkDataSize) + }); + + pos += chunkDataSize; + } + + glb = new Glb(chunks[0], chunks[1]); + return true; + } + catch (Exception _ex) + { + ex = _ex; + return false; + } + } } } diff --git a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneColliderGroupEditor.cs b/Assets/VRM10/Editor/Components/SpringBone/VRM10SpringBoneColliderGroupEditor.cs similarity index 78% rename from Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneColliderGroupEditor.cs rename to Assets/VRM10/Editor/Components/SpringBone/VRM10SpringBoneColliderGroupEditor.cs index b982d82f08..f97b0f5ccb 100644 --- a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneColliderGroupEditor.cs +++ b/Assets/VRM10/Editor/Components/SpringBone/VRM10SpringBoneColliderGroupEditor.cs @@ -5,14 +5,14 @@ namespace UniVRM10 { - [CustomEditor(typeof(VRMSpringBoneColliderGroup))] - public class VRMSpringBoneColliderGroupEditor : Editor + [CustomEditor(typeof(VRM10SpringBoneColliderGroup))] + public class VRM10SpringBoneColliderGroupEditor : Editor { - VRMSpringBoneColliderGroup m_target; + VRM10SpringBoneColliderGroup m_target; private void OnEnable() { - m_target = (VRMSpringBoneColliderGroup)target; + m_target = (VRM10SpringBoneColliderGroup)target; } private void OnSceneGUI() @@ -39,15 +39,15 @@ private void OnSceneGUI() EditorUtility.SetDirty(m_target); } } - + [MenuItem("CONTEXT/VRM10SpringBoneColliderGroup/X Mirror")] private static void InvertOffsetX(MenuCommand command) { - var target = command.context as VRMSpringBoneColliderGroup; + var target = command.context as VRM10SpringBoneColliderGroup; if (target == null) return; - + Undo.RecordObject(target, "X Mirror"); - + foreach (var sphereCollider in target.Colliders) { var offset = sphereCollider.Offset; @@ -55,27 +55,27 @@ private static void InvertOffsetX(MenuCommand command) sphereCollider.Offset = offset; } } - + [MenuItem("CONTEXT/VRM10SpringBoneColliderGroup/Sort Colliders by Radius")] private static void SortByRadius(MenuCommand command) { - var target = command.context as VRMSpringBoneColliderGroup; + var target = command.context as VRM10SpringBoneColliderGroup; if (target == null) return; - + Undo.RecordObject(target, "Sort Colliders by Radius"); - target.Colliders = target.Colliders.OrderBy(x => -x.Radius).ToArray(); + target.Colliders = target.Colliders.OrderBy(x => -x.Radius).ToList(); } - + [MenuItem("CONTEXT/VRM10SpringBoneColliderGroup/Sort Colliders by Offset Y")] private static void SortByOffsetY(MenuCommand command) { - var target = command.context as VRMSpringBoneColliderGroup; + var target = command.context as VRM10SpringBoneColliderGroup; if (target == null) return; - + Undo.RecordObject(target, "Sort Colliders by Offset Y"); - target.Colliders = target.Colliders.OrderBy(x => -x.Offset.y).ToArray(); + target.Colliders = target.Colliders.OrderBy(x => -x.Offset.y).ToList(); } } } diff --git a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs.meta b/Assets/VRM10/Editor/Components/SpringBone/VRM10SpringBoneColliderGroupEditor.cs.meta similarity index 83% rename from Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs.meta rename to Assets/VRM10/Editor/Components/SpringBone/VRM10SpringBoneColliderGroupEditor.cs.meta index b92fd0895d..0bd7bfe1b3 100644 --- a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs.meta +++ b/Assets/VRM10/Editor/Components/SpringBone/VRM10SpringBoneColliderGroupEditor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6903dd580e905a243b9841fafe3871bb +guid: 280a977934f9b9b449ac58bc0f016959 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta b/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta deleted file mode 100644 index 00b5aa5c0b..0000000000 --- a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneColliderGroupEditor.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: a092aa1d5a21feb4db6523a3284ca561 -timeCreated: 1519735770 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneView.cs b/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneView.cs deleted file mode 100644 index e69acddb68..0000000000 --- a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneView.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using UnityEditor.IMGUI.Controls; - -namespace UniVRM10 -{ - class VRMSpringBoneTreeView : TreeView - - { - VRM10Controller m_root; - - Dictionary m_boneMap = new Dictionary(); - - public bool TryGetSpringBone(int id, out VRMSpringBone bone) - { - return m_boneMap.TryGetValue(id, out bone); - } - - public VRMSpringBoneTreeView(TreeViewState treeViewState, VRM10Controller root) - : base(treeViewState) - { - m_root = root; - Reload(); - } - - protected override bool CanMultiSelect(TreeViewItem item) - { - return false; - } - - protected override TreeViewItem BuildRoot() - { - m_boneMap.Clear(); - var root = new TreeViewItem { id = 0, depth = -1, displayName = m_root.name }; - var allItems = new List(); - var bones = m_root.GetComponentsInChildren(); - var id = 1; - for (int i = 0; i < bones.Length; ++i, ++id) - { - var bone = bones[i]; - - m_boneMap.Add(id, bone); - - allItems.Add(new TreeViewItem - { - id = id, - displayName = bone.name, - }); - } - // Utility method that initializes the TreeViewItem.children and -parent for all items. - SetupParentsAndChildrenFromDepths(root, allItems); - - // Return root of the tree - return root; - } - } -} diff --git a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs b/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs deleted file mode 100644 index e4e8aeea9e..0000000000 --- a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using UnityEditor; -using UnityEditor.IMGUI.Controls; -using UnityEngine; - - -namespace UniVRM10 -{ - public class VRMSpringBoneWindow : EditorWindow - { - [MenuItem(VRMVersion.MENU + "/VRMSpringBoneWindow")] - public static void ShowEditorWindow() - { - var wnd = GetWindow(); - wnd.titleContent = new GUIContent("VRMSpringBoneWindow"); - } - - void OnEnable() - { - minSize = new Vector2(100, 100); - maxSize = new Vector2(4000, 4000); - Selection.selectionChanged += OnSelectionChanged; - } - - void OnDisable() - { - Selection.selectionChanged -= OnSelectionChanged; - } - - VRM10Controller m_currentRoot; - - [SerializeField] - TreeViewState m_treeViewState; - VRMSpringBoneTreeView m_treeView; - - Rect m_treeRect; - Rect m_inspectorRect; - Vector2 m_inspectorScrollPos; - - void OnGUI() - { - if (m_treeView is null) - { - return; - } - - // bone selector - Rect fullRect = GUILayoutUtility.GetRect(0, 100000, 0, 10000); - var treeRect = new Rect(fullRect.x, fullRect.y, fullRect.width, EditorGUIUtility.singleLineHeight * 3); - var inspectorRect = new Rect(fullRect.x, treeRect.y + treeRect.height, fullRect.width, fullRect.height - treeRect.height); - if (Event.current.type == EventType.Repaint) - { - m_treeRect = treeRect; - m_inspectorRect = inspectorRect; - // Debug.Log($"{m_treeRect}, {m_inspectorRect}"); - } - - m_treeView.OnGUI(m_treeRect); - - GUILayout.BeginArea(m_inspectorRect); - m_inspectorScrollPos = GUILayout.BeginScrollView(m_inspectorScrollPos, GUI.skin.box); - if (m_treeViewState.selectedIDs.Any()) - { - // selected な SpringBone の Inspector を描画する - if (m_treeView.TryGetSpringBone(m_treeViewState.selectedIDs[0], out VRMSpringBone bone)) - { - // Debug.Log(bone); - using (var inspector = new CustomInspector(new SerializedObject(bone))) - { - inspector.OnInspectorGUI(); - } - } - } - GUILayout.EndScrollView(); - GUILayout.EndArea(); - } - - void OnSelectionChanged() - { - var go = Selection.activeObject as GameObject; - if (go is null) - { - return; - } - - var meta = go.GetComponentInParent(); - if (meta == m_currentRoot) - { - return; - } - - m_currentRoot = meta; - if (m_currentRoot is null) - { - m_treeViewState = null; - m_treeView = null; - } - else - { - // update treeview - Debug.Log(m_currentRoot); - m_treeViewState = new TreeViewState(); - m_treeView = new VRMSpringBoneTreeView(m_treeViewState, m_currentRoot); - } - - Repaint(); - } - } -} diff --git a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs index bc61fdc028..0324983ea4 100644 --- a/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs +++ b/Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs @@ -2,7 +2,7 @@ using UnityEditor; using UnityEngine; using System.Linq; - +using System; namespace UniVRM10 { @@ -53,11 +53,12 @@ void OnEnable() { m_metaEditor = Editor.CreateEditor(m_target.Meta); } - m_controller = serializedObject.FindProperty(nameof(m_target.Controller)); - m_expression = serializedObject.FindProperty(nameof(m_target.Expression)); - m_lookAt = serializedObject.FindProperty(nameof(m_target.LookAt)); - m_firstPerson = serializedObject.FindProperty(nameof(m_target.FirstPerson)); - m_asset = serializedObject.FindProperty(nameof(m_target.ModelAsset)); + m_controller = PropGui.FromObject(serializedObject, nameof(m_target.Controller)); + m_expression = PropGui.FromObject(serializedObject, nameof(m_target.Expression)); + m_lookAt = PropGui.FromObject(serializedObject, nameof(m_target.LookAt)); + m_firstPerson = PropGui.FromObject(serializedObject, nameof(m_target.FirstPerson)); + m_springBone = PropGui.FromObject(serializedObject, nameof(m_target.SpringBone)); + m_asset = PropGui.FromObject(serializedObject, nameof(m_target.ModelAsset)); } void OnDisable() @@ -76,23 +77,61 @@ enum Tabs Expression, LookAt, FirstPerson, + SpringBone, Assets, } Tabs _tab; Editor m_metaEditor; - SerializedProperty m_controller; - SerializedProperty m_expression; - SerializedProperty m_lookAt; - SerializedProperty m_firstPerson; - SerializedProperty m_asset; + + class PropGui + { + SerializedProperty m_prop; + + PropGui(SerializedProperty property) + { + m_prop = property; + } + + public static PropGui FromObject(SerializedObject serializedObject, string name) + { + var prop = serializedObject.FindProperty(name); + return new PropGui(prop); + } + + public void RecursiveProperty() + { + var depth = m_prop.depth; + var iterator = m_prop.Copy(); + for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false) + { + if (iterator.depth < depth) + return; + + depth = iterator.depth; + + using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath)) + EditorGUILayout.PropertyField(iterator, true); + } + } + } + + PropGui m_controller; + PropGui m_expression; + PropGui m_lookAt; + PropGui m_firstPerson; + PropGui m_springBone; + PropGui m_asset; public override void OnInspectorGUI() { { var backup = GUI.enabled; GUI.enabled = true; - _tab = MeshUtility.TabBar.OnGUI(_tab); + + _tab = (Tabs)EditorGUILayout.EnumPopup("Select GUI", _tab); + EditorGUILayout.Separator(); + GUI.enabled = backup; } @@ -106,24 +145,28 @@ public override void OnInspectorGUI() break; case Tabs.Controller: - RecursiveProperty(m_controller); + m_controller.RecursiveProperty(); break; case Tabs.Expression: ExpressionGUI(); - RecursiveProperty(m_expression); + m_expression.RecursiveProperty(); break; case Tabs.LookAt: - RecursiveProperty(m_lookAt); + m_lookAt.RecursiveProperty(); break; case Tabs.FirstPerson: - RecursiveProperty(m_firstPerson); + m_firstPerson.RecursiveProperty(); + break; + + case Tabs.SpringBone: + m_springBone.RecursiveProperty(); break; case Tabs.Assets: - RecursiveProperty(m_asset); + m_asset.RecursiveProperty(); break; } @@ -161,22 +204,6 @@ void ExpressionGUI() } } - static void RecursiveProperty(SerializedProperty property) - { - var depth = property.depth; - var iterator = property.Copy(); - for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false) - { - if (iterator.depth < depth) - return; - - depth = iterator.depth; - - using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath)) - EditorGUILayout.PropertyField(iterator, true); - } - } - void OnSceneGUI() { OnSceneGUIOffset(); diff --git a/Assets/VRM10/Editor/MigrationMenu.cs b/Assets/VRM10/Editor/MigrationMenu.cs new file mode 100644 index 0000000000..c6f47a4e3f --- /dev/null +++ b/Assets/VRM10/Editor/MigrationMenu.cs @@ -0,0 +1,47 @@ +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace UniVRM10 +{ + public static class MigrationMenu + { + static string s_lastPath = Application.dataPath; + + const string CONTEXT_MENU = "Assets/Migration: Vrm1"; + + [MenuItem(CONTEXT_MENU, true)] + static bool Enable() + { + var path = UniGLTF.UnityPath.FromAsset(Selection.activeObject); + var isVrm = path.Extension.ToLower() == ".vrm"; + return isVrm; + } + + [MenuItem(CONTEXT_MENU, false)] + static void Exec() + { + var path = UniGLTF.UnityPath.FromAsset(Selection.activeObject); + var isVrm = path.Extension.ToLower() == ".vrm"; + + var vrm1Bytes = Migration.Migrate(File.ReadAllBytes(path.FullPath)); + + var dst = EditorUtility.SaveFilePanel( + "Save vrm1 file", + s_lastPath, + $"{path.FileNameWithoutExtension}_vrm1", + "vrm"); + if (string.IsNullOrEmpty(dst)) + { + return; + } + s_lastPath = Path.GetDirectoryName(dst); + + // write result + File.WriteAllBytes(dst, vrm1Bytes); + + // immediately import for GUI update + UniGLTF.UnityPath.FromFullpath(dst).ImportAsset(); + } + } +} diff --git a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneProcessor.cs.meta b/Assets/VRM10/Editor/MigrationMenu.cs.meta similarity index 83% rename from Assets/VRM10/Runtime/Components/SpringBone/SpringBoneProcessor.cs.meta rename to Assets/VRM10/Editor/MigrationMenu.cs.meta index 51b9ea6c26..cc4e4ebc94 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneProcessor.cs.meta +++ b/Assets/VRM10/Editor/MigrationMenu.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fb0714842bac26b44b774260bbef58c3 +guid: c0be718f3aba6ad4282a4b74eae9045b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM10/Editor/VRM10.Editor.asmdef b/Assets/VRM10/Editor/VRM10.Editor.asmdef index eb354e9902..9d1baa8c85 100644 --- a/Assets/VRM10/Editor/VRM10.Editor.asmdef +++ b/Assets/VRM10/Editor/VRM10.Editor.asmdef @@ -5,7 +5,8 @@ "VrmLib", "MeshUtility", "MeshUtility.Editor", - "UniGLTF.Editor" + "UniGLTF.Editor", + "UniGLTF" ], "optionalUnityReferences": [], "includePlatforms": [ diff --git a/Assets/VRM10/Editor/Vrm10ExportDialog.cs b/Assets/VRM10/Editor/Vrm10ExportDialog.cs index db9821ad19..f6b005117f 100644 --- a/Assets/VRM10/Editor/Vrm10ExportDialog.cs +++ b/Assets/VRM10/Editor/Vrm10ExportDialog.cs @@ -68,6 +68,7 @@ void OnEnable() Selection.selectionChanged += Repaint; m_tmpMeta = ScriptableObject.CreateInstance(); + m_tmpMeta.Authors = new string[] { "" }; m_state = new MeshUtility.ExporterDialogState(); m_state.ExportRootChanged += (root) => @@ -343,7 +344,7 @@ static byte[] GetGlb(VrmLib.Model model) }; var glbBytes10 = exporter.Export(model, option); // ? - var glb10 = VrmLib.Glb.Parse(glbBytes10); + var glb10 = UniGLTF.Glb.Parse(glbBytes10); return glb10.ToBytes(); } diff --git a/Assets/VRM10/Runtime/Components/Meta/VRM10MetaObject.cs b/Assets/VRM10/Runtime/Components/Meta/VRM10MetaObject.cs index 3834870112..d395f1e07c 100644 --- a/Assets/VRM10/Runtime/Components/Meta/VRM10MetaObject.cs +++ b/Assets/VRM10/Runtime/Components/Meta/VRM10MetaObject.cs @@ -79,14 +79,16 @@ public IEnumerable Validate(GameObject _) { yield return Validation.Error("Require Name. "); } - // if (string.IsNullOrEmpty(Version)) - // { - // yield return Validation.Error("Require Version. "); - // } - if (Authors == null || Authors.Length == 0 || Authors.All(x => string.IsNullOrEmpty(x))) + + if (Authors == null || Authors.Length == 0) { yield return Validation.Error("Require at leaset one Author."); } + + if (Authors.All(x => string.IsNullOrWhiteSpace(x))) + { + yield return Validation.Error("All Authors is whitespace"); + } } public void CopyTo(VRM10MetaObject dst) diff --git a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneLogic.cs b/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneLogic.cs index 6eed0936cf..0543c0d9c1 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneLogic.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneLogic.cs @@ -36,8 +36,6 @@ public Quaternion LocalRotation } public Vector3 m_boneAxis; - public float Radius { get; set; } - public SpringBoneLogic(Transform center, Transform transform, Vector3 localChildPosition) { m_transform = transform; @@ -65,7 +63,7 @@ Quaternion ParentRotation public struct InternalCollider { - public SpringBoneColliderTypes ColliderTypes; + public VRM10SpringBoneColliderTypes ColliderTypes; public Vector3 WorldPosition; public float Radius; public Vector3 WorldTail; @@ -73,7 +71,7 @@ public struct InternalCollider public void Update(Transform center, float stiffnessForce, float dragForce, Vector3 external, - List colliders) + List colliders, float jointRadius) { var currentTail = center != null ? center.TransformPoint(m_currentTail) @@ -95,7 +93,7 @@ public void Update(Transform center, nextTail = m_transform.position + (nextTail - m_transform.position).normalized * m_length; // Collisionで移動 - nextTail = Collision(colliders, nextTail); + nextTail = Collision(colliders, nextTail, jointRadius); m_prevTail = center != null ? center.InverseTransformPoint(currentTail) @@ -117,14 +115,14 @@ protected virtual Quaternion ApplyRotation(Vector3 nextTail) nextTail - m_transform.position) * rotation; } - bool TrySphereCollision(Vector3 worldPosition, float radius, ref Vector3 nextTail) + bool TrySphereCollision(Vector3 worldPosition, float radius, ref Vector3 nextTail, float jointRadius) { - var r = Radius + radius; + var r = jointRadius + radius; if (Vector3.SqrMagnitude(nextTail - worldPosition) <= (r * r)) { // ヒット。Colliderの半径方向に押し出す var normal = (nextTail - worldPosition).normalized; - var posFromCollider = worldPosition + normal * (Radius + radius); + var posFromCollider = worldPosition + normal * (jointRadius + radius); // 長さをboneLengthに強制 nextTail = m_transform.position + (posFromCollider - m_transform.position).normalized * m_length; return true; @@ -135,7 +133,7 @@ bool TrySphereCollision(Vector3 worldPosition, float radius, ref Vector3 nextTai } } - bool TryCapsuleCollision(in InternalCollider collider, ref Vector3 nextTail) + bool TryCapsuleCollision(in InternalCollider collider, ref Vector3 nextTail, float jointRadius) { var P = collider.WorldTail - collider.WorldPosition; var Q = m_transform.position - collider.WorldPosition; @@ -143,34 +141,34 @@ bool TryCapsuleCollision(in InternalCollider collider, ref Vector3 nextTail) if (dot <= 0) { // head側半球の球判定 - return TrySphereCollision(collider.WorldPosition, collider.Radius, ref nextTail); + return TrySphereCollision(collider.WorldPosition, collider.Radius, ref nextTail, jointRadius); } var t = dot / P.magnitude; if (t >= 1.0f) { // tail側半球の球判定 - return TrySphereCollision(collider.WorldTail, collider.Radius, ref nextTail); + return TrySphereCollision(collider.WorldTail, collider.Radius, ref nextTail, jointRadius); } // head-tail上の m_transform.position との最近点 var p = collider.WorldPosition + P * t; - return TrySphereCollision(p, collider.Radius, ref nextTail); + return TrySphereCollision(p, collider.Radius, ref nextTail, jointRadius); } - protected virtual Vector3 Collision(List colliders, Vector3 nextTail) + protected virtual Vector3 Collision(List colliders, Vector3 nextTail, float jointRadius) { foreach (var collider in colliders) { // すべての衝突判定を順番に実行する switch (collider.ColliderTypes) { - case SpringBoneColliderTypes.Sphere: - TrySphereCollision(collider.WorldPosition, collider.Radius, ref nextTail); + case VRM10SpringBoneColliderTypes.Sphere: + TrySphereCollision(collider.WorldPosition, collider.Radius, ref nextTail, jointRadius); break; - case SpringBoneColliderTypes.Capsule: - TryCapsuleCollision(in collider, ref nextTail); + case VRM10SpringBoneColliderTypes.Capsule: + TryCapsuleCollision(in collider, ref nextTail, jointRadius); break; default: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneProcessor.cs b/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneProcessor.cs deleted file mode 100644 index 3d4add914e..0000000000 --- a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneProcessor.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace UniVRM10 -{ - public class SpringBoneProcessor - { - List m_logics = new List(); - Dictionary m_initialLocalRotationMap; - - - public void ResetSpringBone() - { - foreach (var verlet in m_logics) - { - verlet.Head.localRotation = Quaternion.identity; - } - } - - public void SetupRecursive(Transform parent, Transform center) - { - if (parent.childCount == 0) - { - // 末端に追加のスプリングを付加する - var delta = parent.position - parent.parent.position; - var childPosition = parent.position + delta.normalized * 0.07f; - m_logics.Add(new SpringBoneLogic(center, parent, parent.worldToLocalMatrix.MultiplyPoint(childPosition))); - } - else - { - // 最初の子ボーンを尻尾としてスプリングを付加する - var firstChild = parent.GetChild(0); - var localPosition = firstChild.localPosition; - var scale = firstChild.lossyScale; - m_logics.Add(new SpringBoneLogic(center, parent, - new Vector3( - localPosition.x * scale.x, - localPosition.y * scale.y, - localPosition.z * scale.z - ))) - ; - } - - foreach (Transform child in parent) - { - SetupRecursive(child, center); - } - } - - void Setup(List RootBones, Transform m_center, bool force = false) - { - if (force || m_initialLocalRotationMap == null) - { - m_initialLocalRotationMap = new Dictionary(); - } - else - { - // restore initial rotation - foreach (var kv in m_initialLocalRotationMap) - { - kv.Key.localRotation = kv.Value; - } - m_initialLocalRotationMap.Clear(); - } - m_logics.Clear(); - - foreach (var transform in RootBones) - { - if (transform != null) - { - foreach (var x in transform.Traverse()) - { - // backup initial rotation - m_initialLocalRotationMap[x] = x.localRotation; - } - - SetupRecursive(transform, m_center); - } - } - } - - public void Update(List RootBones, List m_colliderList, - float stiffness, float m_dragForce, Vector3 external, - float m_hitRadius, Transform m_center) - { - if (m_logics == null || m_logics.Count == 0) - { - Setup(RootBones, m_center); - } - - foreach (var verlet in m_logics) - { - verlet.Radius = m_hitRadius; - verlet.Update(m_center, - stiffness, - m_dragForce, - external, - m_colliderList - ); - } - } - - public void DrawGizmos(Transform m_center, float m_hitRadius, Color m_gizmoColor) - { - foreach (var verlet in m_logics) - { - verlet.DrawGizmo(m_center, m_hitRadius, m_gizmoColor); - } - } - } -} diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBone.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBone.cs similarity index 54% rename from Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBone.cs rename to Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBone.cs index 8cc409ed0f..6b57c62c66 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBone.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBone.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace UniVRM10 @@ -7,57 +9,37 @@ namespace UniVRM10 /// The base algorithm is http://rocketjump.skr.jp/unity3d/109/ of @ricopin416 /// DefaultExecutionOrder(11000) means calculate springbone after FinalIK( VRIK ) /// - [AddComponentMenu("VRM/VRMSpringBone")] -#if UNITY_5_5_OR_NEWER - [DefaultExecutionOrder(11000)] -#endif - public class VRMSpringBone : MonoBehaviour + [Serializable] + public class VRM10SpringBone { [SerializeField] public string m_comment; - [SerializeField, Header("Gizmo")] - bool m_drawGizmo = default; - [SerializeField] - Color m_gizmoColor = Color.yellow; - - [SerializeField, Range(0, 4), Header("Settings")] - public float m_stiffnessForce = 1.0f; - - [SerializeField, Range(0, 2)] - public float m_gravityPower = 0; + public List Joints = new List(); [SerializeField] - public Vector3 m_gravityDir = new Vector3(0, -1.0f, 0); - - [SerializeField, Range(0, 1)] - public float m_dragForce = 0.4f; + public List ColliderGroups = new List(); [SerializeField] public Transform m_center; - [SerializeField] - public List RootBones = new List(); - - [SerializeField, Range(0, 0.5f), Header("Collision")] - public float m_hitRadius = 0.02f; - - [SerializeField] - public VRMSpringBoneColliderGroup[] ColliderGroups; - - SpringBoneProcessor m_processor = new SpringBoneProcessor(); - [ContextMenu("Reset bones")] - public void ResetSpringBone() + public void ResetJoints() { - m_processor.ResetSpringBone(); + foreach (var joint in Joints) + { + if (joint != null) + { + joint.Transform.localRotation = Quaternion.identity; + } + } } List m_colliderList = new List(); public void Process() { - if (RootBones == null) + if (Joints == null) { return; } @@ -74,20 +56,20 @@ public void Process() { switch (collider.ColliderType) { - case SpringBoneColliderTypes.Sphere: + case VRM10SpringBoneColliderTypes.Sphere: m_colliderList.Add(new SpringBoneLogic.InternalCollider { - ColliderTypes = SpringBoneColliderTypes.Sphere, + ColliderTypes = VRM10SpringBoneColliderTypes.Sphere, WorldPosition = group.transform.TransformPoint(collider.Offset), Radius = collider.Radius, }); break; - case SpringBoneColliderTypes.Capsule: + case VRM10SpringBoneColliderTypes.Capsule: m_colliderList.Add(new SpringBoneLogic.InternalCollider { - ColliderTypes = SpringBoneColliderTypes.Capsule, + ColliderTypes = VRM10SpringBoneColliderTypes.Capsule, WorldPosition = group.transform.TransformPoint(collider.Offset), Radius = collider.Radius, WorldTail = group.transform.TransformPoint(collider.Tail) @@ -99,19 +81,26 @@ public void Process() } } - var stiffness = m_stiffnessForce * Time.deltaTime; - var external = m_gravityDir * (m_gravityPower * Time.deltaTime); - - m_processor.Update(RootBones, m_colliderList, - stiffness, m_dragForce, external, - m_hitRadius, m_center); + { + // udpate joints + VRM10SpringJoint lastJoint = Joints.FirstOrDefault(x => x != null); + foreach (var joint in Joints.Where(x => x != null).Skip(1)) + { + lastJoint.Update(m_center, Time.deltaTime, m_colliderList, joint); + lastJoint = joint; + } + lastJoint.Update(m_center, Time.deltaTime, m_colliderList, null); + } } - private void OnDrawGizmos() + public void DrawGizmo(Color color) { - if (m_drawGizmo) + foreach (var joint in Joints) { - m_processor.DrawGizmos(m_center, m_hitRadius, m_gizmoColor); + if (joint != null) + { + joint.DrawGizmo(m_center, color); + } } } } diff --git a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneCollider.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBone.cs.meta similarity index 83% rename from Assets/VRM10/Runtime/Components/SpringBone/SpringBoneCollider.cs.meta rename to Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBone.cs.meta index 5df2bad9d6..3e62418a8e 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneCollider.cs.meta +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBone.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b939a4d43f2f17341b3e28bc9c59f8dd +guid: 1f440a6e91549e542bda6d6d741b1409 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneCollider.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs similarity index 71% rename from Assets/VRM10/Runtime/Components/SpringBone/SpringBoneCollider.cs rename to Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs index 54b957d6af..1f5fe5357a 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/SpringBoneCollider.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs @@ -3,16 +3,16 @@ namespace UniVRM10 { - public enum SpringBoneColliderTypes + public enum VRM10SpringBoneColliderTypes { Sphere, Capsule, } [Serializable] - public class SpringBoneCollider + public class VRM10SpringBoneCollider { - public SpringBoneColliderTypes ColliderType; + public VRM10SpringBoneColliderTypes ColliderType; /// bone local position public Vector3 Offset; diff --git a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneView.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs.meta similarity index 83% rename from Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneView.cs.meta rename to Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs.meta index a68f49acac..5d1c1d17de 100644 --- a/Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneView.cs.meta +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneCollider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cd352799c630a8d49813a5c4d937a847 +guid: 35bfb658269b2af478e501de243deda6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBoneColliderGroup.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneColliderGroup.cs similarity index 76% rename from Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBoneColliderGroup.cs rename to Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneColliderGroup.cs index dc325d55f8..180ca31dd0 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBoneColliderGroup.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneColliderGroup.cs @@ -1,26 +1,28 @@ -using System; +using System.Collections.Generic; using UnityEngine; namespace UniVRM10 { - [AddComponentMenu("VRM/VRMSpringBoneColliderGroup")] -#if UNITY_5_5_OR_NEWER - [DefaultExecutionOrder(11001)] -#endif - public class VRMSpringBoneColliderGroup : MonoBehaviour + /// + /// VRMC_node_collider + /// + [DisallowMultipleComponent] + [AddComponentMenu("VRM10/VRM10SpringBoneColliderGroup")] + public class VRM10SpringBoneColliderGroup : MonoBehaviour { - [SerializeField] - public SpringBoneCollider[] Colliders = new SpringBoneCollider[]{ - new SpringBoneCollider + void Reset() + { + // default shape + Colliders.Add(new VRM10SpringBoneCollider { - ColliderType = SpringBoneColliderTypes.Capsule, - Radius=0.1f - } - }; + ColliderType = VRM10SpringBoneColliderTypes.Capsule, + Radius = 0.1f + }); + } [SerializeField] - Color m_gizmoColor = Color.magenta; + public List Colliders = new List(); public static void DrawWireCapsule(Vector3 headPos, Vector3 tailPos, float radius) { @@ -70,9 +72,9 @@ private static void DrawWireCircle(Vector3 centerPos, Vector3 xAxis, Vector3 yAx } } - private void OnDrawGizmosSelected() + public void DrawGizmos(Color color) { - Gizmos.color = m_gizmoColor; + Gizmos.color = color; Matrix4x4 mat = transform.localToWorldMatrix; Gizmos.matrix = mat * Matrix4x4.Scale(new Vector3( 1.0f / transform.lossyScale.x, @@ -83,14 +85,11 @@ private void OnDrawGizmosSelected() { switch (y.ColliderType) { - case SpringBoneColliderTypes.Sphere: + case VRM10SpringBoneColliderTypes.Sphere: Gizmos.DrawWireSphere(y.Offset, y.Radius); break; - case SpringBoneColliderTypes.Capsule: - // Gizmos.DrawWireSphere(y.Offset, y.Radius); - // Gizmos.DrawWireSphere(y.Tail, y.Radius); - // Gizmos.DrawLine(y.Offset, y.Tail); + case VRM10SpringBoneColliderTypes.Capsule: DrawWireCapsule(y.Offset, y.Tail, y.Radius); break; } diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneColliderGroup.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneColliderGroup.cs.meta new file mode 100644 index 0000000000..a53d706324 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneColliderGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1542d3467a35afa4dbefa2a112058078 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs new file mode 100644 index 0000000000..1eea53e463 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + /// + /// すべての SpringBone を管理する。 + /// Humanoid 一体につきひとつだけ存在する。 + /// + [Serializable] + public class VRM10SpringBoneManager + { + [SerializeField, Header("Gizmo")] + bool m_drawGizmo = default; + + [SerializeField] + Color m_gizmoColor = Color.yellow; + + + [SerializeField] + public List Springs = new List(); + + + /// + /// 1フレームに一回呼び出す(VRM10Controllerの仕事) + /// + public void Process() + { + foreach (var spring in Springs) + { + spring.Process(); + } + } + + public void DrawGizmos() + { + if (m_drawGizmo) + { + foreach (var spring in Springs) + { + spring.DrawGizmo(m_gizmoColor); + } + } + } + } +} diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs.meta new file mode 100644 index 0000000000..7a973865ae --- /dev/null +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringBoneManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d09e28f7198c42d42bb31fd40e164b2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringJoint.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringJoint.cs new file mode 100644 index 0000000000..88214a493d --- /dev/null +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringJoint.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + [Serializable] + public class VRM10SpringJoint + { + [SerializeField] + public Transform Transform; + + public VRM10SpringJoint(Transform t) + { + Transform = t; + } + + [SerializeField, Range(0, 4), Header("Settings")] + public float m_stiffnessForce = 1.0f; + + [SerializeField, Range(0, 2)] + public float m_gravityPower = 0; + + [SerializeField] + public Vector3 m_gravityDir = new Vector3(0, -1.0f, 0); + + [SerializeField, Range(0, 1)] + public float m_dragForce = 0.4f; + + [SerializeField] + public bool m_exclude; + + [SerializeField, Range(0, 0.5f), Header("Collision")] + public float m_jointRadius = 0.02f; + + SpringBoneLogic m_logic; + + public void DrawGizmo(Transform center, Color color) + { + if (m_logic != null) + { + m_logic.DrawGizmo(center, m_jointRadius, color); + } + else + { + // TODO + } + } + + public void Update(Transform center, float deltaTime, List colliders, VRM10SpringJoint tail) + { + if (m_logic == null) + { + // 初期化 + if (tail != null) + { + var localPosition = tail.Transform.localPosition; + var scale = tail.Transform.lossyScale; + m_logic = new SpringBoneLogic(center, Transform, + new Vector3( + localPosition.x * scale.x, + localPosition.y * scale.y, + localPosition.z * scale.z + )); + } + else + { + // 親からまっすぐの位置に tail を作成 + var delta = Transform.position - Transform.parent.position; + var childPosition = Transform.position + delta.normalized * 0.07f; + m_logic = new SpringBoneLogic(center, Transform, Transform.worldToLocalMatrix.MultiplyPoint(childPosition)); + } + } + + m_logic.Update(center, m_stiffnessForce * deltaTime, m_dragForce, m_gravityDir * (m_gravityPower * deltaTime), colliders, m_jointRadius); + } + } +} diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringJoint.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringJoint.cs.meta new file mode 100644 index 0000000000..0fab6c6f83 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringJoint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5a7fc353e202e4f44a1025ba6e806262 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringUtility.cs b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringUtility.cs similarity index 99% rename from Assets/VRM10/Runtime/Components/SpringBone/VRMSpringUtility.cs rename to Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringUtility.cs index 429063bff6..920e02d55e 100644 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringUtility.cs +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringUtility.cs @@ -1,7 +1,7 @@ namespace UniVRM10 { - public static class VRMSpringUtility + public static class VRM10SpringUtility { #if false diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringUtility.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringUtility.cs.meta new file mode 100644 index 0000000000..844f7c71f6 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/SpringBone/VRM10SpringUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61a4226bc18ddab4daf79fc681690fab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBone.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBone.cs.meta deleted file mode 100644 index 11d32a3a4a..0000000000 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBone.cs.meta +++ /dev/null @@ -1,13 +0,0 @@ -fileFormatVersion: 2 -guid: 9972432db4ac7af489ee0b864c5baaf9 -timeCreated: 1517224588 -licenseType: Free -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBoneColliderGroup.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBoneColliderGroup.cs.meta deleted file mode 100644 index ff68b26e7b..0000000000 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringBoneColliderGroup.cs.meta +++ /dev/null @@ -1,13 +0,0 @@ -fileFormatVersion: 2 -guid: 079b0ee290c8a8d40b07314c753397a1 -timeCreated: 1517984922 -licenseType: Free -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringUtility.cs.meta b/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringUtility.cs.meta deleted file mode 100644 index 5a48996218..0000000000 --- a/Assets/VRM10/Runtime/Components/SpringBone/VRMSpringUtility.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 59fca440205c8134a898c5164771468c -timeCreated: 1528358605 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/VRM10Controller.cs b/Assets/VRM10/Runtime/Components/VRM10Controller.cs index 12e8daa1f9..08fc8f857b 100644 --- a/Assets/VRM10/Runtime/Components/VRM10Controller.cs +++ b/Assets/VRM10/Runtime/Components/VRM10Controller.cs @@ -15,16 +15,16 @@ namespace UniVRM10 [DisallowMultipleComponent] public class VRM10Controller : MonoBehaviour { - public enum UpdateTypes - { - None, - Update, - LateUpdate, - } - [Serializable] public class VRM10ControllerImpl { + public enum UpdateTypes + { + None, + Update, + LateUpdate, + } + [SerializeField, Header("UpdateSetting")] public UpdateTypes UpdateType = UpdateTypes.LateUpdate; } @@ -44,6 +44,9 @@ public class VRM10ControllerImpl [SerializeField] public VRM10ControllerFirstPerson FirstPerson = new VRM10ControllerFirstPerson(); + [SerializeField] + public VRM10SpringBoneManager SpringBone = new VRM10SpringBoneManager(); + [SerializeField] public ModelAsset ModelAsset; @@ -62,7 +65,6 @@ void OnDestroy() } VRMConstraint[] m_constraints; - VRMSpringBone[] m_springs; Transform m_head; public Transform Head @@ -133,14 +135,7 @@ public void Apply() // // spring // - if (m_springs == null) - { - m_springs = GetComponentsInChildren(); - } - foreach (var spring in m_springs) - { - spring.Process(); - } + SpringBone.Process(); // // expression @@ -160,7 +155,7 @@ public void Apply() private void Update() { - if (Controller.UpdateType == UpdateTypes.Update) + if (Controller.UpdateType == VRM10ControllerImpl.UpdateTypes.Update) { Apply(); } @@ -168,10 +163,15 @@ private void Update() private void LateUpdate() { - if (Controller.UpdateType == UpdateTypes.LateUpdate) + if (Controller.UpdateType == VRM10ControllerImpl.UpdateTypes.LateUpdate) { Apply(); } } + + private void OnDrawGizmos() + { + SpringBone.DrawGizmos(); + } } } diff --git a/Assets/VRM10/Runtime/Format/NodeCollider/Deserializer.g.cs b/Assets/VRM10/Runtime/Format/NodeCollider/Deserializer.g.cs index 0eba7de28a..02a1ca8056 100644 --- a/Assets/VRM10/Runtime/Format/NodeCollider/Deserializer.g.cs +++ b/Assets/VRM10/Runtime/Format/NodeCollider/Deserializer.g.cs @@ -36,6 +36,16 @@ public static VRMC_node_collider Deserialize(ListTreeNode parsed) { var key = kv.Key.GetString(); + if(key=="extensions"){ + value.Extensions = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="extras"){ + value.Extras = new glTFExtensionImport(kv.Value); + continue; + } + if(key=="shapes"){ value.Shapes = Deserialize_Shapes(kv.Value); continue; @@ -63,6 +73,16 @@ public static ColliderShape Deserialize_Shapes_ITEM(ListTreeNode pars { var key = kv.Key.GetString(); + if(key=="extensions"){ + value.Extensions = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="extras"){ + value.Extras = new glTFExtensionImport(kv.Value); + continue; + } + if(key=="sphere"){ value.Sphere = Deserialize_Sphere(kv.Value); continue; diff --git a/Assets/VRM10/Runtime/Format/NodeCollider/Format.g.cs b/Assets/VRM10/Runtime/Format/NodeCollider/Format.g.cs index 5512bc9abd..1f33f6cd6b 100644 --- a/Assets/VRM10/Runtime/Format/NodeCollider/Format.g.cs +++ b/Assets/VRM10/Runtime/Format/NodeCollider/Format.g.cs @@ -30,6 +30,12 @@ public class ColliderShapeCapsule public class ColliderShape { + // Dictionary object with extension-specific objects. + public glTFExtension Extensions; + + // Application-specific data. + public glTFExtension Extras; + public ColliderShapeSphere Sphere; public ColliderShapeCapsule Capsule; @@ -40,6 +46,12 @@ public class VRMC_node_collider public const string ExtensionName = "VRMC_node_collider"; public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(ExtensionName); + // Dictionary object with extension-specific objects. + public glTFExtension Extensions; + + // Application-specific data. + public glTFExtension Extras; + public List Shapes; } } diff --git a/Assets/VRM10/Runtime/Format/NodeCollider/Serializer.g.cs b/Assets/VRM10/Runtime/Format/NodeCollider/Serializer.g.cs index 298c122bde..5fc75ed36b 100644 --- a/Assets/VRM10/Runtime/Format/NodeCollider/Serializer.g.cs +++ b/Assets/VRM10/Runtime/Format/NodeCollider/Serializer.g.cs @@ -33,6 +33,16 @@ public static void Serialize(JsonFormatter f, VRMC_node_collider value) f.BeginMap(); + if(value.Extensions!=null){ + f.Key("extensions"); + value.Extensions.Serialize(f); + } + + if(value.Extras!=null){ + f.Key("extras"); + value.Extras.Serialize(f); + } + if(value.Shapes!=null&&value.Shapes.Count()>=0){ f.Key("shapes"); Serialize_Shapes(f, value.Shapes); @@ -58,6 +68,16 @@ public static void Serialize_Shapes_ITEM(JsonFormatter f, ColliderShape value) f.BeginMap(); + if(value.Extensions!=null){ + f.Key("extensions"); + value.Extensions.Serialize(f); + } + + if(value.Extras!=null){ + f.Key("extras"); + value.Extras.Serialize(f); + } + if(value.Sphere!=null){ f.Key("sphere"); Serialize_Sphere(f, value.Sphere); diff --git a/Assets/VRM10/Runtime/Format/SpringBone/Deserializer.g.cs b/Assets/VRM10/Runtime/Format/SpringBone/Deserializer.g.cs index 5a363bb619..33b72345aa 100644 --- a/Assets/VRM10/Runtime/Format/SpringBone/Deserializer.g.cs +++ b/Assets/VRM10/Runtime/Format/SpringBone/Deserializer.g.cs @@ -36,8 +36,13 @@ public static VRMC_springBone Deserialize(ListTreeNode parsed) { var key = kv.Key.GetString(); - if(key=="settings"){ - value.Settings = Deserialize_Settings(kv.Value); + if(key=="extensions"){ + value.Extensions = new glTFExtensionImport(kv.Value); + continue; + } + + if(key=="extras"){ + value.Extras = new glTFExtensionImport(kv.Value); continue; } @@ -50,41 +55,36 @@ public static VRMC_springBone Deserialize(ListTreeNode parsed) return value; } -public static List Deserialize_Settings(ListTreeNode parsed) +public static List Deserialize_Springs(ListTreeNode parsed) { - var value = new List(); + var value = new List(); foreach(var x in parsed.ArrayItems()) { - value.Add(Deserialize_Settings_ITEM(x)); + value.Add(Deserialize_Springs_ITEM(x)); } return value; } -public static SpringSetting Deserialize_Settings_ITEM(ListTreeNode parsed) +public static Spring Deserialize_Springs_ITEM(ListTreeNode parsed) { - var value = new SpringSetting(); + var value = new Spring(); foreach(var kv in parsed.ObjectItems()) { var key = kv.Key.GetString(); - if(key=="stiffness"){ - value.Stiffness = kv.Value.GetSingle(); - continue; - } - - if(key=="gravityPower"){ - value.GravityPower = kv.Value.GetSingle(); + if(key=="name"){ + value.Name = kv.Value.GetString(); continue; } - if(key=="gravityDir"){ - value.GravityDir = Deserialize_GravityDir(kv.Value); + if(key=="joints"){ + value.Joints = Deserialize_Joints(kv.Value); continue; } - if(key=="dragForce"){ - value.DragForce = kv.Value.GetSingle(); + if(key=="colliders"){ + value.Colliders = Deserialize_Colliders(kv.Value); continue; } @@ -92,47 +92,36 @@ public static SpringSetting Deserialize_Settings_ITEM(ListTreeNode pa return value; } -public static float[] Deserialize_GravityDir(ListTreeNode parsed) -{ - var value = new float[parsed.GetArrayCount()]; - int i=0; - foreach(var x in parsed.ArrayItems()) - { - value[i++] = x.GetSingle(); - } - return value; -} - -public static List Deserialize_Springs(ListTreeNode parsed) +public static List Deserialize_Joints(ListTreeNode parsed) { - var value = new List(); + var value = new List(); foreach(var x in parsed.ArrayItems()) { - value.Add(Deserialize_Springs_ITEM(x)); + value.Add(Deserialize_Joints_ITEM(x)); } return value; } -public static Spring Deserialize_Springs_ITEM(ListTreeNode parsed) +public static SpringBoneJoint Deserialize_Joints_ITEM(ListTreeNode parsed) { - var value = new Spring(); + var value = new SpringBoneJoint(); foreach(var kv in parsed.ObjectItems()) { var key = kv.Key.GetString(); - if(key=="name"){ - value.Name = kv.Value.GetString(); + if(key=="extensions"){ + value.Extensions = new glTFExtensionImport(kv.Value); continue; } - if(key=="setting"){ - value.Setting = kv.Value.GetInt32(); + if(key=="extras"){ + value.Extras = new glTFExtensionImport(kv.Value); continue; } - if(key=="springRoot"){ - value.SpringRoot = kv.Value.GetInt32(); + if(key=="node"){ + value.Node = kv.Value.GetInt32(); continue; } @@ -141,8 +130,28 @@ public static Spring Deserialize_Springs_ITEM(ListTreeNode parsed) continue; } - if(key=="colliders"){ - value.Colliders = Deserialize_Colliders(kv.Value); + if(key=="stiffness"){ + value.Stiffness = kv.Value.GetSingle(); + continue; + } + + if(key=="gravityPower"){ + value.GravityPower = kv.Value.GetSingle(); + continue; + } + + if(key=="gravityDir"){ + value.GravityDir = Deserialize_GravityDir(kv.Value); + continue; + } + + if(key=="dragForce"){ + value.DragForce = kv.Value.GetSingle(); + continue; + } + + if(key=="exclude"){ + value.Exclude = kv.Value.GetBoolean(); continue; } @@ -150,6 +159,17 @@ public static Spring Deserialize_Springs_ITEM(ListTreeNode parsed) return value; } +public static float[] Deserialize_GravityDir(ListTreeNode parsed) +{ + var value = new float[parsed.GetArrayCount()]; + int i=0; + foreach(var x in parsed.ArrayItems()) + { + value[i++] = x.GetSingle(); + } + return value; +} + public static int[] Deserialize_Colliders(ListTreeNode parsed) { var value = new int[parsed.GetArrayCount()]; diff --git a/Assets/VRM10/Runtime/Format/SpringBone/Format.g.cs b/Assets/VRM10/Runtime/Format/SpringBone/Format.g.cs index 9510b42e86..8febc816d0 100644 --- a/Assets/VRM10/Runtime/Format/SpringBone/Format.g.cs +++ b/Assets/VRM10/Runtime/Format/SpringBone/Format.g.cs @@ -7,19 +7,34 @@ namespace UniGLTF.Extensions.VRMC_springBone { - public class SpringSetting + public class SpringBoneJoint { - // The force to return to the initial pose + // Dictionary object with extension-specific objects. + public glTFExtension Extensions; + + // Application-specific data. + public glTFExtension Extras; + + // The node index. + public int? Node; + + // The radius of spring sphere. + public float? HitRadius; + + // The force to return to the initial pose. public float? Stiffness; - // Gravitational acceleration + // Gravitational acceleration. public float? GravityPower; // The direction of gravity. A gravity other than downward direction also works. public float[] GravityDir; - // Air resistance. Deceleration force + // Air resistance. Deceleration force. public float? DragForce; + + // When enabled, this joint will skip Spring processing. + public bool? Exclude; } public class Spring @@ -27,14 +42,8 @@ public class Spring // Name of the Spring public string Name; - // The index of spring settings - public int? Setting; - - // The node index of spring root - public int? SpringRoot; - - // The radius of spring sphere - public float? HitRadius; + // Joints in this spring. Except for the first element, the parent of the element must be the previous element of the array + public List Joints; // Colliders that detect collision with nodes start from springRoot public int[] Colliders; @@ -45,8 +54,11 @@ public class VRMC_springBone public const string ExtensionName = "VRMC_springBone"; public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(ExtensionName); - // An array of settings. - public List Settings; + // Dictionary object with extension-specific objects. + public glTFExtension Extensions; + + // Application-specific data. + public glTFExtension Extras; // An array of springs. public List Springs; diff --git a/Assets/VRM10/Runtime/Format/SpringBone/Serializer.g.cs b/Assets/VRM10/Runtime/Format/SpringBone/Serializer.g.cs index b033f9b26f..8a87d1aefa 100644 --- a/Assets/VRM10/Runtime/Format/SpringBone/Serializer.g.cs +++ b/Assets/VRM10/Runtime/Format/SpringBone/Serializer.g.cs @@ -33,9 +33,14 @@ public static void Serialize(JsonFormatter f, VRMC_springBone value) f.BeginMap(); - if(value.Settings!=null&&value.Settings.Count()>=0){ - f.Key("settings"); - Serialize_Settings(f, value.Settings); + if(value.Extensions!=null){ + f.Key("extensions"); + value.Extensions.Serialize(f); + } + + if(value.Extras!=null){ + f.Key("extras"); + value.Extras.Serialize(f); } if(value.Springs!=null&&value.Springs.Count()>=0){ @@ -46,88 +51,71 @@ public static void Serialize(JsonFormatter f, VRMC_springBone value) f.EndMap(); } -public static void Serialize_Settings(JsonFormatter f, List value) +public static void Serialize_Springs(JsonFormatter f, List value) { f.BeginList(); foreach(var item in value) { - Serialize_Settings_ITEM(f, item); + Serialize_Springs_ITEM(f, item); } f.EndList(); } -public static void Serialize_Settings_ITEM(JsonFormatter f, SpringSetting value) +public static void Serialize_Springs_ITEM(JsonFormatter f, Spring value) { f.BeginMap(); - if(value.Stiffness.HasValue){ - f.Key("stiffness"); - f.Value(value.Stiffness.GetValueOrDefault()); - } - - if(value.GravityPower.HasValue){ - f.Key("gravityPower"); - f.Value(value.GravityPower.GetValueOrDefault()); + if(!string.IsNullOrEmpty(value.Name)){ + f.Key("name"); + f.Value(value.Name); } - if(value.GravityDir!=null&&value.GravityDir.Count()>=3){ - f.Key("gravityDir"); - Serialize_GravityDir(f, value.GravityDir); + if(value.Joints!=null&&value.Joints.Count()>=0){ + f.Key("joints"); + Serialize_Joints(f, value.Joints); } - if(value.DragForce.HasValue){ - f.Key("dragForce"); - f.Value(value.DragForce.GetValueOrDefault()); + if(value.Colliders!=null&&value.Colliders.Count()>=0){ + f.Key("colliders"); + Serialize_Colliders(f, value.Colliders); } f.EndMap(); } -public static void Serialize_GravityDir(JsonFormatter f, float[] value) +public static void Serialize_Joints(JsonFormatter f, List value) { f.BeginList(); foreach(var item in value) { - f.Value(item); - - } - f.EndList(); -} - -public static void Serialize_Springs(JsonFormatter f, List value) -{ - f.BeginList(); - - foreach(var item in value) - { - Serialize_Springs_ITEM(f, item); + Serialize_Joints_ITEM(f, item); } f.EndList(); } -public static void Serialize_Springs_ITEM(JsonFormatter f, Spring value) +public static void Serialize_Joints_ITEM(JsonFormatter f, SpringBoneJoint value) { f.BeginMap(); - if(!string.IsNullOrEmpty(value.Name)){ - f.Key("name"); - f.Value(value.Name); + if(value.Extensions!=null){ + f.Key("extensions"); + value.Extensions.Serialize(f); } - if(value.Setting.HasValue){ - f.Key("setting"); - f.Value(value.Setting.GetValueOrDefault()); + if(value.Extras!=null){ + f.Key("extras"); + value.Extras.Serialize(f); } - if(value.SpringRoot.HasValue){ - f.Key("springRoot"); - f.Value(value.SpringRoot.GetValueOrDefault()); + if(value.Node.HasValue){ + f.Key("node"); + f.Value(value.Node.GetValueOrDefault()); } if(value.HitRadius.HasValue){ @@ -135,14 +123,46 @@ public static void Serialize_Springs_ITEM(JsonFormatter f, Spring value) f.Value(value.HitRadius.GetValueOrDefault()); } - if(value.Colliders!=null&&value.Colliders.Count()>=0){ - f.Key("colliders"); - Serialize_Colliders(f, value.Colliders); + if(value.Stiffness.HasValue){ + f.Key("stiffness"); + f.Value(value.Stiffness.GetValueOrDefault()); + } + + if(value.GravityPower.HasValue){ + f.Key("gravityPower"); + f.Value(value.GravityPower.GetValueOrDefault()); + } + + if(value.GravityDir!=null&&value.GravityDir.Count()>=3){ + f.Key("gravityDir"); + Serialize_GravityDir(f, value.GravityDir); + } + + if(value.DragForce.HasValue){ + f.Key("dragForce"); + f.Value(value.DragForce.GetValueOrDefault()); + } + + if(value.Exclude.HasValue){ + f.Key("exclude"); + f.Value(value.Exclude.GetValueOrDefault()); } f.EndMap(); } +public static void Serialize_GravityDir(JsonFormatter f, float[] value) +{ + f.BeginList(); + + foreach(var item in value) + { + f.Value(item); + + } + f.EndList(); +} + public static void Serialize_Colliders(JsonFormatter f, int[] value) { f.BeginList(); diff --git a/Assets/VRM10/Runtime/IO/BufferAccessorAdapter.cs b/Assets/VRM10/Runtime/IO/BufferAccessorAdapter.cs index 4dc9b1fd37..8bc457cdd4 100644 --- a/Assets/VRM10/Runtime/IO/BufferAccessorAdapter.cs +++ b/Assets/VRM10/Runtime/IO/BufferAccessorAdapter.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Numerics; -using System.Runtime.InteropServices; using UniGLTF; -using VrmLib; namespace UniVRM10 { @@ -31,7 +29,7 @@ public static int TypeCount(this string type) } } - public static int AddViewTo(this BufferAccessor self, + public static int AddViewTo(this VrmLib.BufferAccessor self, Vrm10Storage storage, int bufferIndex, int offset = 0, int count = 0) { @@ -44,7 +42,7 @@ public static int AddViewTo(this BufferAccessor self, return storage.AppendToBuffer(bufferIndex, slice, stride); } - static glTFAccessor CreateGltfAccessor(this BufferAccessor self, + static glTFAccessor CreateGltfAccessor(this VrmLib.BufferAccessor self, int viewIndex, int count = 0, int byteOffset = 0) { if (count == 0) @@ -61,7 +59,7 @@ static glTFAccessor CreateGltfAccessor(this BufferAccessor self, }; } - public static int AddAccessorTo(this BufferAccessor self, + public static int AddAccessorTo(this VrmLib.BufferAccessor self, Vrm10Storage storage, int viewIndex, Action, glTFAccessor> minMax = null, int offset = 0, int count = 0) @@ -77,15 +75,15 @@ public static int AddAccessorTo(this BufferAccessor self, return accessorIndex; } - public static int AddAccessorTo(this BufferAccessor self, + public static int AddAccessorTo(this VrmLib.BufferAccessor self, Vrm10Storage storage, int bufferIndex, // GltfBufferTargetType targetType, bool useSparse, Action, glTFAccessor> minMax = null, int offset = 0, int count = 0) { - if (self.ComponentType == AccessorValueType.FLOAT - && self.AccessorType == AccessorVectorType.VEC3 + if (self.ComponentType == VrmLib.AccessorValueType.FLOAT + && self.AccessorType == VrmLib.AccessorVectorType.VEC3 ) { var values = self.GetSpan(); @@ -107,9 +105,9 @@ public static int AddAccessorTo(this BufferAccessor self, { // use sparse var sparseIndexBin = new ArraySegment(new byte[sparseValuesWithIndex.Count * 4]); - var sparseIndexSpan = SpanLike.Wrap(sparseIndexBin); + var sparseIndexSpan = VrmLib.SpanLike.Wrap(sparseIndexBin); var sparseValueBin = new ArraySegment(new byte[sparseValuesWithIndex.Count * 12]); - var sparseValueSpan = SpanLike.Wrap(sparseValueBin); + var sparseValueSpan = VrmLib.SpanLike.Wrap(sparseValueBin); for (int i = 0; i < sparseValuesWithIndex.Count; ++i) { @@ -132,7 +130,7 @@ public static int AddAccessorTo(this BufferAccessor self, count = sparseValuesWithIndex.Count, indices = new glTFSparseIndices { - componentType = (glComponentType)AccessorValueType.UNSIGNED_INT, + componentType = (glComponentType)VrmLib.AccessorValueType.UNSIGNED_INT, bufferView = sparseIndexView, }, values = new glTFSparseValues diff --git a/Assets/VRM10/Runtime/IO/ImageAdapter.cs b/Assets/VRM10/Runtime/IO/ImageAdapter.cs index d7415c5f92..f678237d95 100644 --- a/Assets/VRM10/Runtime/IO/ImageAdapter.cs +++ b/Assets/VRM10/Runtime/IO/ImageAdapter.cs @@ -1,4 +1,3 @@ -using VrmLib; using System; using UniGLTF; @@ -6,7 +5,7 @@ namespace UniVRM10 { public static class ImageAdapter { - public static Image FromGltf(this glTFImage x, Vrm10Storage storage) + public static VrmLib.Image FromGltf(this glTFImage x, Vrm10Storage storage) { if (x.bufferView == -1) { @@ -19,24 +18,24 @@ public static Image FromGltf(this glTFImage x, Vrm10Storage storage) var buffer = storage.Gltf.buffers[view.buffer]; // テクスチャの用途を調べる - var usage = default(ImageUsage); + var usage = default(VrmLib.ImageUsage); foreach (var material in storage.Gltf.materials) { var colorImage = GetColorImage(storage, material); if (colorImage == x) { - usage |= ImageUsage.Color; + usage |= VrmLib.ImageUsage.Color; } var normalImage = GetNormalImage(storage, material); if (normalImage == x) { - usage |= ImageUsage.Normal; + usage |= VrmLib.ImageUsage.Normal; } } var memory = storage.GetBufferBytes(buffer); - return new Image(x.name, + return new VrmLib.Image(x.name, x.mimeType, usage, memory.Slice(view.byteOffset, view.byteLength)); @@ -86,7 +85,7 @@ static glTFImage GetNormalImage(Vrm10Storage storage, glTFMaterial m) return GetTexture(storage, index); } - public static glTFImage ToGltf(this Image src, Vrm10Storage storage) + public static glTFImage ToGltf(this VrmLib.Image src, Vrm10Storage storage) { var viewIndex = storage.AppendToBuffer(0, src.Bytes, 1); var gltf = storage.Gltf; diff --git a/Assets/VRM10/Runtime/IO/ModelExtensions.cs b/Assets/VRM10/Runtime/IO/ModelExtensions.cs index 97d1cdabc6..7ed669d129 100644 --- a/Assets/VRM10/Runtime/IO/ModelExtensions.cs +++ b/Assets/VRM10/Runtime/IO/ModelExtensions.cs @@ -13,7 +13,7 @@ public static byte[] ToGlb(this VrmLib.Model model) // vrm = false }; var glbBytes10 = exporter10.Export(model, option); - var glb10 = VrmLib.Glb.Parse(glbBytes10); + var glb10 = UniGLTF.Glb.Parse(glbBytes10); return glb10.ToBytes(); } } diff --git a/Assets/VRM10/Runtime/IO/TextureAdapter.cs b/Assets/VRM10/Runtime/IO/TextureAdapter.cs index 289825d8f6..d5c6f187c7 100644 --- a/Assets/VRM10/Runtime/IO/TextureAdapter.cs +++ b/Assets/VRM10/Runtime/IO/TextureAdapter.cs @@ -11,7 +11,7 @@ public static ImageTexture FromGltf(this glTFTexture x, glTFTextureSampler sampl { var image = images[x.source]; var name = !string.IsNullOrEmpty(x.name) ? x.name : image.Name; - return new ImageTexture(x.name, sampler.FromGltf(), image, colorSpace, textureType); + return new ImageTexture(name, sampler.FromGltf(), image, colorSpace, textureType); } public static TextureSampler FromGltf(this glTFTextureSampler sampler) diff --git a/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs b/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs index 00f4b22afb..1e154dffb6 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs @@ -37,9 +37,7 @@ public byte[] ToBytes() UniGLTF.GltfSerializer.Serialize(f, Storage.Gltf); var json = f.GetStoreBytes(); - var glb = new VrmLib.Glb( - new VrmLib.GlbChunk(VrmLib.GlbChunkType.JSON, json), - new VrmLib.GlbChunk(VrmLib.GlbChunkType.BIN, Storage.Buffers[0].Bytes)); + var glb = UniGLTF.Glb.Create(json, Storage.Buffers[0].Bytes); return glb.ToBytes(); } diff --git a/Assets/VRM10/Runtime/IO/Vrm10Storage.cs b/Assets/VRM10/Runtime/IO/Vrm10Storage.cs index 1bc7f17fe8..20e452ef40 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Storage.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Storage.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Numerics; using System.Runtime.InteropServices; -using UniGLTF; using VrmLib; using UniJSON; @@ -13,7 +12,7 @@ namespace UniVRM10 public class Vrm10Storage : IVrmStorage { public ArraySegment OriginalJson { get; private set; } - public glTF Gltf + public UniGLTF.glTF Gltf { get; private set; @@ -30,7 +29,7 @@ public glTF Gltf /// public Vrm10Storage() { - Gltf = new glTF() + Gltf = new UniGLTF.glTF() { extensionsUsed = new List(), }; @@ -78,7 +77,7 @@ public int AppendToBuffer(int bufferIndex, ArraySegment segment, int strid { Buffers[bufferIndex].Extend(segment, stride, out int offset, out int length); var viewIndex = Gltf.bufferViews.Count; - Gltf.bufferViews.Add(new glTFBufferView + Gltf.bufferViews.Add(new UniGLTF.glTFBufferView { buffer = 0, byteOffset = offset, @@ -176,14 +175,14 @@ public ArraySegment GetAccessorBytes(int accessorIndex) .Slice(sparse.values.byteOffset, accessor.GetStride() * sparse.count); ; - if (sparse.indices.componentType == (glComponentType)AccessorValueType.UNSIGNED_SHORT - && accessor.componentType == (glComponentType)AccessorValueType.FLOAT + if (sparse.indices.componentType == (UniGLTF.glComponentType)AccessorValueType.UNSIGNED_SHORT + && accessor.componentType == (UniGLTF.glComponentType)AccessorValueType.FLOAT && accessor.type == "VEC3") { return RestoreSparseAccessorUInt16(bytes, accessor.count, sparseIndexBytes, sparseValueBytes); } - if (sparse.indices.componentType == (glComponentType)AccessorValueType.UNSIGNED_INT - && accessor.componentType == (glComponentType)AccessorValueType.FLOAT + if (sparse.indices.componentType == (UniGLTF.glComponentType)AccessorValueType.UNSIGNED_INT + && accessor.componentType == (UniGLTF.glComponentType)AccessorValueType.FLOAT && accessor.type == "VEC3") { return RestoreSparseAccessorUInt32(bytes, accessor.count, sparseIndexBytes, sparseValueBytes); @@ -451,7 +450,7 @@ public Image CreateImage(int index) /// /// /// - private (Texture.TextureTypes, glTFMaterial) GetTextureType(int textureIndex) + private (Texture.TextureTypes, UniGLTF.glTFMaterial) GetTextureType(int textureIndex) { foreach (var material in Gltf.materials) { @@ -460,7 +459,7 @@ public Image CreateImage(int index) { if (material.normalTexture?.index == textureIndex) return (Texture.TextureTypes.NormalMap, material); } - else if (glTF_KHR_materials_unlit.IsEnable(material)) + else if (UniGLTF.glTF_KHR_materials_unlit.IsEnable(material)) { } else @@ -495,7 +494,7 @@ private Texture.ColorSpaceTypes GetTextureColorSpaceType(int textureIndex) if (material.normalTexture?.index == textureIndex) return Texture.ColorSpaceTypes.Linear; } - else if (glTF_KHR_materials_unlit.IsEnable(material)) + else if (UniGLTF.glTF_KHR_materials_unlit.IsEnable(material)) { // unlit if (material.pbrMetallicRoughness.baseColorTexture?.index == textureIndex) return Texture.ColorSpaceTypes.Srgb; @@ -522,7 +521,7 @@ public Texture CreateTexture(int index, List images) var sampler = (texture.sampler >= 0 && texture.sampler < Gltf.samplers.Count) ? Gltf.samplers[texture.sampler] - : new glTFTextureSampler() + : new UniGLTF.glTFTextureSampler() ; if (textureType.Item1 == Texture.TextureTypes.MetallicRoughness && textureType.Item2.pbrMetallicRoughness != null) @@ -607,7 +606,7 @@ public Meta CreateVrmMeta(List textures) static void AssignHumanoid(List nodes, UniGLTF.Extensions.VRMC_vrm.HumanBone humanBone, VrmLib.HumanoidBones key) { - if (humanBone.Node.HasValue) + if (humanBone != null && humanBone.Node.HasValue) { nodes[humanBone.Node.Value].HumanoidBone = key; } @@ -710,66 +709,63 @@ public SpringBoneManager CreateVrmSpringBone(List nodes) return null; } - var springBone = new SpringBoneManager(); + var springBoneManager = new SpringBoneManager(); // springs if (gltfVrmSpringBone.Springs != null) { - foreach (var group in gltfVrmSpringBone.Springs.GroupBy(x => x.Setting.Value)) + foreach (var gltfSpring in gltfVrmSpringBone.Springs) { - var sb = new SpringBone(); - sb.Comment = group.First().Name; - sb.HitRadius = group.First().HitRadius.Value; - var setting = gltfVrmSpringBone.Settings[group.Key]; - sb.DragForce = setting.DragForce.Value; - sb.GravityDir = setting.GravityDir.ToVector3(); - sb.GravityPower = setting.GravityPower.Value; - sb.Stiffness = setting.Stiffness.Value; - - foreach (var spring in group) + var springBone = new SpringBone(); + springBone.Comment = gltfSpring.Name; + + // joint + foreach (var gltfJoint in gltfSpring.Joints) { - // root - sb.Bones.Add(nodes[spring.SpringRoot.Value]); - // collider - foreach (var colliderNode in spring.Colliders) + var joint = new SpringJoint(nodes[gltfJoint.Node.Value]); + joint.HitRadius = gltfJoint.HitRadius.Value; + joint.DragForce = gltfJoint.DragForce.Value; + joint.GravityDir = gltfJoint.GravityDir.ToVector3(); + joint.GravityPower = gltfJoint.GravityPower.Value; + joint.Stiffness = gltfJoint.Stiffness.Value; + joint.Exclude = gltfJoint.Exclude.Value; + springBone.Joints.Add(joint); + } + + // collider + springBone.Colliders.AddRange(gltfSpring.Colliders.Select(colliderNode => + { + if (UniGLTF.Extensions.VRMC_node_collider.GltfDeserializer.TryGet(Gltf.nodes[colliderNode].extensions, + out UniGLTF.Extensions.VRMC_node_collider.VRMC_node_collider extension)) { - var collider = springBone.Colliders.FirstOrDefault(x => x.Node == nodes[colliderNode]); - if (collider == null) + var collider = new SpringBoneColliderGroup(nodes[colliderNode], extension.Shapes.Select(x => { - if (UniGLTF.Extensions.VRMC_node_collider.GltfDeserializer.TryGet(Gltf.nodes[colliderNode].extensions, - out UniGLTF.Extensions.VRMC_node_collider.VRMC_node_collider extension)) + if (x.Sphere != null) + { + return VrmSpringBoneCollider.CreateSphere(x.Sphere.Offset.ToVector3(), x.Sphere.Radius.Value); + } + else if (x.Capsule != null) { - collider = new SpringBoneColliderGroup(nodes[colliderNode], extension.Shapes.Select(x => - { - if (x.Sphere != null) - { - return VrmSpringBoneCollider.CreateSphere(x.Sphere.Offset.ToVector3(), x.Sphere.Radius.Value); - } - else if (x.Capsule != null) - { - return VrmSpringBoneCollider.CreateCapsule(x.Capsule.Offset.ToVector3(), x.Capsule.Radius.Value, x.Capsule.Tail.ToVector3()); - } - else - { - throw new NotImplementedException(); - } - })); - springBone.Colliders.Add(collider); + return VrmSpringBoneCollider.CreateCapsule(x.Capsule.Offset.ToVector3(), x.Capsule.Radius.Value, x.Capsule.Tail.ToVector3()); } else { - throw new Exception("collider not found"); + throw new NotImplementedException(); } - } - sb.Colliders.Add(collider); + })); + return collider; } - } + else + { + return null; + } + }).Where(x => x != null)); - springBone.Springs.Add(sb); + springBoneManager.Springs.Add(springBone); } } - return springBone; + return springBoneManager; } public FirstPerson CreateVrmFirstPerson(List nodes, List meshGroups) @@ -790,7 +786,7 @@ public LookAt CreateVrmLookAt() return gltfVrm.LookAt.FromGltf(); } - public ArraySegment GetBufferBytes(glTFBufferView bufferView) + public ArraySegment GetBufferBytes(UniGLTF.glTFBufferView bufferView) { if (!bufferView.buffer.TryGetValidIndex(Gltf.buffers.Count, out int bufferViewBufferIndex)) { @@ -799,7 +795,7 @@ public ArraySegment GetBufferBytes(glTFBufferView bufferView) return GetBufferBytes(Gltf.buffers[bufferViewBufferIndex]); } - public ArraySegment GetBufferBytes(glTFBuffer buffer) + public ArraySegment GetBufferBytes(UniGLTF.glTFBuffer buffer) { int index = Gltf.buffers.IndexOf(buffer); return Buffers[index].Bytes; diff --git a/Assets/VRM10/Runtime/IO/VrmSpringBoneAdapter.cs b/Assets/VRM10/Runtime/IO/VrmSpringBoneAdapter.cs index 96ee5c1ed1..87d4178354 100644 --- a/Assets/VRM10/Runtime/IO/VrmSpringBoneAdapter.cs +++ b/Assets/VRM10/Runtime/IO/VrmSpringBoneAdapter.cs @@ -10,18 +10,6 @@ namespace UniVRM10 { public static class SpringBoneAdapter { - public static SpringSetting ToGltf(this SpringBone self, List nodes) - { - var setting = new SpringSetting - { - DragForce = self.DragForce, - GravityPower = self.GravityPower, - Stiffness = self.Stiffness, - GravityDir = self.GravityDir.ToFloat3(), - }; - return setting; - } - public static VRMC_springBone ToGltf(this SpringBoneManager self, List nodes, List gltfNodes) { @@ -35,11 +23,11 @@ public static VRMC_springBone ToGltf(this SpringBoneManager self, List nod // // VRMC_node_collider // - foreach (var x in self.Colliders) + foreach (var nodeCollider in self.Springs.SelectMany(x => x.Colliders)) { - var index = nodes.IndexOfThrow(x.Node); - var collider = new VRMC_node_collider(); - foreach (var y in x.Colliders) + var index = nodes.IndexOfThrow(nodeCollider.Node); + var gltfCollider = new VRMC_node_collider(); + foreach (var y in nodeCollider.Colliders) { switch (y.ColliderType) { @@ -50,7 +38,7 @@ public static VRMC_springBone ToGltf(this SpringBoneManager self, List nod Radius = y.Radius, Offset = y.Offset.ToFloat3(), }; - collider.Shapes.Add(new ColliderShape + gltfCollider.Shapes.Add(new ColliderShape { Sphere = sphere, }); @@ -65,7 +53,7 @@ public static VRMC_springBone ToGltf(this SpringBoneManager self, List nod Offset = y.Offset.ToFloat3(), Tail = y.CapsuleTail.ToFloat3(), }; - collider.Shapes.Add(new ColliderShape + gltfCollider.Shapes.Add(new ColliderShape { Capsule = capsule, }); @@ -80,7 +68,7 @@ public static VRMC_springBone ToGltf(this SpringBoneManager self, List nod // // add to node.extensions // - UniGLTF.Extensions.VRMC_node_collider.GltfSerializer.SerializeTo(ref gltfNodes[index].extensions, collider); + UniGLTF.Extensions.VRMC_node_collider.GltfSerializer.SerializeTo(ref gltfNodes[index].extensions, gltfCollider); } // @@ -88,20 +76,25 @@ public static VRMC_springBone ToGltf(this SpringBoneManager self, List nod // foreach (var x in self.Springs) { - var settingIndex = springBone.Settings.Count; - springBone.Settings.Add(x.ToGltf(nodes)); - foreach (var bone in x.Bones) + var spring = new Spring { - var spring = new Spring + Name = x.Comment, + Colliders = x.Colliders.Select(y => nodes.IndexOfThrow(y.Node)).ToArray(), + }; + + foreach (var y in x.Joints) + { + spring.Joints.Add(new SpringBoneJoint { - Name = x.Comment, - HitRadius = x.HitRadius, - SpringRoot = nodes.IndexOfThrow(bone), - Setting = settingIndex, - Colliders = x.Colliders.Select(y => nodes.IndexOfThrow(y.Node)).ToArray(), - }; - springBone.Springs.Add(spring); + HitRadius = y.HitRadius, + DragForce = y.DragForce, + GravityDir = y.GravityDir.ToFloat3(), + GravityPower = y.GravityPower, + Stiffness = y.Stiffness, + }); } + + springBone.Springs.Add(spring); } return springBone; diff --git a/Assets/VRM10/Runtime/Migration.meta b/Assets/VRM10/Runtime/Migration.meta new file mode 100644 index 0000000000..26398a4090 --- /dev/null +++ b/Assets/VRM10/Runtime/Migration.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 475aa606f7fac0648a504be5102a6f16 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Migration/Migration.cs b/Assets/VRM10/Runtime/Migration/Migration.cs new file mode 100644 index 0000000000..720776d14e --- /dev/null +++ b/Assets/VRM10/Runtime/Migration/Migration.cs @@ -0,0 +1,613 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UniJSON; + +namespace UniVRM10 +{ + /// + /// Convert vrm0 binary to vrm1 binary. Json processing + /// + public static class Migration + { + static bool TryGet(this UniGLTF.glTFExtensionImport extensions, string key, out ListTreeNode value) + { + foreach (var kv in extensions.ObjectItems()) + { + if (kv.Key.GetString() == key) + { + value = kv.Value; + return true; + } + } + + value = default; + return false; + } + + // { + // "bone": "hips", + // "node": 14, + // "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 + // }, + static UniGLTF.Extensions.VRMC_vrm.HumanBone MigrateHumanoidBone(ListTreeNode vrm0) + { + return new UniGLTF.Extensions.VRMC_vrm.HumanBone + { + Node = vrm0["node"].GetInt32(), + }; + } + + static UniGLTF.Extensions.VRMC_vrm.Humanoid MigrateHumanoid(ListTreeNode vrm0) + { + var humanoid = new UniGLTF.Extensions.VRMC_vrm.Humanoid + { + HumanBones = new UniGLTF.Extensions.VRMC_vrm.HumanBones() + }; + + foreach (var humanoidBone in vrm0["humanBones"].ArrayItems()) + { + var boneType = humanoidBone["bone"].GetString(); + switch (boneType) + { + case "hips": humanoid.HumanBones.Hips = MigrateHumanoidBone(humanoidBone); break; + case "leftUpperLeg": humanoid.HumanBones.LeftUpperLeg = MigrateHumanoidBone(humanoidBone); break; + case "rightUpperLeg": humanoid.HumanBones.RightUpperLeg = MigrateHumanoidBone(humanoidBone); break; + case "leftLowerLeg": humanoid.HumanBones.LeftLowerLeg = MigrateHumanoidBone(humanoidBone); break; + case "rightLowerLeg": humanoid.HumanBones.RightLowerLeg = MigrateHumanoidBone(humanoidBone); break; + case "leftFoot": humanoid.HumanBones.LeftFoot = MigrateHumanoidBone(humanoidBone); break; + case "rightFoot": humanoid.HumanBones.RightFoot = MigrateHumanoidBone(humanoidBone); break; + case "spine": humanoid.HumanBones.Spine = MigrateHumanoidBone(humanoidBone); break; + case "chest": humanoid.HumanBones.Chest = MigrateHumanoidBone(humanoidBone); break; + case "neck": humanoid.HumanBones.Neck = MigrateHumanoidBone(humanoidBone); break; + case "head": humanoid.HumanBones.Head = MigrateHumanoidBone(humanoidBone); break; + case "leftShoulder": humanoid.HumanBones.LeftShoulder = MigrateHumanoidBone(humanoidBone); break; + case "rightShoulder": humanoid.HumanBones.RightShoulder = MigrateHumanoidBone(humanoidBone); break; + case "leftUpperArm": humanoid.HumanBones.LeftUpperArm = MigrateHumanoidBone(humanoidBone); break; + case "rightUpperArm": humanoid.HumanBones.RightUpperArm = MigrateHumanoidBone(humanoidBone); break; + case "leftLowerArm": humanoid.HumanBones.LeftLowerArm = MigrateHumanoidBone(humanoidBone); break; + case "rightLowerArm": humanoid.HumanBones.RightLowerArm = MigrateHumanoidBone(humanoidBone); break; + case "leftHand": humanoid.HumanBones.LeftHand = MigrateHumanoidBone(humanoidBone); break; + case "rightHand": humanoid.HumanBones.RightHand = MigrateHumanoidBone(humanoidBone); break; + case "leftToes": humanoid.HumanBones.LeftToes = MigrateHumanoidBone(humanoidBone); break; + case "rightToes": humanoid.HumanBones.RightToes = MigrateHumanoidBone(humanoidBone); break; + case "leftEye": humanoid.HumanBones.LeftEye = MigrateHumanoidBone(humanoidBone); break; + case "rightEye": humanoid.HumanBones.RightEye = MigrateHumanoidBone(humanoidBone); break; + case "jaw": humanoid.HumanBones.Jaw = MigrateHumanoidBone(humanoidBone); break; + case "leftThumbProximal": humanoid.HumanBones.LeftThumbProximal = MigrateHumanoidBone(humanoidBone); break; + case "leftThumbIntermediate": humanoid.HumanBones.LeftThumbIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "leftThumbDistal": humanoid.HumanBones.LeftThumbDistal = MigrateHumanoidBone(humanoidBone); break; + case "leftIndexProximal": humanoid.HumanBones.LeftIndexProximal = MigrateHumanoidBone(humanoidBone); break; + case "leftIndexIntermediate": humanoid.HumanBones.LeftIndexIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "leftIndexDistal": humanoid.HumanBones.LeftIndexDistal = MigrateHumanoidBone(humanoidBone); break; + case "leftMiddleProximal": humanoid.HumanBones.LeftMiddleProximal = MigrateHumanoidBone(humanoidBone); break; + case "leftMiddleIntermediate": humanoid.HumanBones.LeftMiddleIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "leftMiddleDistal": humanoid.HumanBones.LeftMiddleDistal = MigrateHumanoidBone(humanoidBone); break; + case "leftRingProximal": humanoid.HumanBones.LeftRingProximal = MigrateHumanoidBone(humanoidBone); break; + case "leftRingIntermediate": humanoid.HumanBones.LeftRingIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "leftRingDistal": humanoid.HumanBones.LeftRingDistal = MigrateHumanoidBone(humanoidBone); break; + case "leftLittleProximal": humanoid.HumanBones.LeftLittleProximal = MigrateHumanoidBone(humanoidBone); break; + case "leftLittleIntermediate": humanoid.HumanBones.LeftLittleIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "leftLittleDistal": humanoid.HumanBones.LeftLittleDistal = MigrateHumanoidBone(humanoidBone); break; + case "rightThumbProximal": humanoid.HumanBones.RightThumbProximal = MigrateHumanoidBone(humanoidBone); break; + case "rightThumbIntermediate": humanoid.HumanBones.RightThumbIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "rightThumbDistal": humanoid.HumanBones.RightThumbDistal = MigrateHumanoidBone(humanoidBone); break; + case "rightIndexProximal": humanoid.HumanBones.RightIndexProximal = MigrateHumanoidBone(humanoidBone); break; + case "rightIndexIntermediate": humanoid.HumanBones.RightIndexIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "rightIndexDistal": humanoid.HumanBones.RightIndexDistal = MigrateHumanoidBone(humanoidBone); break; + case "rightMiddleProximal": humanoid.HumanBones.RightMiddleProximal = MigrateHumanoidBone(humanoidBone); break; + case "rightMiddleIntermediate": humanoid.HumanBones.RightMiddleIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "rightMiddleDistal": humanoid.HumanBones.RightMiddleDistal = MigrateHumanoidBone(humanoidBone); break; + case "rightRingProximal": humanoid.HumanBones.RightRingProximal = MigrateHumanoidBone(humanoidBone); break; + case "rightRingIntermediate": humanoid.HumanBones.RightRingIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "rightRingDistal": humanoid.HumanBones.RightRingDistal = MigrateHumanoidBone(humanoidBone); break; + case "rightLittleProximal": humanoid.HumanBones.RightLittleProximal = MigrateHumanoidBone(humanoidBone); break; + case "rightLittleIntermediate": humanoid.HumanBones.RightLittleIntermediate = MigrateHumanoidBone(humanoidBone); break; + case "rightLittleDistal": humanoid.HumanBones.RightLittleDistal = MigrateHumanoidBone(humanoidBone); break; + case "upperChest": humanoid.HumanBones.UpperChest = MigrateHumanoidBone(humanoidBone); break; + default: throw new NotImplementedException($"unknown bone: {boneType}"); + } + } + + return humanoid; + } + + /// + /// 互換性の無いところ + /// + /// * きつくなる方向は許す + /// * 緩くなる方向は不許可(throw) + /// + // "meta": { + // "title": "Alicia Solid", + // "version": "1.10", + // "author": "© DWANGO Co., Ltd.", + // "contactInformation": "https://3d.nicovideo.jp/alicia/", + // "reference": "", + // "texture": 7, + // "allowedUserName": "Everyone", + // "violentUssageName": "Disallow", + // "sexualUssageName": "Disallow", + // "commercialUssageName": "Allow", + // "otherPermissionUrl": "https://3d.nicovideo.jp/alicia/rule.html", + // "licenseName": "Other", + // "otherLicenseUrl": "https://3d.nicovideo.jp/alicia/rule.html" + // }, + static UniGLTF.Extensions.VRMC_vrm.Meta MigrateMeta(ListTreeNode vrm0) + { + var meta = new UniGLTF.Extensions.VRMC_vrm.Meta + { + AllowPoliticalOrReligiousUsage = false, + AllowExcessivelySexualUsage = false, + AllowExcessivelyViolentUsage = false, + AllowRedistribution = false, + AvatarPermission = UniGLTF.Extensions.VRMC_vrm.AvatarPermissionType.onlyAuthor, + CommercialUsage = UniGLTF.Extensions.VRMC_vrm.CommercialUsageType.personalNonProfit, + CreditNotation = UniGLTF.Extensions.VRMC_vrm.CreditNotationType.required, + Modification = UniGLTF.Extensions.VRMC_vrm.ModificationType.prohibited, + }; + + foreach (var kv in vrm0.ObjectItems()) + { + var key = kv.Key.GetString(); + switch (key) + { + case "title": meta.Name = kv.Value.GetString(); break; + case "version": meta.Version = kv.Value.GetString(); break; + case "author": meta.Authors = new List() { kv.Value.GetString() }; break; + case "contactInformation": meta.ContactInformation = kv.Value.GetString(); break; + case "reference": meta.References = new List() { kv.Value.GetString() }; break; + case "texture": meta.ThumbnailImage = kv.Value.GetInt32(); break; + + case "allowedUserName": + { + var allowdUserType = kv.Value.GetString(); + switch (allowdUserType) + { + case "Everyone": meta.AvatarPermission = UniGLTF.Extensions.VRMC_vrm.AvatarPermissionType.everyone; break; + default: throw new NotImplementedException($"allowedUser: {allowdUserType}"); + } + } + break; + + case "violentUssageName": meta.AllowExcessivelyViolentUsage = kv.Value.GetString().ToLower() == "allow"; break; + case "sexualUssageName": meta.AllowExcessivelySexualUsage = kv.Value.GetString().ToLower() == "allow"; break; + case "commercialUssageName": + { + var commercialUssageType = kv.Value.GetString(); + switch (commercialUssageType) + { + case "Allow": meta.CommercialUsage = UniGLTF.Extensions.VRMC_vrm.CommercialUsageType.personalProfit; break; + default: meta.CommercialUsage = UniGLTF.Extensions.VRMC_vrm.CommercialUsageType.personalNonProfit; break; + } + } + break; + + case "otherPermissionUrl": + { + // TODO + // var url = kv.Value.GetString(); + // if (!String.IsNullOrWhiteSpace(url)) + // { + // throw new NotImplementedException("otherPermissionUrl not allowd"); + // } + } + break; + + case "otherLicenseUrl": meta.OtherLicenseUrl = kv.Value.GetString(); break; + + case "licenseName": + { + // TODO + // CreditNotation = CreditNotationType.required, + } + break; + + default: + throw new NotImplementedException(key); + } + } + + return meta; + } + + static string GetLicenseUrl(ListTreeNode vrm0) + { + string l0 = default; + string l1 = default; + foreach (var kv in vrm0.ObjectItems()) + { + switch (kv.Key.GetString()) + { + case "otherLicenseUrl": + l0 = kv.Value.GetString(); + break; + + case "otherPermissionUrl": + l1 = kv.Value.GetString(); + break; + } + } + if (!string.IsNullOrWhiteSpace(l0)) + { + return l0; + } + if (!string.IsNullOrWhiteSpace(l1)) + { + return l1; + } + return ""; + } + + static float[] ReverseZ(ListTreeNode xyz) + { + return new float[]{ + xyz["x"].GetSingle(), + xyz["y"].GetSingle(), + -xyz["z"].GetSingle(), + }; + } + + static IEnumerable EnumJoint(List nodes, UniGLTF.glTFNode node) + { + yield return node; + + if (node.children != null && node.children.Length > 0) + { + foreach (var x in EnumJoint(nodes, nodes[node.children[0]])) + { + yield return x; + } + } + } + + static UniGLTF.Extensions.VRMC_springBone.VRMC_springBone MigrateSpringBone(UniGLTF.glTF gltf, ListTreeNode sa) + { + var colliderNodes = new List(); + + foreach (var x in sa["colliderGroups"].ArrayItems()) + { + var node = x["node"].GetInt32(); + colliderNodes.Add(node); + var gltfNode = gltf.nodes[node]; + + var collider = new UniGLTF.Extensions.VRMC_node_collider.VRMC_node_collider() + { + Shapes = new List(), + }; + + // { + // "node": 14, + // "colliders": [ + // { + // "offset": { + // "x": 0.025884293, + // "y": -0.120000005, + // "z": 0 + // }, + // "radius": 0.05 + // }, + // { + // "offset": { + // "x": -0.02588429, + // "y": -0.120000005, + // "z": 0 + // }, + // "radius": 0.05 + // }, + // { + // "offset": { + // "x": 0, + // "y": -0.0220816135, + // "z": 0 + // }, + // "radius": 0.08 + // } + // ] + // }, + foreach (var y in x["colliders"].ArrayItems()) + { + collider.Shapes.Add(new UniGLTF.Extensions.VRMC_node_collider.ColliderShape + { + Sphere = new UniGLTF.Extensions.VRMC_node_collider.ColliderShapeSphere + { + Offset = ReverseZ(y["offset"]), + Radius = y["radius"].GetSingle() + } + }); + } + + if (!(gltfNode.extensions is UniGLTF.glTFExtensionExport extensions)) + { + extensions = new UniGLTF.glTFExtensionExport(); + gltfNode.extensions = extensions; + } + + var f = new JsonFormatter(); + UniGLTF.Extensions.VRMC_node_collider.GltfSerializer.Serialize(f, collider); + extensions.Add(UniGLTF.Extensions.VRMC_node_collider.VRMC_node_collider.ExtensionName, f.GetStoreBytes()); + } + + var springBone = new UniGLTF.Extensions.VRMC_springBone.VRMC_springBone + { + Springs = new List(), + }; + foreach (var x in sa["boneGroups"].ArrayItems()) + { + // { + // "comment": "", + // "stiffiness": 2, + // "gravityPower": 0, + // "gravityDir": { + // "x": 0, + // "y": -1, + // "z": 0 + // }, + // "dragForce": 0.7, + // "center": -1, + // "hitRadius": 0.02, + // "bones": [ + // 97, + // 99, + // 101, + // 113, + // 114 + // ], + // "colliderGroups": [ + // 3, + // 4, + // 5 + // ] + // }, + foreach (var y in x["bones"].ArrayItems()) + { + var spring = new UniGLTF.Extensions.VRMC_springBone.Spring + { + Name = x["comment"].GetString(), + Colliders = x["colliderGroups"].ArrayItems().Select(z => colliderNodes[z.GetInt32()]).ToArray(), + Joints = new List(), + }; + + foreach (var z in EnumJoint(gltf.nodes, gltf.nodes[y.GetInt32()])) + { + spring.Joints.Add(new UniGLTF.Extensions.VRMC_springBone.SpringBoneJoint + { + Node = gltf.nodes.IndexOf(z), + DragForce = x["dragForce"].GetSingle(), + GravityDir = ReverseZ(x["gravityDir"]), + GravityPower = x["gravityPower"].GetSingle(), + HitRadius = x["hitRadius"].GetSingle(), + Stiffness = x["stiffiness"].GetSingle(), + }); + } + + springBone.Springs.Add(spring); + } + } + + return springBone; + } + + public static byte[] Migrate(byte[] src) + { + var glb = UniGLTF.Glb.Parse(src); + var json = glb.Json.Bytes.ParseAsJson(); + var gltf = UniGLTF.GltfDeserializer.Deserialize(json); + + var extensions = new UniGLTF.glTFExtensionExport(); + { + var vrm0 = json["extensions"]["VRM"]; + + { + // vrm + var vrm1 = new UniGLTF.Extensions.VRMC_vrm.VRMC_vrm(); + vrm1.Meta = MigrateMeta(vrm0["meta"]); + vrm1.Humanoid = MigrateHumanoid(vrm0["humanoid"]); + + var f = new JsonFormatter(); + UniGLTF.Extensions.VRMC_vrm.GltfSerializer.Serialize(f, vrm1); + extensions.Add(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm.ExtensionName, f.GetStoreBytes()); + } + { + // springBone & collider + var vrm1 = MigrateSpringBone(gltf, json["extensions"]["VRM"]["secondaryAnimation"]); + + var f = new JsonFormatter(); + UniGLTF.Extensions.VRMC_springBone.GltfSerializer.Serialize(f, vrm1); + extensions.Add(UniGLTF.Extensions.VRMC_springBone.VRMC_springBone.ExtensionName, f.GetStoreBytes()); + } + { + // MToon + } + { + // constraint + } + } + + ArraySegment vrm1Json = default; + { + gltf.extensions = extensions; + + var f = new JsonFormatter(); + UniGLTF.GltfSerializer.Serialize(f, gltf); + vrm1Json = f.GetStoreBytes(); + } + + return UniGLTF.Glb.Create(vrm1Json, glb.Binary.Bytes).ToBytes(); + } + + #region for UnitTest + public class MigrationException : Exception + { + public MigrationException(string key, string value) : base($"{key}: {value}") + { + } + } + + public static void CheckBone(string bone, ListTreeNode vrm0, UniGLTF.Extensions.VRMC_vrm.HumanBone vrm1) + { + var vrm0NodeIndex = vrm0["node"].GetInt32(); + if (vrm0NodeIndex != vrm1.Node) + { + throw new Exception($"different {bone}: {vrm0NodeIndex} != {vrm1.Node}"); + } + } + + public static void CheckHumanoid(ListTreeNode vrm0, UniGLTF.Extensions.VRMC_vrm.Humanoid vrm1) + { + foreach (var humanoidBone in vrm0["humanBones"].ArrayItems()) + { + var boneType = humanoidBone["bone"].GetString(); + switch (boneType) + { + case "hips": CheckBone(boneType, humanoidBone, vrm1.HumanBones.Hips); break; + case "leftUpperLeg": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftUpperLeg); break; + case "rightUpperLeg": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightUpperLeg); break; + case "leftLowerLeg": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftLowerLeg); break; + case "rightLowerLeg": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightLowerLeg); break; + case "leftFoot": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftFoot); break; + case "rightFoot": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightFoot); break; + case "spine": CheckBone(boneType, humanoidBone, vrm1.HumanBones.Spine); break; + case "chest": CheckBone(boneType, humanoidBone, vrm1.HumanBones.Chest); break; + case "neck": CheckBone(boneType, humanoidBone, vrm1.HumanBones.Neck); break; + case "head": CheckBone(boneType, humanoidBone, vrm1.HumanBones.Head); break; + case "leftShoulder": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftShoulder); break; + case "rightShoulder": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightShoulder); break; + case "leftUpperArm": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftUpperArm); break; + case "rightUpperArm": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightUpperArm); break; + case "leftLowerArm": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftLowerArm); break; + case "rightLowerArm": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightLowerArm); break; + case "leftHand": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftHand); break; + case "rightHand": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightHand); break; + case "leftToes": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftToes); break; + case "rightToes": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightToes); break; + case "leftEye": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftEye); break; + case "rightEye": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightEye); break; + case "jaw": CheckBone(boneType, humanoidBone, vrm1.HumanBones.Jaw); break; + case "leftThumbProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftThumbProximal); break; + case "leftThumbIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftThumbIntermediate); break; + case "leftThumbDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftThumbDistal); break; + case "leftIndexProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftIndexProximal); break; + case "leftIndexIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftIndexIntermediate); break; + case "leftIndexDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftIndexDistal); break; + case "leftMiddleProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftMiddleProximal); break; + case "leftMiddleIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftMiddleIntermediate); break; + case "leftMiddleDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftMiddleDistal); break; + case "leftRingProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftRingProximal); break; + case "leftRingIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftRingIntermediate); break; + case "leftRingDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftRingDistal); break; + case "leftLittleProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftLittleProximal); break; + case "leftLittleIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftLittleIntermediate); break; + case "leftLittleDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.LeftLittleDistal); break; + case "rightThumbProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightThumbProximal); break; + case "rightThumbIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightThumbIntermediate); break; + case "rightThumbDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightThumbDistal); break; + case "rightIndexProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightIndexProximal); break; + case "rightIndexIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightIndexIntermediate); break; + case "rightIndexDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightIndexDistal); break; + case "rightMiddleProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightMiddleProximal); break; + case "rightMiddleIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightMiddleIntermediate); break; + case "rightMiddleDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightMiddleDistal); break; + case "rightRingProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightRingProximal); break; + case "rightRingIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightRingIntermediate); break; + case "rightRingDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightRingDistal); break; + case "rightLittleProximal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightLittleProximal); break; + case "rightLittleIntermediate": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightLittleIntermediate); break; + case "rightLittleDistal": CheckBone(boneType, humanoidBone, vrm1.HumanBones.RightLittleDistal); break; + case "upperChest": CheckBone(boneType, humanoidBone, vrm1.HumanBones.UpperChest); break; + default: throw new MigrationException("humanonoid.humanBones[*].bone", boneType); + } + } + } + + static bool IsSingleList(string key, string lhs, List rhs) + { + if (rhs.Count != 1) throw new MigrationException(key, $"{rhs.Count}"); + return lhs == rhs[0]; + } + + static string AvatarPermission(string key, UniGLTF.Extensions.VRMC_vrm.AvatarPermissionType x) + { + switch (x) + { + case UniGLTF.Extensions.VRMC_vrm.AvatarPermissionType.everyone: return "Everyone"; + // case AvatarPermissionType.onlyAuthor: return "OnlyAuthor"; + // case AvatarPermissionType.explicitlyLicensedPerson: return "Explicited"; + } + throw new MigrationException(key, $"{x}"); + } + + public static void CheckMeta(ListTreeNode vrm0, UniGLTF.Extensions.VRMC_vrm.Meta vrm1) + { + if (vrm0["title"].GetString() != vrm1.Name) throw new MigrationException("meta.title", vrm1.Name); + if (vrm0["version"].GetString() != vrm1.Version) throw new MigrationException("meta.version", vrm1.Version); + if (!IsSingleList("meta.author", vrm0["author"].GetString(), vrm1.Authors)) throw new MigrationException("meta.author", $"{vrm1.Authors}"); + if (vrm0["contactInformation"].GetString() != vrm1.ContactInformation) throw new MigrationException("meta.contactInformation", vrm1.ContactInformation); + if (!IsSingleList("meta.reference", vrm0["reference"].GetString(), vrm1.References)) throw new MigrationException("meta.reference", $"{vrm1.References}"); + if (vrm0["texture"].GetInt32() != vrm1.ThumbnailImage) throw new MigrationException("meta.texture", $"{vrm1.ThumbnailImage}"); + + if (vrm0["allowedUserName"].GetString() != AvatarPermission("meta.allowedUserName", vrm1.AvatarPermission)) throw new MigrationException("meta.allowedUserName", $"{vrm1.AvatarPermission}"); + if (vrm0["violentUssageName"].GetString() == "Allow" != vrm1.AllowExcessivelyViolentUsage) throw new MigrationException("meta.violentUssageName", $"{vrm1.AllowExcessivelyViolentUsage}"); + if (vrm0["sexualUssageName"].GetString() == "Allow" != vrm1.AllowExcessivelySexualUsage) throw new MigrationException("meta.sexualUssageName", $"{vrm1.AllowExcessivelyViolentUsage}"); + + if (vrm0["commercialUssageName"].GetString() == "Allow") + { + if (vrm1.CommercialUsage == UniGLTF.Extensions.VRMC_vrm.CommercialUsageType.personalNonProfit) + { + throw new MigrationException("meta.commercialUssageName", $"{vrm1.CommercialUsage}"); + } + } + else + { + if (vrm1.CommercialUsage == UniGLTF.Extensions.VRMC_vrm.CommercialUsageType.corporation + || vrm1.CommercialUsage == UniGLTF.Extensions.VRMC_vrm.CommercialUsageType.personalProfit) + { + throw new MigrationException("meta.commercialUssageName", $"{vrm1.CommercialUsage}"); + } + } + + if (GetLicenseUrl(vrm0) != vrm1.OtherLicenseUrl) throw new MigrationException("meta.otherLicenseUrl", vrm1.OtherLicenseUrl); + + switch (vrm0["licenseName"].GetString()) + { + case "Other": + { + if (vrm1.Modification != UniGLTF.Extensions.VRMC_vrm.ModificationType.prohibited) throw new MigrationException("meta.licenceName", $"{vrm1.Modification}"); + if (vrm1.AllowRedistribution.Value) throw new MigrationException("meta.liceneName", $"{vrm1.Modification}"); + break; + } + + default: + throw new NotImplementedException(); + } + } + + public static void Check(ListTreeNode vrm0, UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm1) + { + Migration.CheckMeta(vrm0["meta"], vrm1.Meta); + Migration.CheckHumanoid(vrm0["humanoid"], vrm1.Humanoid); + } + + public static void Check(ListTreeNode vrm0, UniGLTF.Extensions.VRMC_springBone.VRMC_springBone vrm1, List nodes) + { + // Migration.CheckSpringBone(vrm0["secondaryAnimation"], vrm1.sp) + } + #endregion + } +} diff --git a/Assets/VRM10/Runtime/Migration/Migration.cs.meta b/Assets/VRM10/Runtime/Migration/Migration.cs.meta new file mode 100644 index 0000000000..09c3438043 --- /dev/null +++ b/Assets/VRM10/Runtime/Migration/Migration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab1292ee15555c249af9108c10133c99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Scenes/ExportDebugUtil.cs b/Assets/VRM10/Runtime/Scenes/ExportDebugUtil.cs index a15d106033..f68cc24c0a 100644 --- a/Assets/VRM10/Runtime/Scenes/ExportDebugUtil.cs +++ b/Assets/VRM10/Runtime/Scenes/ExportDebugUtil.cs @@ -28,7 +28,7 @@ public static string GetJsonString(VrmLib.Model model) // vrm = false }; var glbBytes10 = exporter10.Export(model, option); - var glb10 = VrmLib.Glb.Parse(glbBytes10); + var glb10 = UniGLTF.Glb.Parse(glbBytes10); return System.Text.Encoding.UTF8.GetString(glb10.Json.Bytes.Array, glb10.Json.Bytes.Offset, glb10.Json.Bytes.Count); } } diff --git a/Assets/VRM10/Runtime/UnityBuilder/ComponentBuilder.cs b/Assets/VRM10/Runtime/UnityBuilder/ComponentBuilder.cs index 47c078174f..6c54bbb0aa 100644 --- a/Assets/VRM10/Runtime/UnityBuilder/ComponentBuilder.cs +++ b/Assets/VRM10/Runtime/UnityBuilder/ComponentBuilder.cs @@ -184,121 +184,133 @@ public static void Build10(VrmLib.Model model, ModelAsset asset) // firstPerson { // VRMFirstPerson - controller.FirstPerson.Renderers = model.Vrm.FirstPerson.Annotations.Select(x => - new UniVRM10.RendererFirstPersonFlags() - { - Renderer = asset.Map.Renderers[x.Node], - FirstPersonFlag = x.FirstPersonFlag - } - ).ToList(); - - // VRMLookAtApplyer - controller.LookAt.OffsetFromHead = model.Vrm.LookAt.OffsetFromHeadBone.ToUnityVector3(); - if (model.Vrm.LookAt.LookAtType == VrmLib.LookAtType.Expression) + if (model.Vrm.FirstPerson != null) { - var lookAtApplyer = controller; - lookAtApplyer.LookAt.LookAtType = VRM10ControllerLookAt.LookAtTypes.Expression; - lookAtApplyer.LookAt.HorizontalOuter = new UniVRM10.CurveMapper( - model.Vrm.LookAt.HorizontalOuter.InputMaxValue, - model.Vrm.LookAt.HorizontalOuter.OutputScaling); - lookAtApplyer.LookAt.VerticalUp = new UniVRM10.CurveMapper( - model.Vrm.LookAt.VerticalUp.InputMaxValue, - model.Vrm.LookAt.VerticalUp.OutputScaling); - lookAtApplyer.LookAt.VerticalDown = new UniVRM10.CurveMapper( - model.Vrm.LookAt.VerticalDown.InputMaxValue, - model.Vrm.LookAt.VerticalDown.OutputScaling); - } - else if (model.Vrm.LookAt.LookAtType == VrmLib.LookAtType.Bone) - { - var lookAtBoneApplyer = controller; - lookAtBoneApplyer.LookAt.HorizontalInner = new UniVRM10.CurveMapper( - model.Vrm.LookAt.HorizontalInner.InputMaxValue, - model.Vrm.LookAt.HorizontalInner.OutputScaling); - lookAtBoneApplyer.LookAt.HorizontalOuter = new UniVRM10.CurveMapper( - model.Vrm.LookAt.HorizontalOuter.InputMaxValue, - model.Vrm.LookAt.HorizontalOuter.OutputScaling); - lookAtBoneApplyer.LookAt.VerticalUp = new UniVRM10.CurveMapper( - model.Vrm.LookAt.VerticalUp.InputMaxValue, - model.Vrm.LookAt.VerticalUp.OutputScaling); - lookAtBoneApplyer.LookAt.VerticalDown = new UniVRM10.CurveMapper( - model.Vrm.LookAt.VerticalDown.InputMaxValue, - model.Vrm.LookAt.VerticalDown.OutputScaling); + controller.FirstPerson.Renderers = model.Vrm.FirstPerson.Annotations.Select(x => + new UniVRM10.RendererFirstPersonFlags() + { + Renderer = asset.Map.Renderers[x.Node], + FirstPersonFlag = x.FirstPersonFlag + } + ).ToList(); } - else + + // VRMLookAtApplyer + if (model.Vrm.LookAt != null) { - throw new NotImplementedException(); + controller.LookAt.OffsetFromHead = model.Vrm.LookAt.OffsetFromHeadBone.ToUnityVector3(); + if (model.Vrm.LookAt.LookAtType == VrmLib.LookAtType.Expression) + { + var lookAtApplyer = controller; + lookAtApplyer.LookAt.LookAtType = VRM10ControllerLookAt.LookAtTypes.Expression; + lookAtApplyer.LookAt.HorizontalOuter = new UniVRM10.CurveMapper( + model.Vrm.LookAt.HorizontalOuter.InputMaxValue, + model.Vrm.LookAt.HorizontalOuter.OutputScaling); + lookAtApplyer.LookAt.VerticalUp = new UniVRM10.CurveMapper( + model.Vrm.LookAt.VerticalUp.InputMaxValue, + model.Vrm.LookAt.VerticalUp.OutputScaling); + lookAtApplyer.LookAt.VerticalDown = new UniVRM10.CurveMapper( + model.Vrm.LookAt.VerticalDown.InputMaxValue, + model.Vrm.LookAt.VerticalDown.OutputScaling); + } + else if (model.Vrm.LookAt.LookAtType == VrmLib.LookAtType.Bone) + { + var lookAtBoneApplyer = controller; + lookAtBoneApplyer.LookAt.HorizontalInner = new UniVRM10.CurveMapper( + model.Vrm.LookAt.HorizontalInner.InputMaxValue, + model.Vrm.LookAt.HorizontalInner.OutputScaling); + lookAtBoneApplyer.LookAt.HorizontalOuter = new UniVRM10.CurveMapper( + model.Vrm.LookAt.HorizontalOuter.InputMaxValue, + model.Vrm.LookAt.HorizontalOuter.OutputScaling); + lookAtBoneApplyer.LookAt.VerticalUp = new UniVRM10.CurveMapper( + model.Vrm.LookAt.VerticalUp.InputMaxValue, + model.Vrm.LookAt.VerticalUp.OutputScaling); + lookAtBoneApplyer.LookAt.VerticalDown = new UniVRM10.CurveMapper( + model.Vrm.LookAt.VerticalDown.InputMaxValue, + model.Vrm.LookAt.VerticalDown.OutputScaling); + } + else + { + throw new NotImplementedException(); + } } } // springBone + if (model.Vrm.SpringBone != null) { - var colliders = new Dictionary(); - if (model.Vrm.SpringBone != null) + foreach (var vrmSpring in model.Vrm.SpringBone.Springs) { - foreach (var colliderGroup in model.Vrm.SpringBone.Colliders) + // create a spring + var springBone = new UniVRM10.VRM10SpringBone(); + springBone.m_comment = vrmSpring.Comment; + if (vrmSpring.Origin != null && asset.Map.Nodes.TryGetValue(vrmSpring.Origin, out GameObject origin)) { - var go = asset.Map.Nodes[colliderGroup.Node]; - var springBoneColliderGroup = go.AddComponent(); + springBone.m_center = origin.transform; + } + controller.SpringBone.Springs.Add(springBone); - springBoneColliderGroup.Colliders = colliderGroup.Colliders.Select(x => + // create colliders for the spring + foreach (var vrmSpringBoneCollider in vrmSpring.Colliders) + { + var go = asset.Map.Nodes[vrmSpringBoneCollider.Node]; + var springBoneColliderGroup = go.GetComponent(); + if (springBoneColliderGroup != null) + { + // already setup + } + else { - switch (x.ColliderType) + // new collider + springBoneColliderGroup = go.AddComponent(); + + // add collider shapes + springBoneColliderGroup.Colliders.Clear(); + foreach (var x in vrmSpringBoneCollider.Colliders) { - case VrmLib.VrmSpringBoneColliderTypes.Sphere: - return new UniVRM10.SpringBoneCollider() - { - ColliderType = SpringBoneColliderTypes.Sphere, - Offset = x.Offset.ToUnityVector3(), - Radius = x.Radius - }; + switch (x.ColliderType) + { + case VrmLib.VrmSpringBoneColliderTypes.Sphere: + springBoneColliderGroup.Colliders.Add(new UniVRM10.VRM10SpringBoneCollider() + { + ColliderType = VRM10SpringBoneColliderTypes.Sphere, + Offset = x.Offset.ToUnityVector3(), + Radius = x.Radius + }); + break; - case VrmLib.VrmSpringBoneColliderTypes.Capsule: - return new UniVRM10.SpringBoneCollider() - { - ColliderType = SpringBoneColliderTypes.Capsule, - Offset = x.Offset.ToUnityVector3(), - Radius = x.Radius, - Tail = x.CapsuleTail.ToUnityVector3(), - }; + case VrmLib.VrmSpringBoneColliderTypes.Capsule: + springBoneColliderGroup.Colliders.Add(new UniVRM10.VRM10SpringBoneCollider() + { + ColliderType = VRM10SpringBoneColliderTypes.Capsule, + Offset = x.Offset.ToUnityVector3(), + Radius = x.Radius, + Tail = x.CapsuleTail.ToUnityVector3(), + }); + break; - default: - throw new NotImplementedException(); + default: + throw new NotImplementedException(); + } } - }).ToArray(); ; - - colliders.Add(colliderGroup, springBoneColliderGroup); + } + springBone.ColliderGroups.Add(springBoneColliderGroup); } - } - GameObject springBoneObject = null; - var springBoneTransform = asset.Root.transform.GetChildren().FirstOrDefault(x => x.name == "SpringBone"); - if (springBoneTransform == null) - { - springBoneObject = new GameObject("SpringBone"); - } - else - { - springBoneObject = springBoneTransform.gameObject; - } - - springBoneObject.transform.SetParent(asset.Root.transform); - if (model.Vrm.SpringBone != null) - { - foreach (var spring in model.Vrm.SpringBone.Springs) + // create joint for the spring + foreach (var vrmJoint in vrmSpring.Joints) { - var springBoneComponent = springBoneObject.AddComponent(); - springBoneComponent.m_comment = spring.Comment; - springBoneComponent.m_stiffnessForce = spring.Stiffness; - springBoneComponent.m_gravityPower = spring.GravityPower; - springBoneComponent.m_gravityDir = spring.GravityDir.ToUnityVector3(); - springBoneComponent.m_dragForce = spring.DragForce; - if (spring.Origin != null && asset.Map.Nodes.TryGetValue(spring.Origin, out GameObject origin)) - { - springBoneComponent.m_center = origin.transform; - } - springBoneComponent.RootBones = spring.Bones.Select(x => asset.Map.Nodes[x].transform).ToList(); - springBoneComponent.m_hitRadius = spring.HitRadius; - springBoneComponent.ColliderGroups = spring.Colliders.Select(x => colliders[x]).ToArray(); + var go = asset.Map.Nodes[vrmJoint.Node]; + var joint = new VRM10SpringJoint(go.transform); + + joint.m_stiffnessForce = vrmJoint.Stiffness; + joint.m_gravityPower = vrmJoint.GravityPower; + joint.m_gravityDir = vrmJoint.GravityDir.ToUnityVector3(); + joint.m_dragForce = vrmJoint.DragForce; + joint.m_jointRadius = vrmJoint.HitRadius; + joint.m_exclude = vrmJoint.Exclude; + + springBone.Joints.Add(joint); } } } diff --git a/Assets/VRM10/Runtime/UnityBuilder/Editor/ScriptedImporter/GltfScriptedImporter.cs b/Assets/VRM10/Runtime/UnityBuilder/Editor/ScriptedImporter/GltfScriptedImporter.cs index f006ab7921..2faf89e8a8 100644 --- a/Assets/VRM10/Runtime/UnityBuilder/Editor/ScriptedImporter/GltfScriptedImporter.cs +++ b/Assets/VRM10/Runtime/UnityBuilder/Editor/ScriptedImporter/GltfScriptedImporter.cs @@ -83,7 +83,7 @@ public override void OnImportAsset(AssetImportContext ctx) private Model CreateGlbModel(string path) { var bytes = File.ReadAllBytes(path); - if (!VrmLib.Glb.TryParse(bytes, out VrmLib.Glb glb, out Exception ex)) + if (!UniGLTF.Glb.TryParse(bytes, out UniGLTF.Glb glb, out Exception ex)) { throw ex; } diff --git a/Assets/VRM10/Runtime/UnityBuilder/VrmLoader.cs b/Assets/VRM10/Runtime/UnityBuilder/VrmLoader.cs index f2889f8f4b..1c01594759 100644 --- a/Assets/VRM10/Runtime/UnityBuilder/VrmLoader.cs +++ b/Assets/VRM10/Runtime/UnityBuilder/VrmLoader.cs @@ -24,7 +24,7 @@ public static Model CreateVrmModel(string path) public static Model CreateVrmModel(byte[] bytes, FileInfo path) { - if (!Glb.TryParse(bytes, out Glb glb, out Exception ex)) + if (!UniGLTF.Glb.TryParse(bytes, out UniGLTF.Glb glb, out Exception ex)) { throw ex; } diff --git a/Assets/VRM10/Runtime/VRMConverter/RuntimeVrmConverter.cs b/Assets/VRM10/Runtime/VRMConverter/RuntimeVrmConverter.cs index e38e53a5b5..978572936f 100644 --- a/Assets/VRM10/Runtime/VRMConverter/RuntimeVrmConverter.cs +++ b/Assets/VRM10/Runtime/VRMConverter/RuntimeVrmConverter.cs @@ -258,140 +258,133 @@ public VrmLib.Model ToModelFrom10(GameObject root, VRM10MetaObject metaObject = // blendShape var controller = root.GetComponent(); + if (controller != null) { - Model.Vrm.ExpressionManager = new VrmLib.ExpressionManager(); - if (controller != null) { - foreach (var clip in controller.Expression.ExpressionAvatar.Clips) + Model.Vrm.ExpressionManager = new VrmLib.ExpressionManager(); + if (controller != null) { - var expression = ToVrmLib(clip, root); - if (expression != null) + foreach (var clip in controller.Expression.ExpressionAvatar.Clips) { - Model.Vrm.ExpressionManager.ExpressionList.Add(expression); + var expression = ToVrmLib(clip, root); + if (expression != null) + { + Model.Vrm.ExpressionManager.ExpressionList.Add(expression); + } } } } - } - // firstPerson - { - var firstPerson = new VrmLib.FirstPerson(); - if (controller != null) + // firstPerson { - foreach (var annotation in controller.FirstPerson.Renderers) + var firstPerson = new VrmLib.FirstPerson(); + if (controller != null) { - firstPerson.Annotations.Add( - new VrmLib.FirstPersonMeshAnnotation(Nodes[annotation.Renderer.gameObject], - annotation.FirstPersonFlag) - ); + foreach (var annotation in controller.FirstPerson.Renderers) + { + firstPerson.Annotations.Add( + new VrmLib.FirstPersonMeshAnnotation(Nodes[annotation.Renderer.gameObject], + annotation.FirstPersonFlag) + ); + } + Model.Vrm.FirstPerson = firstPerson; } - Model.Vrm.FirstPerson = firstPerson; } - } - // lookAt - { - var lookAt = new VrmLib.LookAt(); - if (controller != null) + // lookAt { - if (controller.LookAt.LookAtType == VRM10ControllerLookAt.LookAtTypes.Expression) - { - lookAt.HorizontalInner = new VrmLib.LookAtRangeMap(); - lookAt.HorizontalOuter = new VrmLib.LookAtRangeMap() - { - InputMaxValue = controller.LookAt.HorizontalOuter.CurveXRangeDegree, - OutputScaling = controller.LookAt.HorizontalOuter.CurveYRangeDegree - }; - lookAt.VerticalUp = new VrmLib.LookAtRangeMap() - { - InputMaxValue = controller.LookAt.VerticalUp.CurveXRangeDegree, - OutputScaling = controller.LookAt.VerticalUp.CurveYRangeDegree, - }; - lookAt.VerticalDown = new VrmLib.LookAtRangeMap() - { - InputMaxValue = controller.LookAt.VerticalDown.CurveXRangeDegree, - OutputScaling = controller.LookAt.VerticalDown.CurveYRangeDegree, - }; - } - else if (controller.LookAt.LookAtType == VRM10ControllerLookAt.LookAtTypes.Bone) + var lookAt = new VrmLib.LookAt(); + if (controller != null) { - lookAt.HorizontalInner = new VrmLib.LookAtRangeMap() + if (controller.LookAt.LookAtType == VRM10ControllerLookAt.LookAtTypes.Expression) { - InputMaxValue = controller.LookAt.HorizontalInner.CurveXRangeDegree, - OutputScaling = controller.LookAt.HorizontalInner.CurveYRangeDegree - }; - lookAt.HorizontalOuter = new VrmLib.LookAtRangeMap() - { - InputMaxValue = controller.LookAt.HorizontalOuter.CurveXRangeDegree, - OutputScaling = controller.LookAt.HorizontalOuter.CurveYRangeDegree - }; - lookAt.VerticalUp = new VrmLib.LookAtRangeMap() - { - InputMaxValue = controller.LookAt.VerticalUp.CurveXRangeDegree, - OutputScaling = controller.LookAt.VerticalUp.CurveYRangeDegree, - }; - lookAt.VerticalDown = new VrmLib.LookAtRangeMap() + lookAt.HorizontalInner = new VrmLib.LookAtRangeMap(); + lookAt.HorizontalOuter = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.HorizontalOuter.CurveXRangeDegree, + OutputScaling = controller.LookAt.HorizontalOuter.CurveYRangeDegree + }; + lookAt.VerticalUp = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.VerticalUp.CurveXRangeDegree, + OutputScaling = controller.LookAt.VerticalUp.CurveYRangeDegree, + }; + lookAt.VerticalDown = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.VerticalDown.CurveXRangeDegree, + OutputScaling = controller.LookAt.VerticalDown.CurveYRangeDegree, + }; + } + else if (controller.LookAt.LookAtType == VRM10ControllerLookAt.LookAtTypes.Bone) { - InputMaxValue = controller.LookAt.VerticalDown.CurveXRangeDegree, - OutputScaling = controller.LookAt.VerticalDown.CurveYRangeDegree, - }; + lookAt.HorizontalInner = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.HorizontalInner.CurveXRangeDegree, + OutputScaling = controller.LookAt.HorizontalInner.CurveYRangeDegree + }; + lookAt.HorizontalOuter = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.HorizontalOuter.CurveXRangeDegree, + OutputScaling = controller.LookAt.HorizontalOuter.CurveYRangeDegree + }; + lookAt.VerticalUp = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.VerticalUp.CurveXRangeDegree, + OutputScaling = controller.LookAt.VerticalUp.CurveYRangeDegree, + }; + lookAt.VerticalDown = new VrmLib.LookAtRangeMap() + { + InputMaxValue = controller.LookAt.VerticalDown.CurveXRangeDegree, + OutputScaling = controller.LookAt.VerticalDown.CurveYRangeDegree, + }; + } + lookAt.OffsetFromHeadBone = controller.LookAt.OffsetFromHead.ToNumericsVector3(); } - lookAt.OffsetFromHeadBone = controller.LookAt.OffsetFromHead.ToNumericsVector3(); + Model.Vrm.LookAt = lookAt; } - Model.Vrm.LookAt = lookAt; - } - // springBone - { - var springBoneColliderGroups = root.GetComponentsInChildren(); - if (springBoneColliderGroups != null) + // springBone { - Model.Vrm.SpringBone = new VrmLib.SpringBoneManager(); - var colliders = new Dictionary(); - foreach (var colliderGroup in springBoneColliderGroups) - { - var colliderGroups = colliderGroup.Colliders.Select(x => - { - switch (x.ColliderType) - { - case SpringBoneColliderTypes.Sphere: - return VrmLib.VrmSpringBoneCollider.CreateSphere(x.Offset.ToNumericsVector3(), x.Radius); - - case SpringBoneColliderTypes.Capsule: - return VrmLib.VrmSpringBoneCollider.CreateCapsule(x.Offset.ToNumericsVector3(), x.Radius, x.Tail.ToNumericsVector3()); - - default: - throw new NotImplementedException(); - } - }); - var vrmColliderGroup = new VrmLib.SpringBoneColliderGroup(Nodes[colliderGroup.gameObject], colliderGroups); - Model.Vrm.SpringBone.Colliders.Add(vrmColliderGroup); - - colliders.Add(colliderGroup, vrmColliderGroup); - } - - var springBones = root.GetComponentsInChildren(); - foreach (var springBone in springBones) + var springBoneManager = controller.SpringBone; + foreach (var springBone in springBoneManager.Springs) { var vrmSpringBone = new VrmLib.SpringBone() { Comment = springBone.m_comment, - Stiffness = springBone.m_stiffnessForce, - GravityPower = springBone.m_gravityPower, - GravityDir = springBone.m_gravityDir.ToNumericsVector3(), - DragForce = springBone.m_dragForce, Origin = (springBone.m_center != null) ? Nodes[springBone.m_center.gameObject] : null, - HitRadius = springBone.m_hitRadius, }; - foreach (var rootBone in springBone.RootBones) + foreach (var joint in springBone.Joints) { - vrmSpringBone.Bones.Add(Nodes[rootBone.gameObject]); + vrmSpringBone.Joints.Add(new VrmLib.SpringJoint(Nodes[joint.Transform.gameObject]) + { + Stiffness = joint.m_stiffnessForce, + GravityPower = joint.m_gravityPower, + GravityDir = joint.m_gravityDir.ToNumericsVector3(), + DragForce = joint.m_dragForce, + HitRadius = joint.m_jointRadius, + Exclude = joint.m_exclude, + }); } - foreach (var collider in springBone.ColliderGroups) + foreach (var colliderGroup in springBone.ColliderGroups) { - vrmSpringBone.Colliders.Add(colliders[collider]); + var colliderGroups = colliderGroup.Colliders.Select(x => + { + switch (x.ColliderType) + { + case VRM10SpringBoneColliderTypes.Sphere: + return VrmLib.VrmSpringBoneCollider.CreateSphere(x.Offset.ToNumericsVector3(), x.Radius); + + case VRM10SpringBoneColliderTypes.Capsule: + return VrmLib.VrmSpringBoneCollider.CreateCapsule(x.Offset.ToNumericsVector3(), x.Radius, x.Tail.ToNumericsVector3()); + + default: + throw new NotImplementedException(); + } + }); + var vrmColliderGroup = new VrmLib.SpringBoneColliderGroup(Nodes[colliderGroup.gameObject], colliderGroups); + vrmSpringBone.Colliders.Add(vrmColliderGroup); } Model.Vrm.SpringBone.Springs.Add(vrmSpringBone); diff --git a/Assets/VRM10/Tests.PlayMode/ApiSampleTests.cs b/Assets/VRM10/Tests.PlayMode/ApiSampleTests.cs index 3a2a6a527a..c497474bd4 100644 --- a/Assets/VRM10/Tests.PlayMode/ApiSampleTests.cs +++ b/Assets/VRM10/Tests.PlayMode/ApiSampleTests.cs @@ -11,7 +11,7 @@ VrmLib.Model ReadModel(string path) { var bytes = File.ReadAllBytes(path); - if (!VrmLib.Glb.TryParse(bytes, out VrmLib.Glb glb, out Exception ex)) + if (!UniGLTF.Glb.TryParse(bytes, out UniGLTF.Glb glb, out Exception ex)) { Debug.LogError($"fail to Glb.TryParse: {path} => {ex}"); return null; diff --git a/Assets/VRM10/Tests.PlayMode/VRM10.Tests.PlayMode.asmdef b/Assets/VRM10/Tests.PlayMode/VRM10.Tests.PlayMode.asmdef index 337fbe31ce..180062d21c 100644 --- a/Assets/VRM10/Tests.PlayMode/VRM10.Tests.PlayMode.asmdef +++ b/Assets/VRM10/Tests.PlayMode/VRM10.Tests.PlayMode.asmdef @@ -2,7 +2,8 @@ "name": "VRM10.Tests.PlayMode", "references": [ "VrmLib", - "VRM10" + "VRM10", + "UniGLTF" ], "optionalUnityReferences": [ "TestAssemblies" diff --git a/Assets/VRM10/Tests/MigrationTests.cs b/Assets/VRM10/Tests/MigrationTests.cs new file mode 100644 index 0000000000..a465a66f38 --- /dev/null +++ b/Assets/VRM10/Tests/MigrationTests.cs @@ -0,0 +1,60 @@ +using System.IO; +using NUnit.Framework; +using UnityEngine; +using UniJSON; +using System; + +namespace UniVRM10 +{ + public class MigrationTests + { + static string AliciaPath + { + get + { + return Path.GetFullPath(Application.dataPath + "/../Tests/Models/Alicia_vrm-0.51/AliciaSolid_vrm-0.51.vrm") + .Replace("\\", "/"); + } + } + + static ListTreeNode GetVRM0(byte[] bytes) + { + var glb = UniGLTF.Glb.Parse(bytes); + var json = glb.Json.Bytes.ParseAsJson(); + return json["extensions"]["VRM"]; + } + + T GetExtension(UniGLTF.glTFExtension extensions, UniJSON.Utf8String key, Func, T> deserializer) + { + if (extensions is UniGLTF.glTFExtensionImport import) + { + foreach (var kv in import.ObjectItems()) + { + if (kv.Key.GetUtf8String() == key) + { + return deserializer(kv.Value); + } + } + } + + return default; + } + + [Test] + public void Migrate0to1() + { + var vrm0Bytes = File.ReadAllBytes(AliciaPath); + var vrm0Json = GetVRM0(vrm0Bytes); + + var vrm1 = Migration.Migrate(vrm0Bytes); + var glb = UniGLTF.Glb.Parse(vrm1); + var json = glb.Json.Bytes.ParseAsJson(); + var gltf = UniGLTF.GltfDeserializer.Deserialize(json); + + Migration.Check(vrm0Json, GetExtension(gltf.extensions, UniGLTF.Extensions.VRMC_vrm.VRMC_vrm.ExtensionNameUtf8, + UniGLTF.Extensions.VRMC_vrm.GltfDeserializer.Deserialize)); + Migration.Check(vrm0Json, GetExtension(gltf.extensions, UniGLTF.Extensions.VRMC_springBone.VRMC_springBone.ExtensionNameUtf8, + UniGLTF.Extensions.VRMC_springBone.GltfDeserializer.Deserialize), gltf.nodes); + } + } +} diff --git a/Assets/VRM10/Tests/MigrationTests.cs.meta b/Assets/VRM10/Tests/MigrationTests.cs.meta new file mode 100644 index 0000000000..2bfe03e08b --- /dev/null +++ b/Assets/VRM10/Tests/MigrationTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 856d7b1293642ed4dbce5341b397b74e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/vrmlib/Runtime/Extensions/ArraySegmentExtensions.cs b/Assets/VRM10/vrmlib/Runtime/Extensions/ArraySegmentExtensions.cs new file mode 100644 index 0000000000..a137534d50 --- /dev/null +++ b/Assets/VRM10/vrmlib/Runtime/Extensions/ArraySegmentExtensions.cs @@ -0,0 +1,29 @@ +using System; + +namespace VrmLib +{ + public static class ArraySegmentExtensions + { + public static ArraySegment Slice(this ArraySegment self, int start, int length) + { + if (start + length > self.Count) + { + throw new ArgumentOutOfRangeException(); + } + return new ArraySegment( + self.Array, + self.Offset + start, + length + ); + } + + public static ArraySegment Slice(this ArraySegment self, int start) + { + if (start > self.Count) + { + throw new ArgumentOutOfRangeException(); + } + return self.Slice(start, self.Count - start); + } + } +} diff --git a/Assets/VRM10/vrmlib/Runtime/Extensions/ArraySegmentExtensions.cs.meta b/Assets/VRM10/vrmlib/Runtime/Extensions/ArraySegmentExtensions.cs.meta new file mode 100644 index 0000000000..06000f6f4c --- /dev/null +++ b/Assets/VRM10/vrmlib/Runtime/Extensions/ArraySegmentExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb18d59756b3d844d8dddda3ab391541 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/vrmlib/Runtime/ImportExport/Glb.cs b/Assets/VRM10/vrmlib/Runtime/ImportExport/Glb.cs deleted file mode 100644 index 7d8b1c7794..0000000000 --- a/Assets/VRM10/vrmlib/Runtime/ImportExport/Glb.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace VrmLib -{ - /// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification - - public static class GlbHeader - { - public static readonly byte[] GLB_MAGIC = new byte[] { 0x67, 0x6C, 0x54, 0x46 }; // "glTF" - public static readonly byte[] GLB_VERSION = new byte[] { 2, 0, 0, 0 }; - - public static void WriteTo(Stream s) - { - s.Write(GLB_MAGIC, 0, GLB_MAGIC.Length); - s.Write(GLB_VERSION, 0, GLB_VERSION.Length); - } - - public static int TryParse(ArraySegment bytes, out int pos, out Exception message) - { - pos = 0; - - if (!bytes.Slice(0, 4).SequenceEqual(GLB_MAGIC)) - { - message = new FormatException("invalid magic"); - return 0; - } - pos += 4; - - if (!bytes.Slice(pos, 4).SequenceEqual(GLB_VERSION)) - { - message = new FormatException("invalid magic"); - return 0; - } - pos += 4; - - var totalLength = BitConverter.ToInt32(bytes.Array, bytes.Offset + pos); - pos += 4; - - message = null; - return totalLength; - } - } - - public enum GlbChunkType : uint - { - JSON = 0x4E4F534A, - BIN = 0x004E4942, - } - - public struct GlbChunk - { - public GlbChunkType ChunkType; - public ArraySegment Bytes; - - public GlbChunk(GlbChunkType type, ArraySegment bytes) - { - ChunkType = type; - Bytes = bytes; - } - - public static GlbChunk CreateJson(string json) - { - return CreateJson(new ArraySegment(Encoding.UTF8.GetBytes(json))); - } - - public static GlbChunk CreateJson(ArraySegment bytes) - { - return new GlbChunk(GlbChunkType.JSON, bytes); - } - - public static GlbChunk CreateBin(ArraySegment bytes) - { - return new GlbChunk(GlbChunkType.BIN, bytes); - } - - byte GetPaddingByte() - { - // chunk type - switch (ChunkType) - { - case GlbChunkType.JSON: - return 0x20; - - case GlbChunkType.BIN: - return 0x00; - - default: - throw new Exception("unknown chunk type: " + ChunkType); - } - } - - public int WriteTo(Stream s) - { - // padding - var paddingValue = Bytes.Count % 4; - var padding = (paddingValue > 0) ? 4 - paddingValue : 0; - - // size - var bytes = BitConverter.GetBytes((int)(Bytes.Count + padding)); - s.Write(bytes, 0, bytes.Length); - - // chunk type - switch (ChunkType) - { - case GlbChunkType.JSON: - s.WriteByte((byte)'J'); - s.WriteByte((byte)'S'); - s.WriteByte((byte)'O'); - s.WriteByte((byte)'N'); - break; - - case GlbChunkType.BIN: - s.WriteByte((byte)'B'); - s.WriteByte((byte)'I'); - s.WriteByte((byte)'N'); - s.WriteByte((byte)0); - break; - - default: - throw new Exception("unknown chunk type: " + ChunkType); - } - - // body - s.Write(Bytes.Array, Bytes.Offset, Bytes.Count); - - // 4byte align - var pad = GetPaddingByte(); - for (int i = 0; i < padding; ++i) - { - s.WriteByte(pad); - } - - return 4 + 4 + Bytes.Count + padding; - } - } - - public struct Glb - { - public readonly GlbChunk Json; - public readonly GlbChunk Binary; - - public Glb(GlbChunk json, GlbChunk binary) - { - if (json.ChunkType != GlbChunkType.JSON) throw new ArgumentException(); - Json = json; - if (binary.ChunkType != GlbChunkType.BIN) throw new ArgumentException(); - Binary = binary; - } - - public byte[] ToBytes() - { - using (var s = new MemoryStream()) - { - GlbHeader.WriteTo(s); - - var pos = s.Position; - s.Position += 4; // skip total size - - int size = 12; - - { - size += Json.WriteTo(s); - } - { - size += Binary.WriteTo(s); - } - - s.Position = pos; - var bytes = BitConverter.GetBytes(size); - s.Write(bytes, 0, bytes.Length); - - return s.ToArray(); - } - } - - public static GlbChunkType ToChunkType(string src) - { - switch (src) - { - case "BIN": - return GlbChunkType.BIN; - - case "JSON": - return GlbChunkType.JSON; - - default: - throw new FormatException("unknown chunk type: " + src); - } - } - - public static Glb Parse(Byte[] bytes) - { - if (TryParse(bytes, out Glb glb, out Exception ex)) - { - return glb; - } - else - { - throw ex; - } - } - - public static bool TryParse(Byte[] bytes, out Glb glb, out Exception ex) - { - return TryParse(new ArraySegment(bytes), out glb, out ex); - } - - public static bool TryParse(ArraySegment bytes, out Glb glb, out Exception ex) - { - glb = default(Glb); - if (bytes.Count == 0) - { - ex = new Exception("empty bytes"); - return false; - } - - var length = GlbHeader.TryParse(bytes, out int pos, out ex); - if (length == 0) - { - return false; - } - bytes = bytes.Slice(0, length); - - try - { - var chunks = new List(); - while (pos < bytes.Count) - { - var chunkDataSize = BitConverter.ToInt32(bytes.Array, bytes.Offset + pos); - pos += 4; - - //var type = (GlbChunkType)BitConverter.ToUInt32(bytes, pos); - var chunkTypeBytes = bytes.Slice(pos, 4).Where(x => x != 0).ToArray(); - var chunkTypeStr = Encoding.ASCII.GetString(chunkTypeBytes); - var type = ToChunkType(chunkTypeStr); - pos += 4; - - chunks.Add(new GlbChunk - { - ChunkType = type, - Bytes = bytes.Slice(pos, chunkDataSize) - }); - - pos += chunkDataSize; - } - - glb = new Glb(chunks[0], chunks[1]); - return true; - } - catch (Exception _ex) - { - ex = _ex; - return false; - } - } - - - } -} diff --git a/Assets/VRM10/vrmlib/Runtime/ImportExport/Glb.cs.meta b/Assets/VRM10/vrmlib/Runtime/ImportExport/Glb.cs.meta deleted file mode 100644 index 6a6b3fe5f7..0000000000 --- a/Assets/VRM10/vrmlib/Runtime/ImportExport/Glb.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e8b6e06dd9822ef41bab155e172fe857 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM10/vrmlib/Runtime/ImportExport/ModelLoader.cs b/Assets/VRM10/vrmlib/Runtime/ImportExport/ModelLoader.cs index 1f5d6f262f..21b5a3a62b 100644 --- a/Assets/VRM10/vrmlib/Runtime/ImportExport/ModelLoader.cs +++ b/Assets/VRM10/vrmlib/Runtime/ImportExport/ModelLoader.cs @@ -97,7 +97,9 @@ static bool LoadVrm(Model model, IVrmStorage storage) return false; } - var Vrm = new Vrm(storage.CreateVrmMeta(model.Textures), storage.VrmExporterVersion, storage.VrmSpecVersion); + var meta = storage.CreateVrmMeta(model.Textures); + + var Vrm = new Vrm(meta, storage.VrmExporterVersion, storage.VrmSpecVersion); model.Vrm = Vrm; storage.LoadVrmHumanoid(model.Nodes); diff --git a/Assets/VRM10/vrmlib/Runtime/ModelDiffExtensions.cs b/Assets/VRM10/vrmlib/Runtime/ModelDiffExtensions.cs index d64dc2440d..dea18e4692 100644 --- a/Assets/VRM10/vrmlib/Runtime/ModelDiffExtensions.cs +++ b/Assets/VRM10/vrmlib/Runtime/ModelDiffExtensions.cs @@ -384,7 +384,6 @@ static void Vrm(ModelDiffContext context, Model lhs, Model rhs) VrmFirstPerson(context.Enter(nameof(lhs.Vrm.FirstPerson)), lhs.Vrm.FirstPerson, rhs.Vrm.FirstPerson); VrmLookAt(context.Enter(nameof(lhs.Vrm.LookAt)), lhs.Vrm.LookAt, rhs.Vrm.LookAt); ListDiff(context.Enter("SpringBone.Springs"), lhs.Vrm.SpringBone.Springs, rhs.Vrm.SpringBone.Springs, VrmSpringBoneEquals); - ListDiff(context.Enter("SpringBone.Colliders"), lhs.Vrm.SpringBone.Colliders, rhs.Vrm.SpringBone.Colliders, VrmSpringBoneColliderEquals); } static void VrmMeta(ModelDiffContext context, Meta lhs, Meta rhs) @@ -501,19 +500,26 @@ static void VrmLookAtRangeMap(ModelDiffContext context, LookAtRangeMap lhs, Look context.Enter("Curve").Push(lhs.Curve, rhs.Curve); } - static bool VrmSpringBoneEquals(ModelDiffContext context, SpringBone lhs, SpringBone rhs) + static bool VrmSpringBoneJointEquals(ModelDiffContext context, SpringJoint lhs, SpringJoint rhs) { var equals = true; - if (!context.Enter("Comment").Push(lhs.Comment, rhs.Comment)) equals = false; if (!context.Enter("DragForce").Push(lhs.DragForce, rhs.DragForce)) equals = false; if (!context.Enter("GravityDir").Push(lhs.GravityDir, rhs.GravityDir)) equals = false; if (!context.Enter("GravityPower").Push(lhs.GravityPower, rhs.GravityPower)) equals = false; if (!context.Enter("HitRadius").Push(lhs.HitRadius, rhs.HitRadius)) equals = false; - if (!context.Enter("Origin").Push(lhs.Origin, rhs.Origin)) equals = false; if (!context.Enter("Stiffness").Push(lhs.Stiffness, rhs.Stiffness)) equals = false; return equals; } + static bool VrmSpringBoneEquals(ModelDiffContext context, SpringBone lhs, SpringBone rhs) + { + var equals = true; + if (!context.Enter("Comment").Push(lhs.Comment, rhs.Comment)) equals = false; + if (!context.Enter("Origin").Push(lhs.Origin, rhs.Origin)) equals = false; + if (!ListDiff(context.Enter("Joint"), lhs.Joints, rhs.Joints, VrmSpringBoneJointEquals)) equals = false; + return equals; + } + static bool VrmSpringBoneColliderEquals(ModelDiffContext context, VrmSpringBoneCollider lhs, VrmSpringBoneCollider rhs) { var equals = true; diff --git a/Assets/VRM10/vrmlib/Runtime/ModelExtensionsForCoordinates.cs b/Assets/VRM10/vrmlib/Runtime/ModelExtensionsForCoordinates.cs index 45dae6d52c..d38a426754 100644 --- a/Assets/VRM10/vrmlib/Runtime/ModelExtensionsForCoordinates.cs +++ b/Assets/VRM10/vrmlib/Runtime/ModelExtensionsForCoordinates.cs @@ -167,7 +167,10 @@ static void ReverseZAndFlipTriangle(this Model model, bool ignoreVrm) } } - b.GravityDir = b.GravityDir.ReverseZ(); + foreach (var j in b.Joints) + { + j.GravityDir = j.GravityDir.ReverseZ(); + } } } } diff --git a/Assets/VRM10/vrmlib/Runtime/SpanLike.cs b/Assets/VRM10/vrmlib/Runtime/SpanLike.cs index 7ab5b24bc2..70f128331b 100644 --- a/Assets/VRM10/vrmlib/Runtime/SpanLike.cs +++ b/Assets/VRM10/vrmlib/Runtime/SpanLike.cs @@ -187,31 +187,6 @@ public static void Deconstruct(this KeyValuePair pair, out T key, ou } } - public static class ArraySegmentExtensions - { - public static ArraySegment Slice(this ArraySegment self, int start, int length) - { - if (start + length > self.Count) - { - throw new ArgumentOutOfRangeException(); - } - return new ArraySegment( - self.Array, - self.Offset + start, - length - ); - } - - public static ArraySegment Slice(this ArraySegment self, int start) - { - if (start > self.Count) - { - throw new ArgumentOutOfRangeException(); - } - return self.Slice(start, self.Count - start); - } - } - public struct SpanLike : IEquatable>, IEnumerable where T : struct { diff --git a/Assets/VRM10/vrmlib/Runtime/Vrm/ModelExtensionsForNode.cs b/Assets/VRM10/vrmlib/Runtime/Vrm/ModelExtensionsForNode.cs index 482f962fc1..df51eabb81 100644 --- a/Assets/VRM10/vrmlib/Runtime/Vrm/ModelExtensionsForNode.cs +++ b/Assets/VRM10/vrmlib/Runtime/Vrm/ModelExtensionsForNode.cs @@ -105,14 +105,14 @@ public static IEnumerable GetRemoveNodes(this Model model) { foreach (var x in spring.Springs) { - foreach (var y in x.Bones) + foreach (var y in x.Joints) { - nodeUsage[model.Nodes.IndexOf(y)].SpringUse = true; + nodeUsage[model.Nodes.IndexOf(y.Node)].SpringUse = true; + } + foreach (var y in x.Colliders) + { + nodeUsage[model.Nodes.IndexOf(y.Node)].SpringUse = true; } - } - foreach (var x in spring.Colliders) - { - nodeUsage[model.Nodes.IndexOf(x.Node)].SpringUse = true; } } diff --git a/Assets/VRM10/vrmlib/Runtime/Vrm/SpringBoneManager.cs b/Assets/VRM10/vrmlib/Runtime/Vrm/SpringBoneManager.cs index f0afb1e52c..9639a21fa2 100644 --- a/Assets/VRM10/vrmlib/Runtime/Vrm/SpringBoneManager.cs +++ b/Assets/VRM10/vrmlib/Runtime/Vrm/SpringBoneManager.cs @@ -49,15 +49,14 @@ public SpringBoneColliderGroup(Node node, IEnumerable col } } - public class SpringBone + public class SpringJoint { - public const string ExtensionName = "VRMC_springBone"; - public readonly List Bones = new List(); - public Node Origin; - - public readonly List Colliders = new List(); + public readonly Node Node; - public string Comment = ""; + public SpringJoint(Node node) + { + Node = node; + } public float DragForce; @@ -68,11 +67,23 @@ public class SpringBone public float HitRadius; public float Stiffness; + + public bool Exclude; + } + + public class SpringBone + { + public const string ExtensionName = "VRMC_springBone"; + public readonly List Joints = new List(); + public Node Origin; + + public readonly List Colliders = new List(); + + public string Comment = ""; } public class SpringBoneManager { public readonly List Springs = new List(); - public readonly List Colliders = new List(); } -} \ No newline at end of file +}