diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Loaded.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Loaded.cs
index 60dc9e012..9fbde3277 100644
--- a/Assets/VRM10_Samples/VRM10Viewer/VRM10Loaded.cs
+++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Loaded.cs
@@ -9,6 +9,7 @@ namespace UniVRM10.VRM10Viewer
{
RuntimeGltfInstance m_instance;
Vrm10Instance m_controller;
+ public Vrm10RuntimeControlRig ControlRig => m_controller.Runtime.ControlRig;
VRM10AIUEO m_lipSync;
bool m_enableLipSyncValue;
@@ -88,110 +89,6 @@ namespace UniVRM10.VRM10Viewer
GameObject.Destroy(m_instance.gameObject);
}
- ///
- /// from v0.103
- ///
- ///
- public void UpdateControlRigExplicit(Animator src)
- {
- var controlRig = m_controller.Runtime.ControlRig;
-
- foreach (HumanBodyBones bone in CachedEnum.GetValues())
- {
- if (bone == HumanBodyBones.LastBone)
- {
- continue;
- }
-
- var controlRigBone = controlRig.GetBoneTransform(bone);
- if (controlRigBone == null)
- {
- continue;
- }
-
- var bvhBone = src.GetBoneTransform(bone);
- if (bvhBone != null)
- {
- // set normalized pose
- controlRigBone.localRotation = bvhBone.localRotation;
- }
-
- if (bone == HumanBodyBones.Hips)
- {
- controlRigBone.localPosition = bvhBone.localPosition * controlRig.InitialHipsHeight;
- }
- }
- }
-
- ///
- /// from v0.104
- ///
- public void UpdateControlRigImplicit(Animator src)
- {
- var dst = m_controller.GetComponent();
-
- foreach (HumanBodyBones bone in CachedEnum.GetValues())
- {
- if (bone == HumanBodyBones.LastBone)
- {
- continue;
- }
-
- var boneTransform = dst.GetBoneTransform(bone);
- if (boneTransform == null)
- {
- continue;
- }
-
- var bvhBone = src.GetBoneTransform(bone);
- if (bvhBone != null)
- {
- // set normalized pose
- boneTransform.localRotation = bvhBone.localRotation;
- }
-
- if (bone == HumanBodyBones.Hips)
- {
- // TODO: hips position scaling ?
- boneTransform.localPosition = bvhBone.localPosition;
- }
- }
- }
-
- ///
- /// from v0.108
- ///
- public void UpdateControlRigImplicit(UniHumanoid.Humanoid src)
- {
- var dst = m_controller.GetComponent();
-
- foreach (HumanBodyBones bone in CachedEnum.GetValues())
- {
- if (bone == HumanBodyBones.LastBone)
- {
- continue;
- }
-
- var boneTransform = dst.GetBoneTransform(bone);
- if (boneTransform == null)
- {
- continue;
- }
-
- var bvhBone = src.GetBoneTransform(bone);
- if (bvhBone != null)
- {
- // set normalized pose
- boneTransform.localRotation = bvhBone.localRotation;
- if (bone == HumanBodyBones.Hips)
- {
- // TODO: hips position scaling ?
- boneTransform.localPosition = bvhBone.localPosition;
- }
- }
- }
- }
-
public void TPoseControlRig()
{
var controlRig = m_controller.Runtime.ControlRig;
diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs
new file mode 100644
index 000000000..08107b32b
--- /dev/null
+++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using UniGLTF;
+using UniHumanoid;
+using UniJSON;
+using UnityEngine;
+using VRMShaders;
+
+namespace UniVRM10.VRM10Viewer
+{
+ public class VRM10Motion
+ {
+ public (INormalizedPoseProvider, ITPoseProvider) ControlRig;
+
+ UniHumanoid.BvhImporterContext m_context;
+ UniGLTF.RuntimeGltfInstance m_instance;
+
+ public Transform Root => m_context?.Root.transform;
+
+ public VRM10Motion(UniHumanoid.BvhImporterContext context)
+ {
+ m_context = context;
+ var provider = new NormalizedPoseProvider(m_context.Root.transform, m_context.Root.GetComponent());
+ ControlRig = (provider, provider);
+ }
+
+ public VRM10Motion(UniGLTF.RuntimeGltfInstance instance)
+ {
+ m_instance = instance;
+ if (instance.GetComponent() is Animation animation)
+ {
+ animation.Play();
+ }
+ }
+
+ public void ShowBoxMan(bool showBoxMan)
+ {
+ if (m_context != null)
+ {
+ m_context.Root.GetComponent().enabled = showBoxMan;
+ }
+ }
+
+ public static VRM10Motion LoadBvhFromText(string source, string path = "tmp.bvh")
+ {
+ var context = new UniHumanoid.BvhImporterContext();
+ context.Parse(path, source);
+ context.Load();
+ return new VRM10Motion(context);
+ }
+
+ public static VRM10Motion LoadBvhFromPath(string path)
+ {
+ return LoadBvhFromText(File.ReadAllText(path), path);
+ }
+
+ static IEnumerable Traverse(Transform t)
+ {
+ yield return t;
+ foreach (Transform child in t)
+ {
+ foreach (var x in Traverse(child))
+ {
+ yield return x;
+ }
+ }
+ }
+
+ #region experimental vrm-animation deserializer
+ //
+ // vrm-animation の簡易実装
+ //
+ // https://github.com/vrm-c/vrm-animation
+ //
+ static float ForceMeterScale(Dictionary map)
+ {
+ var positionMap = map.ToDictionary(kv => kv.Key, kv => kv.Value.position);
+ var hipsHeight = positionMap[HumanBodyBones.Hips].y;
+
+ float scaling = 1.0f;
+ if (hipsHeight > 80)
+ {
+ // cm スケールであると見做す
+ scaling = 0.01f;
+ }
+ return scaling;
+ }
+
+ static bool TryGet(JsonNode obj, string key, out JsonNode found)
+ {
+ foreach (var kv in obj.ObjectItems())
+ {
+ if (kv.Key.GetString() == key)
+ {
+ found = kv.Value;
+ return true;
+ }
+ }
+ found = default;
+ return false;
+ }
+
+ static (HumanBodyBones, int) ToTuple(KeyValuePair kv)
+ {
+ if (TryGet(kv.Value, "node", out var value))
+ {
+ var name = kv.Key.GetString();
+ switch (name)
+ {
+ case "rightThumbMetacarpal":
+ return (HumanBodyBones.RightThumbProximal, value.GetInt32());
+ case "leftThumbMetacarpal":
+ return (HumanBodyBones.LeftThumbProximal, value.GetInt32());
+ case "rightThumbProximal":
+ return (HumanBodyBones.RightThumbIntermediate, value.GetInt32());
+ case "leftThumbProximal":
+ return (HumanBodyBones.LeftThumbIntermediate, value.GetInt32());
+ default:
+ return ((HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), name, true), value.GetInt32());
+ }
+ }
+ throw new Exception();
+ }
+
+ static Dictionary GetHumanMap(GltfData data, IReadOnlyList nodes)
+ {
+ var humanMap = new Dictionary();
+ if (data.GLTF.extensions is UniGLTF.glTFExtensionImport extensions)
+ {
+ foreach (var kv in extensions.ObjectItems())
+ {
+ if (kv.Key.GetString() == "VRMC_vrm_animation")
+ {
+ var animation = kv.Value;
+ if (TryGet(animation, "humanoid", out var animation_humanoid))
+ {
+ if (TryGet(animation_humanoid, "humanBones", out var bones))
+ {
+ foreach (var kkvv in bones.ObjectItems())
+ {
+ var (bone, index) = ToTuple(kkvv);
+ // Debug.Log($"{bone} => {index}");
+ humanMap.Add(bone, nodes[index]);
+ }
+ }
+ }
+ }
+ }
+ }
+ return humanMap;
+ }
+ #endregion
+
+ public static async Task LoadVrmAnimationFromPathAsync(string path)
+ {
+ //
+ // GetHumanoid Mapping
+ //
+ using (GltfData data = new AutoGltfFileParser(path).Parse())
+ using (var loader = new UniGLTF.ImporterContext(data))
+ {
+ loader.InvertAxis = Axes.X;
+ // loader.PositionScaling = 0.01f;
+ var instance = await loader.LoadAsync(new ImmediateCaller());
+ var humanMap = GetHumanMap(data, loader.Nodes);
+ if (humanMap.Count == 0)
+ {
+ throw new ArgumentException("fail to load VRMC_vrm_animation");
+ }
+
+ var scaling = ForceMeterScale(humanMap);
+ // instance.transform.localScale = new Vector3(scaling, scaling, scaling);
+ var description = AvatarDescription.Create(humanMap);
+
+ //
+ // avatar
+ //
+ var avatar = description.CreateAvatar(instance.Root.transform);
+ avatar.name = "Avatar";
+ // AvatarDescription = description;
+ var animator = instance.gameObject.AddComponent();
+ animator.avatar = avatar;
+
+ // create SkinnedMesh for bone visualize
+ var renderer = SkeletonMeshUtility.CreateRenderer(animator);
+ var material = new Material(Shader.Find("Standard"));
+ renderer.sharedMaterial = material;
+ var mesh = renderer.sharedMesh;
+ mesh.name = "box-man";
+
+ var humanoid = instance.gameObject.AddComponent();
+ humanoid.AssignBonesFromAnimator();
+ var motion = new VRM10Motion(instance);
+ var poseProvider = new InitRotationPoseProvider(instance.transform, humanoid);
+ motion.ControlRig = (poseProvider, poseProvider);
+ return motion;
+ }
+ }
+
+ public void Retarget(Vrm10RuntimeControlRig dst)
+ {
+ VRM10Retarget.Retarget(ControlRig, (dst, dst));
+ }
+ }
+}
diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs.meta b/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs.meta
new file mode 100644
index 000000000..a3c573986
--- /dev/null
+++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 08f06b12436bf594b9982c6a8ec71374
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs
index e73e89caf..42424e86d 100644
--- a/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs
+++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs
@@ -3,7 +3,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using UniGLTF;
-using UniHumanoid;
using UnityEngine;
using UnityEngine.UI;
using VRMShaders;
@@ -34,18 +33,15 @@ namespace UniVRM10.VRM10Viewer
[SerializeField]
Toggle m_useAsync = default;
- [Header("Runtime")]
- [SerializeField]
- Animator m_src = default;
-
[SerializeField]
GameObject m_target = default;
[SerializeField]
+ TextAsset m_motion;
+
GameObject Root = default;
- [SerializeField]
- TextAsset m_motion;
+ VRM10Motion m_src = default;
private CancellationTokenSource _cancellationTokenSource;
@@ -181,8 +177,6 @@ namespace UniVRM10.VRM10Viewer
var texts = GameObject.FindObjectsOfType();
m_version = texts.First(x => x.name == "Version");
- m_src = GameObject.FindObjectOfType();
-
m_target = GameObject.FindObjectOfType().gameObject;
}
@@ -197,7 +191,7 @@ namespace UniVRM10.VRM10Viewer
// load initial bvh
if (m_motion != null)
{
- LoadMotion(m_motion.text);
+ m_src = VRM10Motion.LoadBvhFromText(m_motion.text);
}
string[] cmds = System.Environment.GetCommandLineArgs();
@@ -217,17 +211,6 @@ namespace UniVRM10.VRM10Viewer
_cancellationTokenSource?.Dispose();
}
- private void LoadMotion(string source)
- {
- var context = new UniHumanoid.BvhImporterContext();
- context.Parse("tmp.bvh", File.ReadAllText(source));
- context.Load();
- m_src = context.Root.GetComponent();
- m_ui.IsBvhEnabled = true;
- // hide box man
- context.Root.GetComponent().enabled = false;
- }
-
private void Update()
{
if (m_loaded != null)
@@ -254,7 +237,7 @@ namespace UniVRM10.VRM10Viewer
{
if (m_ui.IsBvhEnabled && m_src != null)
{
- m_loaded.UpdateControlRigImplicit(m_src);
+ m_src.Retarget(m_loaded.ControlRig);
}
else
{
@@ -280,7 +263,7 @@ namespace UniVRM10.VRM10Viewer
var ext = Path.GetExtension(path).ToLower();
if (ext == ".bvh")
{
- LoadMotion(path);
+ m_src = VRM10Motion.LoadBvhFromPath(path);
return;
}