diff --git a/Assets/UniGLTF/Runtime/Utils/EuclideanTransform.cs b/Assets/UniGLTF/Runtime/Utils/EuclideanTransform.cs new file mode 100644 index 000000000..5c6992f1b --- /dev/null +++ b/Assets/UniGLTF/Runtime/Utils/EuclideanTransform.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace UniGLTF.Utils +{ + public readonly struct EuclideanTransform + { + public readonly Quaternion Rotation; + public readonly Vector3 Translation; + + public EuclideanTransform(Quaternion rotation, Vector3 translation) + { + Rotation = rotation; + Translation = translation; + } + public EuclideanTransform(Quaternion rotation) + { + Rotation = rotation; + Translation = Vector3.zero; + } + public EuclideanTransform(Vector3 translation) + { + Rotation = Quaternion.identity; + Translation = translation; + } + public EuclideanTransform(Matrix4x4 matrix) + { + Rotation = matrix.rotation; + Translation = matrix.GetColumn(3); + } + } +} diff --git a/Assets/UniGLTF/Runtime/Utils/EuclideanTransform.cs.meta b/Assets/UniGLTF/Runtime/Utils/EuclideanTransform.cs.meta new file mode 100644 index 000000000..363d1575a --- /dev/null +++ b/Assets/UniGLTF/Runtime/Utils/EuclideanTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af1b96890351fa24e9a49e7c4a6c2185 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs index 5af0d7732..d054033e8 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using UniHumanoid; using UnityEngine; +using UniGLTF.Utils; namespace UniVRM10 { @@ -68,53 +69,30 @@ namespace UniVRM10 } } - #region INormalizedPoseApplicable - public void SetRawHipsPosition(Vector3 position) + void INormalizedPoseApplicable.SetRawHipsPosition(Vector3 position) { var world = _controlRigRoot.TransformPoint(position); _hipBone.ControlBone.position = world; } - public void SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation) + void INormalizedPoseApplicable.SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation) { if (_bones.TryGetValue(bone, out var t)) { t.ControlBone.localRotation = normalizedLocalRotation; } } - #endregion - IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> Traverse(Vrm10ControlBone bone, Vrm10ControlBone parent = null) + EuclideanTransform? ITPoseProvider.GetWorldTransform(HumanBodyBones bone) { - yield return (bone.BoneType, parent != null ? parent.BoneType : HumanBodyBones.LastBone); - - foreach (var child in bone.Children) + if (_bones.TryGetValue(bone, out var t)) { - foreach (var headParent in Traverse(child, bone)) - { - yield return headParent; - } + return new EuclideanTransform(t.InitialTargetGlobalPosition); + } + else + { + return default; } } - - #region ITPoseProvider - public IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs() - { - foreach (var headParent in Traverse(_hipBone)) - { - yield return headParent; - } - } - - public Quaternion? GetBoneWorldRotation(HumanBodyBones bone) - { - return Quaternion.identity; - } - - public Vector3? GetBoneWorldPosition(HumanBodyBones bone) - { - return _bones[bone].InitialTargetGlobalPosition; - } - #endregion } } diff --git a/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs index 90742e54a..5d570c36b 100644 --- a/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs +++ b/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UniGLTF.Utils; using UnityEngine; namespace UniVRM10 @@ -16,7 +17,7 @@ namespace UniVRM10 { Transform m_root; Animator m_animator; - Dictionary m_posMap = new Dictionary(); + Dictionary m_posMap = new Dictionary(); public AnimatorPoseProvider(Transform root, Animator animator) { @@ -24,24 +25,22 @@ namespace UniVRM10 m_animator = animator; var hips = animator.GetBoneTransform(HumanBodyBones.Hips); - HipTPoseWorldPosition = root.worldToLocalMatrix.MultiplyPoint(hips.position); foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones))) { - if(bone==HumanBodyBones.LastBone) + if (bone == HumanBodyBones.LastBone) { continue; } var t = animator.GetBoneTransform(bone); if (t != null) { - m_posMap[bone] = root.worldToLocalMatrix.MultiplyPoint(t.position); + m_posMap[bone] = new EuclideanTransform(root.worldToLocalMatrix * t.localToWorldMatrix); } } } - #region INormalizedPoseProvider - public Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) + Quaternion INormalizedPoseProvider.GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) { if (m_animator.GetBoneTransform(bone) is Transform t) { @@ -55,30 +54,22 @@ namespace UniVRM10 } } - public Vector3 GetRawHipsPosition() + Vector3 INormalizedPoseProvider.GetRawHipsPosition() { // TODO: from model root ? return m_animator.GetBoneTransform(HumanBodyBones.Hips).localPosition; } - #endregion - #region ITPoseProvider - public Vector3 HipTPoseWorldPosition { get; } - - public IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs() + EuclideanTransform? ITPoseProvider.GetWorldTransform(HumanBodyBones bone) { - throw new System.NotImplementedException(); + if (m_posMap.TryGetValue(bone, out var t)) + { + return t; + } + else + { + return default; + } } - - public Quaternion? GetBoneWorldRotation(HumanBodyBones bone) - { - throw new System.NotImplementedException(); - } - - public Vector3? GetBoneWorldPosition(HumanBodyBones bone) - { - return m_posMap[bone]; - } - #endregion } } diff --git a/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs index 1ffe5f60a..3f996bcc5 100644 --- a/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs +++ b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using UniGLTF.Utils; using UnityEngine; namespace UniVRM10 @@ -6,15 +8,56 @@ namespace UniVRM10 public interface ITPoseProvider { /// - /// このTPoseに含まれるボーンと親ボーンの組み合わせを列挙する。 - /// - /// * Humanoidの木構造を深さ優先の順番で列挙する - /// + /// TPose 時のモデルルートを基準とした world 回転と位置を返す。 + /// 該当する bone が無いときは null。 /// - /// hips の場合は parent は HumanBodyBones.LastBone で null を表す - IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs(); + EuclideanTransform? GetWorldTransform(HumanBodyBones bone); + } - Quaternion? GetBoneWorldRotation(HumanBodyBones bone); - Vector3? GetBoneWorldPosition(HumanBodyBones bone); + public static class ITPoseProviderExtensions + { + static HumanBodyBones GetParent(ITPoseProvider self, HumanBodyBones bone) + { + var current = bone; + // 無限ループ防止のため念のためカウンター付き + for (int i = 0; i < 100; ++i) + { + var vrmBone = Vrm10HumanoidBoneSpecification.ConvertFromUnityBone(current); + var def = Vrm10HumanoidBoneSpecification.GetDefine(vrmBone); + var parentBone = Vrm10HumanoidBoneSpecification.ConvertToUnityBone(def.ParentBone.Value); + if (self.GetWorldTransform(parentBone).HasValue) + { + return parentBone; + } + current = parentBone; + } + + // Hips 以外は必ず親ボーンが有るはずなのでここには来ない + throw new Exception("parent not found"); + } + + public static IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs(this ITPoseProvider self) + { + foreach (var bone in CachedEnum.GetValues()) + { + if (bone == HumanBodyBones.LastBone) + { + continue; + } + if (!self.GetWorldTransform(bone).HasValue) + { + continue; + } + + if (bone == HumanBodyBones.Hips) + { + yield return (bone, HumanBodyBones.LastBone); + } + else + { + yield return (bone, GetParent(self, bone)); + } + } + } } } diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs index a2d01d06a..e87769866 100644 --- a/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs +++ b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using UniGLTF.Utils; using UnityEngine; namespace UniVRM10 @@ -31,7 +32,7 @@ namespace UniVRM10 } } - public Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) + Quaternion INormalizedPoseProvider.GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) { if (m_bones.TryGetValue(bone, out var c)) { @@ -44,7 +45,7 @@ namespace UniVRM10 } } - public Vector3 GetRawHipsPosition() + Vector3 INormalizedPoseProvider.GetRawHipsPosition() { if (m_hips.parent == m_root) { @@ -56,17 +57,7 @@ namespace UniVRM10 } } - public IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs() - { - throw new System.NotImplementedException(); - } - - public Quaternion? GetBoneWorldRotation(HumanBodyBones bone) - { - throw new System.NotImplementedException(); - } - - public Vector3? GetBoneWorldPosition(HumanBodyBones bone) + EuclideanTransform? ITPoseProvider.GetWorldTransform(HumanBodyBones bone) { throw new System.NotImplementedException(); } diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs index 7825fd36e..2d91fb3e6 100644 --- a/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs @@ -13,9 +13,9 @@ namespace UniVRM10.VRM10Viewer } // scaling hips position - var scaling = sink.TPose.GetBoneWorldPosition(HumanBodyBones.LeftUpperLeg).Value.y / source.TPose.GetBoneWorldPosition(HumanBodyBones.LeftUpperLeg).Value.y; - var delta = source.Pose.GetRawHipsPosition() - source.TPose.GetBoneWorldPosition(HumanBodyBones.Hips).Value; - sink.Pose.SetRawHipsPosition(sink.TPose.GetBoneWorldPosition(HumanBodyBones.Hips).Value + delta * scaling); + var scaling = sink.TPose.GetWorldTransform(HumanBodyBones.LeftUpperLeg).Value.Translation.y / source.TPose.GetWorldTransform(HumanBodyBones.LeftUpperLeg).Value.Translation.y; + var delta = source.Pose.GetRawHipsPosition() - source.TPose.GetWorldTransform(HumanBodyBones.Hips).Value.Translation; + sink.Pose.SetRawHipsPosition(sink.TPose.GetWorldTransform(HumanBodyBones.Hips).Value.Translation + delta * scaling); } public static void EnforceTPose((INormalizedPoseApplicable Pose, ITPoseProvider TPose) sink) @@ -25,7 +25,7 @@ namespace UniVRM10.VRM10Viewer sink.Pose.SetNormalizedLocalRotation(bone, Quaternion.identity); } - sink.Pose.SetRawHipsPosition(sink.TPose.GetBoneWorldPosition(HumanBodyBones.Hips).Value); + sink.Pose.SetRawHipsPosition(sink.TPose.GetWorldTransform(HumanBodyBones.Hips).Value.Translation); } } }