Vrm10RuntimeControlRig は、正規化されたTPoseを受け取る責務に特化して単純化する。

初期回転を持つポーズの受付は、Vrm10BoneInitialRotation を使う別クラスに委譲する。
(次で作ります)
This commit is contained in:
ousttrue
2023-02-20 13:53:18 +09:00
parent 867eb08ebe
commit a82dfd4611
10 changed files with 92 additions and 84 deletions

View File

@@ -67,12 +67,8 @@ namespace UniVRM10
/// <summary>
/// ControlRig の生成オプション
///
/// null: ControlRigGenerationOption.None
/// empty: ControlRigGenerationOption.Generate = Vrm0XCompatibleRig
/// other: ControlRigGenerationOption.Vrm0XCompatibleWithXR_EXT_hand_tracking など
/// </summary>
private IReadOnlyDictionary<HumanBodyBones, Quaternion> m_controlRigInitialRotations;
private bool m_useControlRig;
/// <summary>
/// VRM ファイルに記録された Humanoid ボーンに対応します。
@@ -99,15 +95,15 @@ namespace UniVRM10
{
if (m_runtime == null)
{
m_runtime = new Vrm10Runtime(this, m_controlRigInitialRotations);
m_runtime = new Vrm10Runtime(this, m_useControlRig);
}
return m_runtime;
}
}
internal void InitializeAtRuntime(IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations)
internal void InitializeAtRuntime(bool useControlRig)
{
m_controlRigInitialRotations = controlRigInitialRotations;
m_useControlRig = useControlRig;
}
void Start()

View File

@@ -49,7 +49,7 @@ namespace UniVRM10
}
}
public Vrm10Runtime(Vrm10Instance target, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations)
public Vrm10Runtime(Vrm10Instance target, bool useControlRig)
{
m_target = target;
@@ -58,9 +58,9 @@ namespace UniVRM10
throw new Exception();
}
if (controlRigInitialRotations != null)
if (useControlRig)
{
ControlRig = new Vrm10RuntimeControlRig(target.Humanoid, m_target.transform, controlRigInitialRotations);
ControlRig = new Vrm10RuntimeControlRig(target.Humanoid, m_target.transform);
}
Constraints = target.GetComponentsInChildren<IVrm10Constraint>();
LookAt = new Vrm10RuntimeLookAt(target.Vrm.LookAt, target.Humanoid, ControlRig);

View File

@@ -29,18 +29,12 @@ namespace UniVRM10
/// </summary>
/// <param name="humanoid">T-Pose である必要があります</param>
/// <param name="vrmRoot"></param>
/// <param name="controlRigInitialRotations">ControlRigの各ボーンの初期回転を表します</param>
public Vrm10RuntimeControlRig(UniHumanoid.Humanoid humanoid, Transform vrmRoot, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations)
public Vrm10RuntimeControlRig(UniHumanoid.Humanoid humanoid, Transform vrmRoot)
{
if (controlRigInitialRotations == null)
{
throw new ArgumentNullException();
}
_controlRigRoot = new GameObject("Runtime Control Rig").transform;
_controlRigRoot.SetParent(vrmRoot);
_hipBone = Vrm10ControlBone.Build(humanoid, controlRigInitialRotations, out _bones);
_hipBone = Vrm10ControlBone.Build(humanoid, out _bones);
_hipBone.ControlBone.SetParent(_controlRigRoot);
InitialHipsHeight = _hipBone.ControlTarget.position.y;
@@ -79,10 +73,11 @@ namespace UniVRM10
public void EnforceTPose()
{
// TODO: restore hips position
foreach (var bone in _bones.Values)
{
bone.ControlBone.localPosition = bone.InitialControlBoneLocalPosition;
bone.ControlBone.localRotation = bone.InitialControlBoneLocalRotation;
bone.ControlBone.localRotation = Quaternion.identity;
}
}
}

View File

@@ -0,0 +1,39 @@
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// Represents the rotation at the initial pose (TPose)
/// </summary>
public readonly struct Vrm10BoneInitialRotation
{
public readonly Transform Transform;
public readonly Vector3 InitialLocalPosition;
public readonly Quaternion InitialLocalRotation;
public readonly Quaternion InitialGlobalRotation;
public Vrm10BoneInitialRotation(Transform transform)
{
Transform = transform;
InitialLocalPosition = transform.localPosition;
InitialLocalRotation = transform.localRotation;
InitialGlobalRotation = transform.rotation;
}
/// <summary>
/// 初期姿勢からの相対的な回転。
///
/// VRM-0.X 互換リグでは localRotation と同じ値を示す。
/// </summary>
Quaternion NormalizedLocalRotation
{
get
{
return InitialGlobalRotation * Quaternion.Inverse(InitialLocalRotation) * Transform.localRotation * Quaternion.Inverse(InitialGlobalRotation);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db1187bf0b13152439c8a145d9631916
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -31,22 +31,11 @@ namespace UniVRM10
/// </summary>
public Transform ControlBone { get; }
/// <summary>
/// コントロールボーンの初期ローカル位置。
/// </summary>
public Vector3 InitialControlBoneLocalPosition { get; }
/// <summary>
/// コントロールボーンの初期ローカル回転。
/// </summary>
public Quaternion InitialControlBoneLocalRotation { get; }
private readonly Quaternion _initialControlBoneGlobalRotation;
private readonly Quaternion _initialTargetLocalRotation;
private readonly Quaternion _initialTargetGlobalRotation;
private readonly List<Vrm10ControlBone> _children = new List<Vrm10ControlBone>();
private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations)
private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent)
{
if (boneType == HumanBodyBones.LastBone)
{
@@ -59,81 +48,59 @@ namespace UniVRM10
BoneType = boneType;
ControlTarget = controlTarget;
// 回転とスケールが除去されたTPoseを構築
// NOTE: bone name must be unique in the vrm instance.
ControlBone = new GameObject($"{nameof(Vrm10ControlBone)}:{boneType.ToString()}").transform;
if (controlRigInitialRotations != null)
{
if (controlRigInitialRotations.TryGetValue(boneType, out var rotation))
{
ControlBone.rotation = rotation;
}
}
ControlBone.position = controlTarget.position;
if (parent != null)
{
ControlBone.SetParent(parent.ControlBone, true);
parent._children.Add(this);
}
InitialControlBoneLocalPosition = ControlBone.localPosition;
InitialControlBoneLocalRotation = ControlBone.localRotation;
_initialControlBoneGlobalRotation = ControlBone.rotation;
_initialTargetLocalRotation = controlTarget.localRotation;
_initialTargetGlobalRotation = controlTarget.rotation;
}
/// <summary>
/// 初期姿勢からの相対的な回転。
///
/// VRM-0.X 互換リグでは localRotation と同じ値を示す。
/// </summary>
Quaternion NormalizedLocalRotation
{
get
{
return _initialControlBoneGlobalRotation * Quaternion.Inverse(InitialControlBoneLocalRotation) * ControlBone.localRotation * Quaternion.Inverse(_initialControlBoneGlobalRotation);
}
}
/// <summary>
/// 親から再帰的にNormalized の ローカル回転を初期回転を加味して Target に適用する。
/// </summary>
internal void ProcessRecursively()
{
ControlTarget.localRotation = _initialTargetLocalRotation * (Quaternion.Inverse(_initialTargetGlobalRotation) * NormalizedLocalRotation * _initialTargetGlobalRotation);
ControlTarget.localRotation = _initialTargetLocalRotation * (Quaternion.Inverse(_initialTargetGlobalRotation) * ControlBone.localRotation * _initialTargetGlobalRotation);
foreach (var child in _children)
{
child.ProcessRecursively();
}
}
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations, out Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, out Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
{
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null, controlRigInitialRotations);
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null);
boneMap = new Dictionary<HumanBodyBones, Vrm10ControlBone>();
boneMap.Add(HumanBodyBones.Hips, hips);
foreach (Transform child in humanoid.Hips)
{
BuildRecursively(humanoid, child, hips, controlRigInitialRotations, boneMap);
BuildRecursively(humanoid, child, hips, boneMap);
}
return hips;
}
private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations, 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))
{
var newBone = new Vrm10ControlBone(current, bone, parent, controlRigInitialRotations);
var newBone = new Vrm10ControlBone(current, bone, parent);
parent = newBone;
boneMap.Add(bone, newBone);
}
foreach (Transform child in current)
{
BuildRecursively(humanoid, child, parent, controlRigInitialRotations, boneMap);
BuildRecursively(humanoid, child, parent, boneMap);
}
}
}

View File

@@ -39,7 +39,7 @@ namespace UniVRM10
public static async Task<Vrm10Instance> LoadPathAsync(
string path,
bool canLoadVrm0X = true,
ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
bool useControlRig = true,
bool showMeshes = true,
IAwaitCaller awaitCaller = null,
ITextureDeserializer textureDeserializer = null,
@@ -58,7 +58,7 @@ namespace UniVRM10
path,
System.IO.File.ReadAllBytes(path),
canLoadVrm0X,
controlRigGenerationOption,
useControlRig,
showMeshes,
awaitCaller,
textureDeserializer,
@@ -86,7 +86,7 @@ namespace UniVRM10
public static async Task<Vrm10Instance> LoadBytesAsync(
byte[] bytes,
bool canLoadVrm0X = true,
ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
bool useControlRig = true,
bool showMeshes = true,
IAwaitCaller awaitCaller = null,
ITextureDeserializer textureDeserializer = null,
@@ -105,7 +105,7 @@ namespace UniVRM10
string.Empty,
bytes,
canLoadVrm0X,
controlRigGenerationOption,
useControlRig,
showMeshes,
awaitCaller,
textureDeserializer,
@@ -118,7 +118,7 @@ namespace UniVRM10
string name,
byte[] bytes,
bool canLoadVrm0X,
ControlRigGenerationOption controlRigGenerationOption,
bool useControlRig,
bool showMeshes,
IAwaitCaller awaitCaller,
ITextureDeserializer textureDeserializer,
@@ -137,7 +137,7 @@ namespace UniVRM10
// 1. Try loading as vrm-1.0
var instance = await TryLoadingAsVrm10Async(
gltfData,
controlRigGenerationOption,
useControlRig,
showMeshes,
awaitCaller,
textureDeserializer,
@@ -163,7 +163,7 @@ namespace UniVRM10
// 3. Try migration from vrm-0.x into vrm-1.0
var migratedInstance = await TryMigratingFromVrm0XAsync(
gltfData,
controlRigGenerationOption,
useControlRig,
showMeshes,
awaitCaller,
textureDeserializer,
@@ -187,7 +187,7 @@ namespace UniVRM10
private static async Task<Vrm10Instance> TryLoadingAsVrm10Async(
GltfData gltfData,
ControlRigGenerationOption controlRigGenerationOption,
bool useControlRig,
bool showMeshes,
IAwaitCaller awaitCaller,
ITextureDeserializer textureDeserializer,
@@ -213,7 +213,7 @@ namespace UniVRM10
return await LoadVrm10DataAsync(
vrm10Data,
null,
controlRigGenerationOption,
useControlRig,
showMeshes,
awaitCaller,
textureDeserializer,
@@ -224,7 +224,7 @@ namespace UniVRM10
private static async Task<Vrm10Instance> TryMigratingFromVrm0XAsync(
GltfData gltfData,
ControlRigGenerationOption controlRigGenerationOption,
bool useControlRig,
bool showMeshes,
IAwaitCaller awaitCaller,
ITextureDeserializer textureDeserializer,
@@ -252,7 +252,7 @@ namespace UniVRM10
var migratedVrm10Instance = await LoadVrm10DataAsync(
migratedVrm10Data,
migrationData,
controlRigGenerationOption,
useControlRig,
showMeshes,
awaitCaller,
textureDeserializer,
@@ -270,7 +270,7 @@ namespace UniVRM10
private static async Task<Vrm10Instance> LoadVrm10DataAsync(
Vrm10Data vrm10Data,
MigrationData migrationData,
ControlRigGenerationOption controlRigGenerationOption,
bool useControlRig,
bool showMeshes,
IAwaitCaller awaitCaller,
ITextureDeserializer textureDeserializer,
@@ -293,7 +293,7 @@ namespace UniVRM10
vrm10Data,
textureDeserializer: textureDeserializer,
materialGenerator: materialGenerator,
controlRigInitialRotations: controlRigGenerationOption.ToInitialRotations()))
useControlRig: useControlRig))
{
// 1. Load meta information if callback was available.
if (vrmMetaInformationCallback != null)

View File

@@ -18,7 +18,7 @@ namespace UniVRM10
private readonly Vrm10Data m_vrm;
/// VrmLib.Model の オブジェクトと UnityEngine.Object のマッピングを記録する
private readonly ModelMap m_map = new ModelMap();
private readonly IReadOnlyDictionary<HumanBodyBones, Quaternion> m_controlRigInitialRotations;
private readonly bool m_useControlRig;
private VrmLib.Model m_model;
private IReadOnlyDictionary<SubAssetKey, UnityEngine.Object> m_externalMap;
@@ -31,7 +31,7 @@ namespace UniVRM10
IReadOnlyDictionary<SubAssetKey, UnityEngine.Object> externalObjectMap = null,
ITextureDeserializer textureDeserializer = null,
IMaterialDescriptorGenerator materialGenerator = null,
IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations = null
bool useControlRig = false
)
: base(vrm.Data, externalObjectMap, textureDeserializer)
{
@@ -40,7 +40,7 @@ namespace UniVRM10
throw new ArgumentNullException("vrm");
}
m_vrm = vrm;
m_controlRigInitialRotations = controlRigInitialRotations;
m_useControlRig = useControlRig;
TextureDescriptorGenerator = new Vrm10TextureDescriptorGenerator(Data);
MaterialDescriptorGenerator = materialGenerator ?? new BuiltInVrm10MaterialDescriptorGenerator();
@@ -247,7 +247,7 @@ namespace UniVRM10
// VrmController
var controller = Root.AddComponent<Vrm10Instance>();
controller.InitializeAtRuntime(m_controlRigInitialRotations);
controller.InitializeAtRuntime(m_useControlRig);
controller.enabled = false;
// vrm

View File

@@ -32,7 +32,7 @@ namespace UniVRM10.Test
controller.Vrm.Expression.Aa.MaterialColorBindings = src.ToArray();
// ok if no exception
var r = new Vrm10Runtime(controller, null);
var r = new Vrm10Runtime(controller, useControlRig: false);
}
[Test]
@@ -56,7 +56,7 @@ namespace UniVRM10.Test
controller.Vrm.Expression.Aa.MaterialUVBindings = src.ToArray();
// ok if no exception
var r = new Vrm10Runtime(controller, null);
var r = new Vrm10Runtime(controller, useControlRig: false);
}
}
}

View File

@@ -199,7 +199,7 @@ namespace UniVRM10
{
try
{
Vrm10.LoadPathAsync(gltf.FullName, true, ControlRigGenerationOption.None).Wait();
Vrm10.LoadPathAsync(gltf.FullName, true, useControlRig: false).Wait();
}
catch (UnNormalizedException)
{