diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs index b896a6017..068492f5d 100644 --- a/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Humanoid.cs @@ -239,7 +239,7 @@ namespace UniHumanoid return null; } - IEnumerable<(Transform, HumanBodyBones)> BoneMap + public IEnumerable<(Transform, HumanBodyBones)> BoneMap { get { diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig.meta new file mode 100644 index 000000000..801f624a0 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f0f3a2bf6126d4a49b24a5fc2a7b6e64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotations/ControlRigGenerationOption.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/ControlRigGenerationOption.cs similarity index 100% rename from Assets/VRM10/Runtime/ControlRig/InitialRotations/ControlRigGenerationOption.cs rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/ControlRigGenerationOption.cs diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotations/ControlRigGenerationOption.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/ControlRigGenerationOption.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/ControlRig/InitialRotations/ControlRigGenerationOption.cs.meta rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/ControlRigGenerationOption.cs.meta diff --git a/Assets/VRM10/Runtime/ControlRig/Vrm10ControlBone.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs similarity index 98% rename from Assets/VRM10/Runtime/ControlRig/Vrm10ControlBone.cs rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs index a10fcaba1..eec7aa06a 100644 --- a/Assets/VRM10/Runtime/ControlRig/Vrm10ControlBone.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs @@ -34,6 +34,7 @@ namespace UniVRM10 private readonly Quaternion _initialTargetLocalRotation; private readonly Quaternion _initialTargetGlobalRotation; private readonly List _children = new List(); + public IReadOnlyList Children => _children; private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent) { diff --git a/Assets/VRM10/Runtime/ControlRig/Vrm10ControlBone.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/ControlRig/Vrm10ControlBone.cs.meta rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs.meta diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeControlRig.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs similarity index 61% rename from Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeControlRig.cs rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs index c6c13c6df..9abd96c5d 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeControlRig.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs @@ -13,7 +13,7 @@ namespace UniVRM10 /// Create a control rig for the VRM 1.0 model instance. /// This provides the normalized operation of bones, like VRM 0.x. /// - public sealed class Vrm10RuntimeControlRig : IDisposable + public sealed class Vrm10RuntimeControlRig : IDisposable, INormalizedPoseApplicable, ITPoseProvider { private readonly Transform _controlRigRoot; private readonly Vrm10ControlBone _hipBone; @@ -22,7 +22,8 @@ namespace UniVRM10 public IReadOnlyDictionary Bones => _bones; public Animator ControlRigAnimator { get; } - public float InitialHipsHeight { get; } + + public float InitialHipsHeight => HipTPoseWorldPosition.y; /// /// humanoid に対して ControlRig を生成します @@ -37,7 +38,7 @@ namespace UniVRM10 _hipBone = Vrm10ControlBone.Build(humanoid, out _bones); _hipBone.ControlBone.SetParent(_controlRigRoot); - InitialHipsHeight = _hipBone.ControlTarget.position.y; + HipTPoseWorldPosition = vrmRoot.worldToLocalMatrix.MultiplyPoint(_hipBone.ControlTarget.position); var transformBonePairs = _bones.Select(kv => (kv.Value.ControlBone, kv.Key)); _controlRigAvatar = HumanoidLoader.LoadHumanoidAvatar(vrmRoot, transformBonePairs); @@ -73,12 +74,53 @@ namespace UniVRM10 public void EnforceTPose() { - // TODO: restore hips position - + SetHipsPosition(_hipBone.ControlBone.position); foreach (var bone in _bones.Values) { bone.ControlBone.localRotation = Quaternion.identity; } } + + public void SetHipsPosition(Vector3 position) + { + // TODO: scale model local position + _hipBone.ControlTarget.position = position; + } + + public void SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation) + { + if (_bones.TryGetValue(bone, out var t)) + { + t.ControlBone.localRotation = normalizedLocalRotation; + } + } + + IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> Traverse(Vrm10ControlBone bone, Vrm10ControlBone parent = null) + { + yield return (bone.BoneType, parent != null ? parent.BoneType : HumanBodyBones.LastBone); + + foreach (var child in bone.Children) + { + foreach (var headParent in Traverse(child, bone)) + { + yield return headParent; + } + } + } + + public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones() + { + foreach (var headParent in Traverse(_hipBone)) + { + yield return headParent; + } + } + + public Vector3 HipTPoseWorldPosition { get; } + + public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone) + { + return Quaternion.identity; + } } } diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeControlRig.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10RuntimeControlRig.cs.meta rename to Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs.meta diff --git a/Assets/VRM10/Runtime/ControlRig/Vrm10BoneInitialRotation.cs b/Assets/VRM10/Runtime/ControlRig/BoneInitialRotation.cs similarity index 74% rename from Assets/VRM10/Runtime/ControlRig/Vrm10BoneInitialRotation.cs rename to Assets/VRM10/Runtime/ControlRig/BoneInitialRotation.cs index ffdb5b7a0..d926be18b 100644 --- a/Assets/VRM10/Runtime/ControlRig/Vrm10BoneInitialRotation.cs +++ b/Assets/VRM10/Runtime/ControlRig/BoneInitialRotation.cs @@ -5,7 +5,7 @@ namespace UniVRM10 /// /// Represents the rotation at the initial pose (TPose) /// - public readonly struct Vrm10BoneInitialRotation + public readonly struct BoneInitialRotation { public readonly Transform Transform; @@ -15,7 +15,7 @@ namespace UniVRM10 public readonly Quaternion InitialGlobalRotation; - public Vrm10BoneInitialRotation(Transform transform) + public BoneInitialRotation(Transform transform) { Transform = transform; InitialLocalPosition = transform.localPosition; @@ -24,11 +24,9 @@ namespace UniVRM10 } /// - /// 初期姿勢からの相対的な回転。 - /// - /// VRM-0.X 互換リグでは localRotation と同じ値を示す。 + /// Convert the local rotation, including the initial rotation, to a normalized local rotation /// - Quaternion NormalizedLocalRotation + public Quaternion NormalizedLocalRotation { get { diff --git a/Assets/VRM10/Runtime/ControlRig/Vrm10BoneInitialRotation.cs.meta b/Assets/VRM10/Runtime/ControlRig/BoneInitialRotation.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/ControlRig/Vrm10BoneInitialRotation.cs.meta rename to Assets/VRM10/Runtime/ControlRig/BoneInitialRotation.cs.meta diff --git a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs new file mode 100644 index 000000000..6d265a60f --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + public interface INormalizedPoseApplicable + { + void SetHipsPosition(Vector3 position); + + void SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation); + } +} diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotations/Vrm0XCompatibleRig.cs.meta b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs.meta similarity index 83% rename from Assets/VRM10/Runtime/ControlRig/InitialRotations/Vrm0XCompatibleRig.cs.meta rename to Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs.meta index 515a87b3b..8eee27ebc 100644 --- a/Assets/VRM10/Runtime/ControlRig/InitialRotations/Vrm0XCompatibleRig.cs.meta +++ b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d1e0f212571bf3f41809f19c2d8f3211 +guid: 9e200e64e4f18884d8a35bf9ce27cd4a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs new file mode 100644 index 000000000..798b3e610 --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs @@ -0,0 +1,17 @@ +using UnityEngine; + +namespace UniVRM10 +{ + public interface INormalizedPoseProvider + { + /// + /// Get hips position in model root space + /// + Vector3 GetHipsPosition(); + + /// + /// Get normalized local rotation + /// + Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone); + } +} diff --git a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs.meta b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs.meta new file mode 100644 index 000000000..7169bd3ee --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 624d9ba92e6c69b40abab8223e530d9e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs new file mode 100644 index 000000000..eb355237d --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + public interface ITPoseProvider + { + /// + /// * world は ModelRoot を意図していることに注意 + /// + Vector3 HipTPoseWorldPosition { get; } + + /// + /// * world は ModelRoot を意図していることに注意 + /// * bone 無いときはどう振る舞うべきか + /// + /// + /// + Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone); + + /// + /// ボーンを親ボーンとセットで列挙する + /// + /// hips の場合は parent は HumanBodyBones.LastBone で null を表す + IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones(); + } +} diff --git a/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs.meta b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs.meta new file mode 100644 index 000000000..16c14f4a1 --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc1da1cfaa59c824295059dc6b1f22c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs new file mode 100644 index 000000000..6b4c0b325 --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + /// + /// VRM0TPose と異なる初期回転を持ったヒエラルキー + /// + /// - XR_EXT_hand_tracking + /// - XR_FB_body_tracking + /// - vrm-animation + /// + /// から VRM0TPose 互換の NormalizedLocalRotation(VRM0互換)を取り出す。 + /// BoneInitialRotation が値を計算できる。 + /// + public sealed class InitRotationPoseProvider : INormalizedPoseProvider, ITPoseProvider + { + Transform m_root; + Transform m_hips; + private readonly Dictionary m_bones = new Dictionary(); + + public Vector3 HipTPoseWorldPosition => throw new System.NotImplementedException(); + + public InitRotationPoseProvider(Transform root, UniHumanoid.Humanoid humanoid) + { + m_root = root; + m_hips = m_bones[HumanBodyBones.Hips].Transform; + foreach (var (t, bone) in humanoid.BoneMap) + { + m_bones.Add(bone, new BoneInitialRotation(t)); + } + } + + public Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) + { + if (m_bones.TryGetValue(bone, out var c)) + { + return c.NormalizedLocalRotation; + } + else + { + // Debug.LogWarning($"${bone} not found"); + return Quaternion.identity; + } + } + + public Vector3 GetHipsPosition() + { + if (m_hips.parent == m_root) + { + return m_hips.localPosition; + } + else + { + throw new System.NotImplementedException(); + } + } + + public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone) + { + throw new System.NotImplementedException(); + } + + public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs.meta b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs.meta new file mode 100644 index 000000000..c3b1ac27e --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b21b9221f1a87564ea6dc5cf6037195a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotations/Vrm0XCompatibleRig.cs b/Assets/VRM10/Runtime/ControlRig/InitialRotations/Vrm0XCompatibleRig.cs deleted file mode 100644 index ddc033dc5..000000000 --- a/Assets/VRM10/Runtime/ControlRig/InitialRotations/Vrm0XCompatibleRig.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace UniVRM10 -{ - public static class Vrm0XCompatibleRig - { - /// - /// 空の Dictionary を返します。 - /// HumanBodyBones に対応する Quaternion が無い場合は Quaternion.Identity を採用する仕様です。 - /// VRM-0.X では T-Pose のときにすべてのボーンが Quaternion.Identity です。 - /// - public static IReadOnlyDictionary InitialRotations => new Dictionary(); - } -} diff --git a/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs new file mode 100644 index 000000000..7d2ba7874 --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + /// + /// 正規化済みのヒエラルキーの getter。 + /// localRotation をコピーするだけなのだが、 + /// GetNormalizedLocalRotation にて、ボーンの有無(upperChestなど)の違いの対応をする予定(未実装)。 + /// + public sealed class NormalizedPoseProvider : INormalizedPoseProvider, ITPoseProvider + { + Transform m_root; + Animator m_animator; + + public Vector3 HipTPoseWorldPosition => throw new System.NotImplementedException(); + + public NormalizedPoseProvider(Transform root, Animator animator) + { + m_root = root; + m_animator = animator; + } + + public Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) + { + if (m_animator.GetBoneTransform(bone) is Transform t) + { + // TODO: parentBone relative + return t.localRotation; + } + else + { + // Debug.LogWarning($"{bone} not found"); + return Quaternion.identity; + } + } + + public Vector3 GetHipsPosition() + { + // TODO: from model root ? + return m_animator.GetBoneTransform(HumanBodyBones.Hips).localPosition; + } + + public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone) + { + throw new System.NotImplementedException(); + } + + public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs.meta b/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs.meta new file mode 100644 index 000000000..ae53d6441 --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 77dc323422a0e59449bd822dac490495 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs new file mode 100644 index 000000000..3af5fc621 --- /dev/null +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs @@ -0,0 +1,15 @@ +namespace UniVRM10 +{ + public static class VRM10Retarget + { + public static void Retarget((INormalizedPoseProvider Pose, ITPoseProvider TPose) source, (INormalizedPoseApplicable Pose, ITPoseProvider TPose) sink) + { + foreach (var (head, parent) in sink.TPose.EnumerateBones()) + { + var q = source.Pose.GetNormalizedLocalRotation(head, parent); + sink.Pose.SetNormalizedLocalRotation(head, q); + } + sink.Pose.SetHipsPosition(source.Pose.GetHipsPosition()); + } + } +} diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs.meta b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs.meta new file mode 100644 index 000000000..51e773495 --- /dev/null +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49423dc9e9a224545a90221f41d975c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: