rename NormalizedPoseProvider to AnimatorPoseProvider. fix Bvh height

This commit is contained in:
ousttrue
2023-02-21 17:21:50 +09:00
parent 0502f30588
commit a310e2dcf5
13 changed files with 157 additions and 91 deletions

View File

@@ -99,6 +99,18 @@ namespace UniHumanoid
Children = new List<BvhNode>();
}
public int GetChannelIndex(Channel channel)
{
for (int i = 0; i < Channels.Length; ++i)
{
if (channel == Channels[i])
{
return i;
}
}
throw new KeyNotFoundException();
}
public virtual void Parse(StringReader r)
{
Offset = ParseOffset(r.ReadLine());

View File

@@ -75,11 +75,15 @@ namespace UniHumanoid
//
// scaling. reposition
//
var yCh = Bvh.Root.GetChannelIndex(Channel.Yposition);
var curve = Bvh.Channels[yCh];
var hipHeight = curve.Keys[0];
float scaling = 1.0f;
{
//var foot = animator.GetBoneTransform(HumanBodyBones.LeftFoot);
var foot = hips.Traverse().Skip(skeleton.GetBoneIndex(HumanBodyBones.LeftFoot)).First();
var hipHeight = hips.position.y - foot.position.y;
// var foot = hips.Traverse().Skip(skeleton.GetBoneIndex(HumanBodyBones.LeftFoot)).First();
// var hipHeight = hips.position.y - foot.position.y;
// hips height to a meter
scaling = 1.0f / hipHeight;
foreach (var x in Root.transform.Traverse())

View File

@@ -33,10 +33,11 @@ namespace UniVRM10
private readonly Quaternion _initialTargetLocalRotation;
private readonly Quaternion _initialTargetGlobalRotation;
public Vector3 InitialTargetGlobalPosition { get; }
private readonly List<Vrm10ControlBone> _children = new List<Vrm10ControlBone>();
public IReadOnlyList<Vrm10ControlBone> Children => _children;
private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent)
private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent, Transform root)
{
if (boneType == HumanBodyBones.LastBone)
{
@@ -62,6 +63,7 @@ namespace UniVRM10
_initialTargetLocalRotation = controlTarget.localRotation;
_initialTargetGlobalRotation = controlTarget.rotation;
InitialTargetGlobalPosition = root.worldToLocalMatrix.MultiplyPoint(controlTarget.position);
}
/// <summary>
@@ -76,32 +78,32 @@ namespace UniVRM10
}
}
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, out Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, out Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap, Transform root)
{
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null);
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null, root);
boneMap = new Dictionary<HumanBodyBones, Vrm10ControlBone>();
boneMap.Add(HumanBodyBones.Hips, hips);
foreach (Transform child in humanoid.Hips)
{
BuildRecursively(humanoid, child, hips, boneMap);
BuildRecursively(humanoid, child, hips, boneMap, root);
}
return hips;
}
private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap, Transform root)
{
if (humanoid.TryGetBoneForTransform(current, out var bone))
{
var newBone = new Vrm10ControlBone(current, bone, parent);
var newBone = new Vrm10ControlBone(current, bone, parent, root);
parent = newBone;
boneMap.Add(bone, newBone);
}
foreach (Transform child in current)
{
BuildRecursively(humanoid, child, parent, boneMap);
BuildRecursively(humanoid, child, parent, boneMap, root);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace UniVRM10
_controlRigRoot = new GameObject("Runtime Control Rig").transform;
_controlRigRoot.SetParent(vrmRoot);
_hipBone = Vrm10ControlBone.Build(humanoid, out _bones);
_hipBone = Vrm10ControlBone.Build(humanoid, out _bones, _controlRigRoot);
_hipBone.ControlBone.SetParent(_controlRigRoot);
HipTPoseWorldPosition = vrmRoot.worldToLocalMatrix.MultiplyPoint(_hipBone.ControlTarget.position);
@@ -72,19 +72,11 @@ namespace UniVRM10
}
}
public void EnforceTPose()
{
SetHipsPosition(_hipBone.ControlBone.position);
foreach (var bone in _bones.Values)
{
bone.ControlBone.localRotation = Quaternion.identity;
}
}
#region INormalizedPoseApplicable
public void SetHipsPosition(Vector3 position)
{
// TODO: scale model local position
_hipBone.ControlTarget.position = position;
var world = _controlRigRoot.TransformPoint(position);
_hipBone.ControlBone.position = world;
}
public void SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation)
@@ -94,6 +86,7 @@ namespace UniVRM10
t.ControlBone.localRotation = normalizedLocalRotation;
}
}
#endregion
IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> Traverse(Vrm10ControlBone bone, Vrm10ControlBone parent = null)
{
@@ -108,6 +101,7 @@ namespace UniVRM10
}
}
#region ITPoseProvider
public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones()
{
foreach (var headParent in Traverse(_hipBone))
@@ -122,5 +116,11 @@ namespace UniVRM10
{
return Quaternion.identity;
}
public Vector3 GetBoneTPoseWorldPosition(HumanBodyBones bone)
{
return _bones[bone].InitialTargetGlobalPosition;
}
#endregion
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// Animator から Pose 供給する。
/// この Animator は以下の条件を満たすこと。
///
/// * TPoseであること
/// * 正規化済みであること
///
/// </summary>
public sealed class AnimatorPoseProvider : INormalizedPoseProvider, ITPoseProvider
{
Transform m_root;
Animator m_animator;
Dictionary<HumanBodyBones, Vector3> m_posMap = new Dictionary<HumanBodyBones, Vector3>();
public AnimatorPoseProvider(Transform root, Animator animator)
{
m_root = root;
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)
{
continue;
}
var t = animator.GetBoneTransform(bone);
if (t != null)
{
m_posMap[bone] = root.worldToLocalMatrix.MultiplyPoint(t.position);
}
}
}
#region INormalizedPoseProvider
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;
}
#endregion
#region ITPoseProvider
public Vector3 HipTPoseWorldPosition { get; }
public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone)
{
throw new System.NotImplementedException();
}
public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones()
{
throw new System.NotImplementedException();
}
public Vector3 GetBoneTPoseWorldPosition(HumanBodyBones bone)
{
return m_posMap[bone];
}
#endregion
}
}

View File

@@ -12,12 +12,20 @@ namespace UniVRM10
/// <summary>
/// * world は ModelRoot を意図していることに注意
/// * bone 無いときはどう振る舞うべきか
/// * bone 無いときは Quaternion.Identity ?
/// </summary>
/// <param name="bone"></param>
/// <returns></returns>
Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone);
/// <summary>
/// * world は ModelRoot を意図していることに注意
/// * bone 無いときは Vector3.zero ?
/// </summary>
/// <param name="bone"></param>
/// <returns></returns>
Vector3 GetBoneTPoseWorldPosition(HumanBodyBones bone);
/// <summary>
/// ボーンを親ボーンとセットで列挙する</returns>
/// </summary>

View File

@@ -65,5 +65,10 @@ namespace UniVRM10
{
throw new System.NotImplementedException();
}
public Vector3 GetBoneTPoseWorldPosition(HumanBodyBones bone)
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -1,54 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// 正規化済みのヒエラルキーの getter。
/// localRotation をコピーするだけなのだが、
/// GetNormalizedLocalRotation にて、ボーンの有無(upperChestなど)の違いの対応をする予定(未実装)。
/// </summary>
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();
}
}
}

View File

@@ -88,11 +88,5 @@ namespace UniVRM10.VRM10Viewer
// destroy GameObject
GameObject.Destroy(m_instance.gameObject);
}
public void TPoseControlRig()
{
var controlRig = m_controller.Runtime.ControlRig;
controlRig.EnforceTPose();
}
}
}

View File

@@ -23,7 +23,7 @@ namespace UniVRM10.VRM10Viewer
public VRM10Motion(UniHumanoid.BvhImporterContext context)
{
m_context = context;
var provider = new NormalizedPoseProvider(m_context.Root.transform, m_context.Root.GetComponent<Animator>());
var provider = new AnimatorPoseProvider(m_context.Root.transform, m_context.Root.GetComponent<Animator>());
ControlRig = (provider, provider);
}
@@ -199,10 +199,5 @@ namespace UniVRM10.VRM10Viewer
return motion;
}
}
public void Retarget(Vrm10RuntimeControlRig dst)
{
VRM10Retarget.Retarget(ControlRig, (dst, dst));
}
}
}

View File

@@ -1,3 +1,5 @@
using UnityEngine;
namespace UniVRM10
{
public static class VRM10Retarget
@@ -9,7 +11,21 @@ namespace UniVRM10
var q = source.Pose.GetNormalizedLocalRotation(head, parent);
sink.Pose.SetNormalizedLocalRotation(head, q);
}
sink.Pose.SetHipsPosition(source.Pose.GetHipsPosition());
// scaling hips position
var scaling = sink.TPose.GetBoneTPoseWorldPosition(HumanBodyBones.LeftUpperLeg).y / source.TPose.GetBoneTPoseWorldPosition(HumanBodyBones.LeftUpperLeg).y;
var delta = source.Pose.GetHipsPosition() - source.TPose.HipTPoseWorldPosition;
sink.Pose.SetHipsPosition(sink.TPose.HipTPoseWorldPosition + delta * scaling);
}
public static void EnforceTPose((INormalizedPoseApplicable Pose, ITPoseProvider TPose) sink)
{
foreach (var (head, parent) in sink.TPose.EnumerateBones())
{
sink.Pose.SetNormalizedLocalRotation(head, Quaternion.identity);
}
sink.Pose.SetHipsPosition(sink.TPose.HipTPoseWorldPosition);
}
}
}

View File

@@ -237,11 +237,11 @@ namespace UniVRM10.VRM10Viewer
{
if (m_ui.IsBvhEnabled && m_src != null)
{
m_src.Retarget(m_loaded.ControlRig);
VRM10Retarget.Retarget(m_src.ControlRig, (m_loaded.ControlRig, m_loaded.ControlRig));
}
else
{
m_loaded.TPoseControlRig();
VRM10Retarget.EnforceTPose((m_loaded.ControlRig, m_loaded.ControlRig));
}
}
}