Vrm10Importer の引き数を整理

* controlRigGenerationOption を削除
* controlRigInitialRotations に一本化
* IReadOnlyDictionary<HumanBodyBones, Quaternion> を使う
* XR_EXT_hand_tracking の親指のロールを修正
This commit is contained in:
ousttrue 2022-11-28 15:39:10 +09:00
parent 18c4b2c904
commit 7af375cf34
20 changed files with 171 additions and 78 deletions

View File

@ -40,7 +40,7 @@ namespace UniVRM10
var materialGenerator = GetMaterialDescriptorGenerator(renderPipeline);
using (var loader = new Vrm10Importer(result, extractedObjects, materialGenerator: materialGenerator))
using (var loader = new Vrm10Importer(result, controlRigInitialRotations: null, externalObjectMap: extractedObjects, materialGenerator: materialGenerator))
{
// settings TextureImporters
foreach (var textureInfo in loader.TextureDescriptorGenerator.Get().GetEnumerable())

View File

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

View File

@ -1,16 +0,0 @@
namespace UniVRM10
{
public enum ControlRigGenerationOption
{
/// <summary>
/// コントロールリグを生成しません。
/// </summary>
None,
/// <summary>
/// 推奨されるオプションです。
/// コントロールリグのボーン Transform を生成し、Root の Animator はコントロールリグのボーンを制御するようになります。
/// </summary>
Generate,
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9723f89e671a460188d12cbcceccaa4e
timeCreated: 1663314138

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ebc9416609f7b734f87e487dfa0ae076
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UniVRM10
{
public enum ControlRigGenerationOption
{
/// <summary>
/// コントロールリグを生成しません。
/// </summary>
None = 0,
/// <summary>
/// 推奨されるオプションです。
/// コントロールリグのボーン Transform を生成し、Root の Animator はコントロールリグのボーンを制御するようになります。
/// </summary>
Generate = 1,
Vrm0XCompatibleRig = 1,
/// <summary>
/// コントロールリグのボーン Transform を生成し、Root の Animator はコントロールリグのボーンを制御するようになります。
/// 手と指に関して、XR_EXT_hand_tracking の初期回転を持ちます。
/// https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_tracking
/// </summary>
Vrm0XCompatibleWithXR_EXT_hand_tracking = 2,
}
public static class ControlRigGenerationOptionExtensions
{
public static IReadOnlyDictionary<HumanBodyBones, Quaternion> ToInitialRotations(this ControlRigGenerationOption option)
{
switch (option)
{
case ControlRigGenerationOption.None:
return null;
case ControlRigGenerationOption.Generate:
// case ControlRigGenerationOption.Vrm0XCompatibleRig:
return Vrm0XCompatibleRig.InitialRotations;
case ControlRigGenerationOption.Vrm0XCompatibleWithXR_EXT_hand_tracking:
return XR_EXT_hand_tracking.InitialRotations;
default:
throw new ArgumentException();
}
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 45a6dbb978533164dafbcbf6f28e5117
guid: 254b61afe523dd74b9579ff1e971bee7
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using UnityEngine;
namespace UniVRM10
{
public static class Vrm0XCompatibleRig
{
/// <summary>
/// 空の Dictionary を返します。
/// HumanBodyBones に対応する Quaternion が無い場合は Quaternion.Identity を採用する仕様です。
/// VRM-0.X では T-Pose のときにすべてのボーンが Quaternion.Identity です。
/// </summary>
public static IReadOnlyDictionary<HumanBodyBones, Quaternion> InitialRotations => new Dictionary<HumanBodyBones, Quaternion>();
}
}

View File

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

View File

@ -4,14 +4,23 @@ using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// TPose のときに XR_EXT_hand_tracking の joint が向いている向きを定義する
/// XR_EXT_hand_tracking の joint を VRM-1.0 の TPose に当てはめたときの方向を定義します
///
/// https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#_conventions_of_hand_joints
/// * https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#_conventions_of_hand_joints
/// * https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_vrm-1.0/tpose.ja.md
///
/// Unityは左手系なので、X軸を反転させます。
/// </summary>
public static class OpenXRHandTracking
public static class XR_EXT_hand_tracking
{
/// <summary>
/// up vector と forward vector の外積により空間を算出して、回転を得ます。
///
/// OpenXR は右手系なのに対して Unityは左手系です。
/// 結果として、X軸が反転することに注意してください。
/// </summary>
/// <param name="up"></param>
/// <param name="forward"></param>
/// <returns></returns>
public static Quaternion GetRotation(Vector3 up, Vector3 forward)
{
var xAxis = Vector3.Cross(up, forward).normalized;
@ -20,11 +29,22 @@ namespace UniVRM10
}
public static Quaternion LeftHand = GetRotation(Vector3.up, Vector3.right);
public static Quaternion LeftThumb = GetRotation(Vector3.up, (Vector3.right + Vector3.forward).normalized);
public static Quaternion RightHand = GetRotation(Vector3.up, Vector3.left);
public static Quaternion RightThumb = GetRotation(Vector3.up, (Vector3.left + Vector3.forward).normalized);
public static readonly Dictionary<HumanBodyBones, Quaternion> InitialRotations = new Dictionary<HumanBodyBones, Quaternion>()
/// <summary>
/// 親指は XZ 平面45度です。
/// </summary>
public static Quaternion LeftThumb = GetRotation((Vector3.forward + Vector3.left).normalized, (Vector3.right + Vector3.forward).normalized);
/// <summary>
/// 親指は XZ 平面45度です。
/// </summary>
public static Quaternion RightThumb = GetRotation((Vector3.forward + Vector3.right).normalized, (Vector3.left + Vector3.forward).normalized);
/// <summary>
/// VRM-1.0 の T-Pose の定義から各指はX軸と並行です。親指はXZ平面に45度です。
/// </summary>
public static IReadOnlyDictionary<HumanBodyBones, Quaternion> InitialRotations => new Dictionary<HumanBodyBones, Quaternion>()
{
// Left
{HumanBodyBones.LeftHand, LeftHand},

View File

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

View File

@ -46,7 +46,7 @@ namespace UniVRM10
private readonly Quaternion _initialTargetGlobalRotation;
private readonly List<Vrm10ControlBone> _children = new List<Vrm10ControlBone>();
private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent, Dictionary<HumanBodyBones, Quaternion> initialRotations)
private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations)
{
if (boneType == HumanBodyBones.LastBone)
{
@ -61,9 +61,9 @@ namespace UniVRM10
ControlTarget = controlTarget;
// NOTE: bone name must be unique in the vrm instance.
ControlBone = new GameObject($"{nameof(Vrm10ControlBone)}:{boneType.ToString()}").transform;
if (initialRotations != null)
if (controlRigInitialRotations != null)
{
if (initialRotations.TryGetValue(boneType, out var rotation))
if (controlRigInitialRotations.TryGetValue(boneType, out var rotation))
{
ControlBone.rotation = rotation;
}
@ -86,7 +86,7 @@ namespace UniVRM10
/// <summary>
/// 初期姿勢からの相対的な回転。
///
/// VRM-0.X では localRotation と同じである
/// VRM-0.X 互換リグでは localRotation と同じ値を示す
/// </summary>
Quaternion NormalizedLocalRotation
{
@ -109,32 +109,32 @@ namespace UniVRM10
}
}
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, Dictionary<HumanBodyBones, Quaternion> initialRotations, out Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations, out Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
{
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null, initialRotations);
var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null, controlRigInitialRotations);
boneMap = new Dictionary<HumanBodyBones, Vrm10ControlBone>();
boneMap.Add(HumanBodyBones.Hips, hips);
foreach (Transform child in humanoid.Hips)
{
BuildRecursively(humanoid, child, hips, initialRotations, boneMap);
BuildRecursively(humanoid, child, hips, controlRigInitialRotations, boneMap);
}
return hips;
}
private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, Dictionary<HumanBodyBones, Quaternion> initialRotations, Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, IReadOnlyDictionary<HumanBodyBones, Quaternion> controlRigInitialRotations, Dictionary<HumanBodyBones, Vrm10ControlBone> boneMap)
{
if (humanoid.TryGetBoneForTransform(current, out var bone))
{
var newBone = new Vrm10ControlBone(current, bone, parent, initialRotations);
var newBone = new Vrm10ControlBone(current, bone, parent, controlRigInitialRotations);
parent = newBone;
boneMap.Add(bone, newBone);
}
foreach (Transform child in current)
{
BuildRecursively(humanoid, child, parent, initialRotations, boneMap);
BuildRecursively(humanoid, child, parent, controlRigInitialRotations, boneMap);
}
}
}

View File

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

View File

@ -25,17 +25,22 @@ namespace UniVRM10
public float InitialHipsHeight { get; }
/// <summary>
/// コンストラクタ。
/// humanoid は VRM T-Pose でなければならない。
/// humanoid に対して ControlRig を生成します
/// </summary>
public Vrm10RuntimeControlRig(UniHumanoid.Humanoid humanoid, Transform vrmRoot, ControlRigGenerationOption option, Dictionary<HumanBodyBones, Quaternion> initialRotations)
/// <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)
{
if (option == ControlRigGenerationOption.None) return;
if (controlRigInitialRotations == null)
{
throw new ArgumentNullException();
}
_controlRigRoot = new GameObject("Runtime Control Rig").transform;
_controlRigRoot.SetParent(vrmRoot);
_hipBone = Vrm10ControlBone.Build(humanoid, initialRotations, out _bones);
_hipBone = Vrm10ControlBone.Build(humanoid, controlRigInitialRotations, out _bones);
_hipBone.ControlBone.SetParent(_controlRigRoot);
InitialHipsHeight = _hipBone.ControlTarget.position.y;

View File

@ -39,7 +39,6 @@ namespace UniVRM10
string path,
bool canLoadVrm0X = true,
ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
Dictionary<HumanBodyBones, Quaternion> initialRotations = null,
bool showMeshes = true,
IAwaitCaller awaitCaller = null,
IMaterialDescriptorGenerator materialGenerator = null,
@ -58,7 +57,6 @@ namespace UniVRM10
System.IO.File.ReadAllBytes(path),
canLoadVrm0X,
controlRigGenerationOption,
initialRotations,
showMeshes,
awaitCaller,
materialGenerator,
@ -85,7 +83,6 @@ namespace UniVRM10
byte[] bytes,
bool canLoadVrm0X = true,
ControlRigGenerationOption controlRigGenerationOption = ControlRigGenerationOption.Generate,
Dictionary<HumanBodyBones, Quaternion> initialRotations = null,
bool showMeshes = true,
IAwaitCaller awaitCaller = null,
IMaterialDescriptorGenerator materialGenerator = null,
@ -104,7 +101,6 @@ namespace UniVRM10
bytes,
canLoadVrm0X,
controlRigGenerationOption,
initialRotations,
showMeshes,
awaitCaller,
materialGenerator,
@ -117,7 +113,6 @@ namespace UniVRM10
byte[] bytes,
bool canLoadVrm0X,
ControlRigGenerationOption controlRigGenerationOption,
Dictionary<HumanBodyBones, Quaternion> initialRotations,
bool showMeshes,
IAwaitCaller awaitCaller,
IMaterialDescriptorGenerator materialGenerator,
@ -136,7 +131,6 @@ namespace UniVRM10
var instance = await TryLoadingAsVrm10Async(
gltfData,
controlRigGenerationOption,
initialRotations,
showMeshes,
awaitCaller,
materialGenerator,
@ -162,7 +156,6 @@ namespace UniVRM10
var migratedInstance = await TryMigratingFromVrm0XAsync(
gltfData,
controlRigGenerationOption,
initialRotations,
showMeshes,
awaitCaller,
materialGenerator,
@ -186,7 +179,6 @@ namespace UniVRM10
private static async Task<Vrm10Instance> TryLoadingAsVrm10Async(
GltfData gltfData,
ControlRigGenerationOption controlRigGenerationOption,
Dictionary<HumanBodyBones, Quaternion> initialRotations,
bool showMeshes,
IAwaitCaller awaitCaller,
IMaterialDescriptorGenerator materialGenerator,
@ -212,7 +204,6 @@ namespace UniVRM10
vrm10Data,
null,
controlRigGenerationOption,
initialRotations,
showMeshes,
awaitCaller,
materialGenerator,
@ -223,7 +214,6 @@ namespace UniVRM10
private static async Task<Vrm10Instance> TryMigratingFromVrm0XAsync(
GltfData gltfData,
ControlRigGenerationOption controlRigGenerationOption,
Dictionary<HumanBodyBones, Quaternion> initialRotations,
bool showMeshes,
IAwaitCaller awaitCaller,
IMaterialDescriptorGenerator materialGenerator,
@ -251,7 +241,6 @@ namespace UniVRM10
migratedVrm10Data,
migrationData,
controlRigGenerationOption,
initialRotations,
showMeshes,
awaitCaller,
materialGenerator,
@ -269,7 +258,6 @@ namespace UniVRM10
Vrm10Data vrm10Data,
MigrationData migrationData,
ControlRigGenerationOption controlRigGenerationOption,
Dictionary<HumanBodyBones, Quaternion> initialRotations,
bool showMeshes,
IAwaitCaller awaitCaller,
IMaterialDescriptorGenerator materialGenerator,
@ -287,7 +275,7 @@ namespace UniVRM10
throw new ArgumentNullException(nameof(vrm10Data));
}
using (var loader = new Vrm10Importer(vrm10Data, controlRigGenerationOption: controlRigGenerationOption, initialRotations: initialRotations, materialGenerator: materialGenerator))
using (var loader = new Vrm10Importer(vrm10Data, controlRigGenerationOption.ToInitialRotations(), materialGenerator: materialGenerator))
{
// 1. Load meta information if callback was available.
if (vrmMetaInformationCallback != null)

View File

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

View File

@ -45,7 +45,7 @@ namespace UniVRM10.Test
private (GameObject, IReadOnlyList<VRMShaders.MaterialFactory.MaterialLoadInfo>) ToUnity(Vrm10Data data)
{
// Model => Unity
using (var loader = new Vrm10Importer(data))
using (var loader = new Vrm10Importer(data, null))
{
var loaded = loader.Load();
return (loaded.gameObject, loader.MaterialFactory.Materials);

View File

@ -21,7 +21,7 @@ namespace UniVRM10.Test
GameObject BuildGameObject(Vrm10Data data, bool showMesh)
{
using (var loader = new Vrm10Importer(data))
using (var loader = new Vrm10Importer(data, null))
{
var loaded = loader.Load();
if (showMesh)

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, ControlRigGenerationOption.None);
var r = new Vrm10Runtime(controller, null);
}
[Test]
@ -56,7 +56,7 @@ namespace UniVRM10.Test
controller.Vrm.Expression.Aa.MaterialUVBindings = src.ToArray();
// ok if no exception
var r = new Vrm10Runtime(controller, ControlRigGenerationOption.None);
var r = new Vrm10Runtime(controller, null);
}
}
}

View File

@ -19,7 +19,7 @@ namespace UniVRM10.Test
// empty thumbnail name
vrm1Data.Data.GLTF.images[index].name = null;
using (var loader = new Vrm10Importer(vrm1Data))
using (var loader = new Vrm10Importer(vrm1Data, null))
{
loader.LoadAsync(new VRMShaders.ImmediateCaller()).Wait();
}