mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-19 01:07:32 -05:00
Merge pull request #1768 from ousttrue/feature10/fk_retarget
Feature10/fk retarget
This commit is contained in:
commit
e73840aec1
|
|
@ -437,5 +437,19 @@ namespace UniHumanoid
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetBoneForTransform(Transform t, out HumanBodyBones bone)
|
||||
{
|
||||
foreach (var (v, k) in BoneMap)
|
||||
{
|
||||
if (v == t)
|
||||
{
|
||||
bone = k;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bone = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
/// <summary>
|
||||
/// このクラスのヒエラルキーが 正規化された TPose を表している。
|
||||
/// 同時に、元のヒエラルキーの初期回転を保持する。
|
||||
/// Apply 関数で、再帰的に正規化済みのローカル回転から初期回転を加味したローカル回転を作って適用する。
|
||||
/// </summary>
|
||||
public class Vrm10BoneWithAxis
|
||||
{
|
||||
public readonly HumanBodyBones Bone;
|
||||
|
||||
/// <summary>
|
||||
/// 元のヒエラルキーの対応ボーン
|
||||
/// </summary>
|
||||
public readonly Transform Target;
|
||||
|
||||
/// <summary>
|
||||
/// 回転と拡大縮小を除去した(正規化された)ボーン。
|
||||
/// このボーンに対して localRotation を代入する。
|
||||
/// </summary>
|
||||
public readonly Transform Normalized;
|
||||
|
||||
/// <summary>
|
||||
/// 元のボーンの初期回転。
|
||||
/// </summary>
|
||||
public readonly Quaternion InitialLocalRotation;
|
||||
|
||||
public readonly Quaternion ToLocal;
|
||||
|
||||
public List<Vrm10BoneWithAxis> Children = new List<Vrm10BoneWithAxis>();
|
||||
|
||||
public Vrm10BoneWithAxis(Transform current, Quaternion parentInverse, HumanBodyBones bone)
|
||||
{
|
||||
if (bone == HumanBodyBones.LastBone)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
if (current == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
Bone = bone;
|
||||
Target = current;
|
||||
Normalized = new GameObject(bone.ToString()).transform;
|
||||
Normalized.position = current.position;
|
||||
// InitialLocalRotation = parentInverse * current.rotation;
|
||||
InitialLocalRotation = current.localRotation;
|
||||
// InitialLocalRotation = current.rotation;
|
||||
ToLocal = current.rotation;
|
||||
}
|
||||
|
||||
public static Vrm10BoneWithAxis Build(UniHumanoid.Humanoid humanoid, Dictionary<HumanBodyBones, Vrm10BoneWithAxis> boneMap)
|
||||
{
|
||||
var hips = new Vrm10BoneWithAxis(humanoid.Hips, Quaternion.identity, HumanBodyBones.Hips);
|
||||
boneMap.Add(HumanBodyBones.Hips, hips);
|
||||
|
||||
foreach (Transform child in humanoid.Hips)
|
||||
{
|
||||
Traverse(humanoid, child, hips, boneMap);
|
||||
}
|
||||
|
||||
return hips;
|
||||
}
|
||||
|
||||
private static void Traverse(UniHumanoid.Humanoid humanoid, Transform current, Vrm10BoneWithAxis parent, Dictionary<HumanBodyBones, Vrm10BoneWithAxis> boneMap)
|
||||
{
|
||||
if (humanoid.TryGetBoneForTransform(current, out var bone))
|
||||
{
|
||||
|
||||
// ヒューマンボーンだけを対象にするので、
|
||||
// parent が current の直接の親でない場合がある。
|
||||
// ワールド回転 parent^-1 * current からローカル回転を算出する。
|
||||
var parentInverse = Quaternion.Inverse(parent.Target.rotation);
|
||||
|
||||
var newBone = new Vrm10BoneWithAxis(current, parentInverse, bone);
|
||||
newBone.Normalized.SetParent(parent.Normalized, true);
|
||||
parent.Children.Add(newBone);
|
||||
parent = newBone;
|
||||
boneMap.Add(bone, newBone);
|
||||
}
|
||||
|
||||
foreach (Transform child in current)
|
||||
{
|
||||
Traverse(humanoid, child, parent, boneMap);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 親から再帰的にNormalized の ローカル回転を初期回転を加味して Target に適用する。
|
||||
/// </summary>
|
||||
public void ApplyRecursive(Quaternion worldParentRotation)
|
||||
{
|
||||
Target.localRotation = InitialLocalRotation * Quaternion.Inverse(ToLocal) * Normalized.localRotation * ToLocal;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.ApplyRecursive(Normalized.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eac40a89a1de8bc4a8d97b3cc2aea5fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public class Vrm10FkRetarget
|
||||
{
|
||||
Vrm10BoneWithAxis m_root;
|
||||
|
||||
Dictionary<HumanBodyBones, Vrm10BoneWithAxis> m_boneMap = new Dictionary<HumanBodyBones, Vrm10BoneWithAxis>();
|
||||
|
||||
public readonly float InitialHipsHeight;
|
||||
|
||||
public Vrm10FkRetarget(UniHumanoid.Humanoid humanoid)
|
||||
{
|
||||
m_root = Vrm10BoneWithAxis.Build(humanoid, m_boneMap);
|
||||
InitialHipsHeight = m_root.Target.position.y;
|
||||
Debug.Log($"InitialHipsHeight: {InitialHipsHeight}");
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
m_root.Target.position = m_root.Normalized.position;
|
||||
m_root.ApplyRecursive(Quaternion.identity);
|
||||
}
|
||||
|
||||
public Transform GetBoneTransform(HumanBodyBones bone)
|
||||
{
|
||||
if (m_boneMap.TryGetValue(bone, out var value))
|
||||
{
|
||||
return value.Normalized;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3c0af836ff386dd469cfedbd238ab2a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -14,6 +14,17 @@ namespace UniVRM10
|
|||
public class Vrm10Runtime : IDisposable
|
||||
{
|
||||
private readonly Vrm10Instance m_target;
|
||||
|
||||
Vrm10FkRetarget m_fkRetarget;
|
||||
public Vrm10FkRetarget GetOrCreateFkRetarget()
|
||||
{
|
||||
if (m_fkRetarget == null)
|
||||
{
|
||||
m_fkRetarget = new Vrm10FkRetarget(m_target.Humanoid);
|
||||
}
|
||||
return m_fkRetarget;
|
||||
}
|
||||
|
||||
private readonly IVrm10Constraint[] m_constraints;
|
||||
private readonly Transform m_head;
|
||||
private readonly FastSpringBoneService m_fastSpringBoneService;
|
||||
|
|
@ -152,6 +163,11 @@ namespace UniVRM10
|
|||
/// </summary>
|
||||
public void Process()
|
||||
{
|
||||
if (m_fkRetarget != null)
|
||||
{
|
||||
m_fkRetarget.Apply();
|
||||
}
|
||||
|
||||
//
|
||||
// constraint
|
||||
//
|
||||
|
|
|
|||
|
|
@ -107,16 +107,15 @@ namespace UniVRM10
|
|||
gaze.SetParent(m_head);
|
||||
gaze.localPosition = Vector3.forward;
|
||||
}
|
||||
switch (m_lookat.LookAtType)
|
||||
|
||||
// bone が無いときのエラー防止。マイグレーション失敗?
|
||||
if (m_lookat.LookAtType == LookAtType.bone && m_leftEye != null && m_rightEye != null)
|
||||
{
|
||||
case LookAtType.bone:
|
||||
_eyeDirectionApplicable = new LookAtEyeDirectionApplicableToBone(m_leftEye, m_rightEye, m_lookat.HorizontalOuter, m_lookat.HorizontalInner, m_lookat.VerticalDown, m_lookat.VerticalUp);
|
||||
break;
|
||||
case LookAtType.expression:
|
||||
_eyeDirectionApplicable = new LookAtEyeDirectionApplicableToExpression(m_lookat.HorizontalOuter, m_lookat.HorizontalInner, m_lookat.VerticalDown, m_lookat.VerticalUp);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
_eyeDirectionApplicable = new LookAtEyeDirectionApplicableToBone(m_leftEye, m_rightEye, m_lookat.HorizontalOuter, m_lookat.HorizontalInner, m_lookat.VerticalDown, m_lookat.VerticalUp);
|
||||
}
|
||||
else
|
||||
{
|
||||
_eyeDirectionApplicable = new LookAtEyeDirectionApplicableToExpression(m_lookat.HorizontalOuter, m_lookat.HorizontalInner, m_lookat.VerticalDown, m_lookat.VerticalUp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
|
||||
[Header("Runtime")]
|
||||
[SerializeField]
|
||||
HumanPoseTransfer m_src = default;
|
||||
Animator m_src = default;
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_target = default;
|
||||
|
|
@ -200,7 +200,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
var texts = GameObject.FindObjectsOfType<Text>();
|
||||
m_version = texts.First(x => x.name == "Version");
|
||||
|
||||
m_src = GameObject.FindObjectOfType<HumanPoseTransfer>();
|
||||
m_src = GameObject.FindObjectOfType<Animator>();
|
||||
|
||||
m_target = GameObject.FindObjectOfType<VRM10TargetMover>().gameObject;
|
||||
}
|
||||
|
|
@ -208,7 +208,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
class Loaded : IDisposable
|
||||
{
|
||||
RuntimeGltfInstance m_instance;
|
||||
HumanPoseTransfer m_pose;
|
||||
bool m_useBvh;
|
||||
Vrm10Instance m_controller;
|
||||
|
||||
VRM10AIUEO m_lipSync;
|
||||
|
|
@ -256,7 +256,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
}
|
||||
}
|
||||
|
||||
public Loaded(RuntimeGltfInstance instance, HumanPoseTransfer src, Transform lookAtTarget)
|
||||
public Loaded(RuntimeGltfInstance instance, Transform lookAtTarget)
|
||||
{
|
||||
m_instance = instance;
|
||||
|
||||
|
|
@ -266,10 +266,6 @@ namespace UniVRM10.VRM10Viewer
|
|||
// VRM
|
||||
m_controller.UpdateType = Vrm10Instance.UpdateTypes.LateUpdate; // after HumanPoseTransfer's setPose
|
||||
{
|
||||
m_pose = instance.gameObject.AddComponent<HumanPoseTransfer>();
|
||||
m_pose.Source = src;
|
||||
m_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
|
||||
|
||||
m_lipSync = instance.gameObject.AddComponent<VRM10AIUEO>();
|
||||
m_blink = instance.gameObject.AddComponent<VRM10Blinker>();
|
||||
m_autoExpression = instance.gameObject.AddComponent<VRM10AutoExpression>();
|
||||
|
|
@ -293,22 +289,57 @@ namespace UniVRM10.VRM10Viewer
|
|||
GameObject.Destroy(m_instance.gameObject);
|
||||
}
|
||||
|
||||
public void EnableBvh(HumanPoseTransfer src)
|
||||
public void EnableBvh()
|
||||
{
|
||||
if (m_pose != null)
|
||||
{
|
||||
m_pose.Source = src;
|
||||
m_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer;
|
||||
}
|
||||
m_useBvh = true;
|
||||
}
|
||||
|
||||
public void EnableTPose(HumanPoseClip pose)
|
||||
public void EnableTPose()
|
||||
{
|
||||
if (m_pose != null)
|
||||
m_useBvh = false;
|
||||
}
|
||||
|
||||
public void UpdatePose(Animator pose)
|
||||
{
|
||||
var fkRetarget = m_controller.Runtime.GetOrCreateFkRetarget();
|
||||
foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones)))
|
||||
{
|
||||
m_pose.PoseClip = pose;
|
||||
m_pose.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseClip;
|
||||
if (bone == HumanBodyBones.LastBone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var dst = fkRetarget.GetBoneTransform(bone);
|
||||
if (dst == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_useBvh)
|
||||
{
|
||||
var src = pose.GetBoneTransform(bone);
|
||||
if (src != null)
|
||||
{
|
||||
// set normalized pose
|
||||
dst.localRotation = src.localRotation;
|
||||
}
|
||||
|
||||
if (bone == HumanBodyBones.Hips)
|
||||
{
|
||||
dst.position = src.position * fkRetarget.InitialHipsHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set TPose
|
||||
dst.localRotation = Quaternion.identity;
|
||||
if (bone == HumanBodyBones.Hips)
|
||||
{
|
||||
dst.position = Vector3.up * fkRetarget.InitialHipsHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
fkRetarget.Apply();
|
||||
}
|
||||
}
|
||||
Loaded m_loaded;
|
||||
|
|
@ -344,7 +375,7 @@ namespace UniVRM10.VRM10Viewer
|
|||
var context = new UniHumanoid.BvhImporterContext();
|
||||
context.Parse("tmp.bvh", source);
|
||||
context.Load();
|
||||
SetMotion(context.Root.GetComponent<HumanPoseTransfer>());
|
||||
SetMotion(context.Root.GetComponent<Animator>());
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
|
@ -369,7 +400,12 @@ namespace UniVRM10.VRM10Viewer
|
|||
}
|
||||
}
|
||||
|
||||
m_ui.UpdateToggle(() => m_loaded?.EnableBvh(m_src), () => m_loaded?.EnableTPose(m_pose));
|
||||
m_ui.UpdateToggle(() => m_loaded?.EnableBvh(), () => m_loaded?.EnableTPose());
|
||||
|
||||
if (m_loaded != null)
|
||||
{
|
||||
m_loaded.UpdatePose(m_src);
|
||||
}
|
||||
}
|
||||
|
||||
void OnOpenClicked()
|
||||
|
|
@ -506,17 +542,15 @@ namespace UniVRM10.VRM10Viewer
|
|||
|
||||
instance.ShowMeshes();
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
m_loaded = new Loaded(instance, m_src, m_target.transform);
|
||||
m_loaded = new Loaded(instance, m_target.transform);
|
||||
}
|
||||
|
||||
void SetMotion(HumanPoseTransfer src)
|
||||
void SetMotion(Animator src)
|
||||
{
|
||||
m_src = src;
|
||||
src.GetComponent<Renderer>().enabled = false;
|
||||
|
||||
if (m_loaded != null)
|
||||
{
|
||||
m_loaded.EnableBvh(m_src);
|
||||
m_loaded.EnableBvh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user