Merge pull request #1775 from Santarh/controlRig2

Refactoring about ControlRig
This commit is contained in:
ousttrue 2022-09-06 20:02:51 +09:00 committed by GitHub
commit 157c48ca06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 58 deletions

View File

@ -5,99 +5,94 @@ using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// The control bone of the control rig.
///
/// このクラスのヒエラルキーが 正規化された TPose を表している。
/// 同時に、元のヒエラルキーの初期回転を保持する。
/// Apply 関数で、再帰的に正規化済みのローカル回転から初期回転を加味したローカル回転を作って適用する。
/// </summary>
public class Vrm10ControlBone
public sealed class Vrm10ControlBone
{
public readonly HumanBodyBones Bone;
/// <summary>
/// このボーンに紐づく種類。
/// </summary>
public HumanBodyBones BoneType { get; }
/// <summary>
/// 元のヒエラルキーの対応ボーン
/// コントロール対象のボーン Transform。
/// </summary>
public readonly Transform Target;
public Transform ControlTarget { get; }
/// <summary>
/// 回転と拡大縮小を除去した(正規化された)ボーン。
/// このボーンに対して localRotation を代入する。
/// コントロールのためのボーン Transform。
///
/// VRM の T-Pose 姿勢をしているときに、回転とスケールが初期値になっている(正規化)。
/// このボーンに対して localRotation を代入し、コントロールを行う。
/// </summary>
public readonly Transform Normalized;
public Transform ControlBone { get; }
/// <summary>
/// 元のボーンの初期回転。
/// </summary>
public readonly Quaternion InitialLocalRotation;
private readonly Quaternion _initialTargetLocalRotation;
private readonly Quaternion _initialTargetGlobalRotation;
private readonly List<Vrm10ControlBone> _children = new List<Vrm10ControlBone>();
public readonly Quaternion ToLocal;
public List<Vrm10ControlBone> Children = new List<Vrm10ControlBone>();
public Vrm10ControlBone(Transform current, Quaternion parentInverse, HumanBodyBones bone)
internal Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType)
{
if (bone == HumanBodyBones.LastBone)
if (boneType == HumanBodyBones.LastBone)
{
throw new ArgumentNullException();
}
if (current == null)
if (controlTarget == 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;
BoneType = boneType;
ControlTarget = controlTarget;
ControlBone = new GameObject(boneType.ToString()).transform;
ControlBone.position = controlTarget.position;
_initialTargetLocalRotation = controlTarget.localRotation;
_initialTargetGlobalRotation = controlTarget.rotation;
}
/// <summary>
/// 親から再帰的にNormalized の ローカル回転を初期回転を加味して Target に適用する。
/// </summary>
internal void ProcessRecursively()
{
ControlTarget.localRotation = _initialTargetLocalRotation * Quaternion.Inverse(_initialTargetGlobalRotation) * ControlBone.localRotation * _initialTargetGlobalRotation;
foreach (var child in _children)
{
child.ProcessRecursively();
}
}
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
{
var hips = new Vrm10ControlBone(humanoid.Hips, Quaternion.identity, HumanBodyBones.Hips);
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips);
boneMap.Add(HumanBodyBones.Hips, hips);
foreach (Transform child in humanoid.Hips)
{
Traverse(humanoid, child, hips, boneMap);
BuildRecursively(humanoid, child, hips, boneMap);
}
return hips;
}
private static void Traverse(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)
{
if (humanoid.TryGetBoneForTransform(current, out var bone))
{
// ヒューマンボーンだけを対象にするので、
// parent が current の直接の親でない場合がある。
// ワールド回転 parent^-1 * current からローカル回転を算出する。
var parentInverse = Quaternion.Inverse(parent.Target.rotation);
var newBone = new Vrm10ControlBone(current, parentInverse, bone);
newBone.Normalized.SetParent(parent.Normalized, true);
parent.Children.Add(newBone);
var newBone = new Vrm10ControlBone(current, bone);
newBone.ControlBone.SetParent(parent.ControlBone, 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);
BuildRecursively(humanoid, child, parent, boneMap);
}
}
}

View File

@ -9,7 +9,14 @@ using UniVRM10.FastSpringBones.System;
namespace UniVRM10
{
/// <summary>
/// Play時 と Editorからの参照情報置き場
/// VRM モデルインスタンスを、状態をもって、元の状態から操作・変更するためのクラス。
/// また、仕様に従ってその操作を行う。
///
/// 操作対象としては以下が挙げられる。
/// - ControlRig
/// - Constraint
/// - LookAt
/// - Expression
/// </summary>
public class Vrm10Runtime : IDisposable
{

View File

@ -10,31 +10,31 @@ namespace UniVRM10
/// Create a control rig for the VRM 1.0 model instance.
/// This provides the normalized operation of bones, like VRM 0.x.
/// </summary>
public class Vrm10RuntimeControlRig
public sealed class Vrm10RuntimeControlRig
{
private readonly Vrm10ControlBone _rootBone;
private readonly Dictionary<HumanBodyBones, Vrm10ControlBone> _bones = new Dictionary<HumanBodyBones, Vrm10ControlBone>();
public readonly float InitialHipsHeight;
public float InitialHipsHeight { get; }
public Vrm10RuntimeControlRig(UniHumanoid.Humanoid humanoid)
{
_rootBone = Vrm10ControlBone.Build(humanoid, _bones);
InitialHipsHeight = _rootBone.Target.position.y;
InitialHipsHeight = _rootBone.ControlTarget.position.y;
Debug.Log($"InitialHipsHeight: {InitialHipsHeight}");
}
public void Process()
{
_rootBone.Target.position = _rootBone.Normalized.position;
_rootBone.ApplyRecursive(Quaternion.identity);
_rootBone.ControlTarget.position = _rootBone.ControlBone.position;
_rootBone.ProcessRecursively();
}
public Transform GetBoneTransform(HumanBodyBones bone)
{
if (_bones.TryGetValue(bone, out var value))
{
return value.Normalized;
return value.ControlBone;
}
else
{