using System; using System.Collections.Generic; using UnityEngine; namespace UniVRM10 { /// /// The control bone of the control rig. /// /// このクラスのヒエラルキーが 正規化された TPose を表している。 /// 同時に、元のヒエラルキーの初期回転を保持する。 /// Apply 関数で、再帰的に正規化済みのローカル回転から初期回転を加味したローカル回転を作って適用する。 /// public sealed class Vrm10ControlBone { /// /// このボーンに紐づく種類。 /// public HumanBodyBones BoneType { get; } /// /// コントロール対象のボーン Transform。 /// public Transform ControlTarget { get; } /// /// コントロールのためのボーン Transform。 /// /// VRM の T-Pose 姿勢をしているときに、回転とスケールが初期値になっている(正規化)。 /// このボーンに対して localRotation を代入し、コントロールを行う。 /// public Transform ControlBone { get; } private readonly Quaternion _initialTargetLocalRotation; private readonly Quaternion _initialTargetGlobalRotation; private readonly List _children = new List(); internal Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType) { if (boneType == HumanBodyBones.LastBone) { throw new ArgumentNullException(); } if (controlTarget == null) { throw new ArgumentNullException(); } BoneType = boneType; ControlTarget = controlTarget; ControlBone = new GameObject(boneType.ToString()).transform; ControlBone.position = controlTarget.position; _initialTargetLocalRotation = controlTarget.localRotation; _initialTargetGlobalRotation = controlTarget.rotation; } /// /// 親から再帰的にNormalized の ローカル回転を初期回転を加味して Target に適用する。 /// 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 boneMap) { var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips); boneMap.Add(HumanBodyBones.Hips, hips); foreach (Transform child in humanoid.Hips) { BuildRecursively(humanoid, child, hips, boneMap); } return hips; } private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, Dictionary boneMap) { if (humanoid.TryGetBoneForTransform(current, out var bone)) { 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) { BuildRecursively(humanoid, child, parent, boneMap); } } } }