diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/Bvh.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Format/Bvh.cs index 1f8b9126e..01712b3b5 100644 --- a/Assets/UniGLTF/Runtime/UniHumanoid/Format/Bvh.cs +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/Bvh.cs @@ -6,178 +6,6 @@ using System.Linq; namespace UniHumanoid { - public class BvhException : Exception - { - public BvhException(string msg) : base(msg) { } - } - - public enum Channel - { - Xposition, - Yposition, - Zposition, - Xrotation, - Yrotation, - Zrotation, - } - public static class ChannelExtensions - { - public static string ToProperty(this Channel ch) - { - switch (ch) - { - case Channel.Xposition: return "localPosition.x"; - case Channel.Yposition: return "localPosition.y"; - case Channel.Zposition: return "localPosition.z"; - case Channel.Xrotation: return "localEulerAnglesBaked.x"; - case Channel.Yrotation: return "localEulerAnglesBaked.y"; - case Channel.Zrotation: return "localEulerAnglesBaked.z"; - } - - throw new BvhException("no property for " + ch); - } - - public static bool IsLocation(this Channel ch) - { - switch (ch) - { - case Channel.Xposition: - case Channel.Yposition: - case Channel.Zposition: return true; - case Channel.Xrotation: - case Channel.Yrotation: - case Channel.Zrotation: return false; - } - - throw new BvhException("no property for " + ch); - } - } - - public struct Single3 - { - public Single x; - public Single y; - public Single z; - - public Single3(Single _x, Single _y, Single _z) - { - x = _x; - y = _y; - z = _z; - } - } - - public class BvhNode - { - public String Name - { - get; - private set; - } - - public Single3 Offset - { - get; - private set; - } - - public Channel[] Channels - { - get; - private set; - } - - public List Children - { - get; - private set; - } - - public BvhNode(string name) - { - Name = name; - Children = new List(); - } - - public virtual void Parse(StringReader r) - { - Offset = ParseOffset(r.ReadLine()); - - Channels = ParseChannel(r.ReadLine()); - } - - static Single3 ParseOffset(string line) - { - var split = line.Trim().Split(); - if (split[0] != "OFFSET") - { - throw new BvhException("OFFSET is not found"); - } - - var offset = split.Skip(1).Where(x => !string.IsNullOrEmpty(x)).Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray(); - return new Single3(offset[0], offset[1], offset[2]); - } - - static Channel[] ParseChannel(string line) - { - var split = line.Trim().Split(); - if (split[0] != "CHANNELS") - { - throw new BvhException("CHANNELS is not found"); - } - var count = int.Parse(split[1]); - if (count + 2 != split.Length) - { - throw new BvhException("channel count is not match with split count"); - } - return split.Skip(2).Select(x => (Channel)Enum.Parse(typeof(Channel), x)).ToArray(); - } - - public IEnumerable Traverse() - { - yield return this; - - foreach (var child in Children) - { - foreach (var descendant in child.Traverse()) - { - yield return descendant; - } - } - } - } - - public class EndSite : BvhNode - { - public EndSite() : base("") - { - } - - public override void Parse(StringReader r) - { - r.ReadLine(); // offset - } - } - - public class ChannelCurve - { - public float[] Keys - { - get; - private set; - } - - public ChannelCurve(int frameCount) - { - Keys = new float[frameCount]; - } - - public void SetKey(int frame, float value) - { - Keys[frame] = value; - } - } - public class Bvh { public BvhNode Root @@ -192,7 +20,7 @@ namespace UniHumanoid private set; } - public ChannelCurve[] Channels + public BvhChannelCurve[] Channels { get; private set; @@ -211,7 +39,7 @@ namespace UniHumanoid public bool IsLocation; } - public bool TryGetPathWithPropertyFromChannel(ChannelCurve channel, out PathWithProperty pathWithProp) + public bool TryGetPathWithPropertyFromChannel(BvhChannelCurve channel, out PathWithProperty pathWithProp) { var index = Channels.ToList().IndexOf(channel); if (index == -1) @@ -270,7 +98,7 @@ namespace UniHumanoid return null; } - public ChannelCurve GetChannel(BvhNode target, Channel channel) + public BvhChannelCurve GetChannel(BvhNode target, BvhChannel channel) { var index = 0; foreach (var node in Root.Traverse()) @@ -306,7 +134,7 @@ namespace UniHumanoid .Select(x => x.Channels.Length) .Sum(); Channels = Enumerable.Range(0, channelCount) - .Select(x => new ChannelCurve(frames)) + .Select(x => new BvhChannelCurve(frames)) .ToArray() ; } @@ -410,7 +238,7 @@ namespace UniHumanoid { throw new BvhException("End in level 0"); } - node = new EndSite(); + node = new BvhEndSite(); } else { @@ -433,7 +261,7 @@ namespace UniHumanoid break; } - if (!(child is EndSite)) + if (!(child is BvhEndSite)) { node.Children.Add(child); } diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannel.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannel.cs new file mode 100644 index 000000000..3c28916f6 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannel.cs @@ -0,0 +1,45 @@ +namespace UniHumanoid +{ + public enum BvhChannel + { + Xposition, + Yposition, + Zposition, + Xrotation, + Yrotation, + Zrotation, + } + + public static class BvhChannelExtensions + { + public static string ToProperty(this BvhChannel ch) + { + switch (ch) + { + case BvhChannel.Xposition: return "localPosition.x"; + case BvhChannel.Yposition: return "localPosition.y"; + case BvhChannel.Zposition: return "localPosition.z"; + case BvhChannel.Xrotation: return "localEulerAnglesBaked.x"; + case BvhChannel.Yrotation: return "localEulerAnglesBaked.y"; + case BvhChannel.Zrotation: return "localEulerAnglesBaked.z"; + } + + throw new BvhException("no property for " + ch); + } + + public static bool IsLocation(this BvhChannel ch) + { + switch (ch) + { + case BvhChannel.Xposition: + case BvhChannel.Yposition: + case BvhChannel.Zposition: return true; + case BvhChannel.Xrotation: + case BvhChannel.Yrotation: + case BvhChannel.Zrotation: return false; + } + + throw new BvhException("no property for " + ch); + } + } +} diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannel.cs.meta b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannel.cs.meta new file mode 100644 index 000000000..c977786a4 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 885c621c7dd0faf4e8e3de2e0187d2ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannelCurve.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannelCurve.cs new file mode 100644 index 000000000..dc0dd8814 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannelCurve.cs @@ -0,0 +1,21 @@ +namespace UniHumanoid +{ + public class BvhChannelCurve + { + public float[] Keys + { + get; + private set; + } + + public BvhChannelCurve(int frameCount) + { + Keys = new float[frameCount]; + } + + public void SetKey(int frame, float value) + { + Keys[frame] = value; + } + } +} diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannelCurve.cs.meta b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannelCurve.cs.meta new file mode 100644 index 000000000..c42c4951b --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhChannelCurve.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 588638a01ee0b7c47bd731aeed170cea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhEndSite.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhEndSite.cs new file mode 100644 index 000000000..63e8ca191 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhEndSite.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace UniHumanoid +{ + public class BvhEndSite : BvhNode + { + public BvhEndSite() : base("") + { + } + + public override void Parse(StringReader r) + { + r.ReadLine(); // offset + } + } +} diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhEndSite.cs.meta b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhEndSite.cs.meta new file mode 100644 index 000000000..7f31574ba --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhEndSite.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc2103bbe9c95224e9458e1fb058140f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhException.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhException.cs new file mode 100644 index 000000000..8e1fa95da --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhException.cs @@ -0,0 +1,9 @@ +using System; + +namespace UniHumanoid +{ + public class BvhException : Exception + { + public BvhException(string msg) : base(msg) { } + } +} diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhException.cs.meta b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhException.cs.meta new file mode 100644 index 000000000..2e9e07579 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86267a6edfc8fc749948c586e622846f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhNode.cs b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhNode.cs new file mode 100644 index 000000000..0421fe09f --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhNode.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace UniHumanoid +{ + public class BvhNode + { + public String Name + { + get; + private set; + } + + public Vector3 Offset + { + get; + private set; + } + + public BvhChannel[] Channels + { + get; + private set; + } + + public List Children + { + get; + private set; + } + + public BvhNode(string name) + { + Name = name; + Children = new List(); + } + + public int GetChannelIndex(BvhChannel channel) + { + for (int i = 0; i < Channels.Length; ++i) + { + if (channel == Channels[i]) + { + return i; + } + } + throw new KeyNotFoundException(); + } + + public virtual void Parse(StringReader r) + { + Offset = ParseOffset(r.ReadLine()); + + Channels = ParseChannel(r.ReadLine()); + } + + private static Vector3 ParseOffset(string line) + { + string[] split = line.Trim().Split(); + if (split[0] != "OFFSET") + { + throw new BvhException("OFFSET is not found"); + } + + var offset = split.Skip(1).Where(x => !string.IsNullOrEmpty(x)).Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray(); + return new Vector3(offset[0], offset[1], offset[2]); + } + + private static BvhChannel[] ParseChannel(string line) + { + var split = line.Trim().Split(); + if (split[0] != "CHANNELS") + { + throw new BvhException("CHANNELS is not found"); + } + var count = int.Parse(split[1]); + if (count + 2 != split.Length) + { + throw new BvhException("channel count is not match with split count"); + } + return split.Skip(2).Select(x => (BvhChannel)Enum.Parse(typeof(BvhChannel), x)).ToArray(); + } + + public IEnumerable Traverse() + { + yield return this; + + foreach (var child in Children) + { + foreach (var descendant in child.Traverse()) + { + yield return descendant; + } + } + } + } +} diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhNode.cs.meta b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhNode.cs.meta new file mode 100644 index 000000000..14b8c7f27 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniHumanoid/Format/BvhNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7f4ac3efd1faca94381a344a5be51cc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhAnimationClip.cs b/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhAnimationClip.cs index ea644f5c9..64036ab57 100644 --- a/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhAnimationClip.cs +++ b/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhAnimationClip.cs @@ -16,9 +16,9 @@ namespace UniHumanoid Node = node; } - public ChannelCurve PositionX; - public ChannelCurve PositionY; - public ChannelCurve PositionZ; + public BvhChannelCurve PositionX; + public BvhChannelCurve PositionY; + public BvhChannelCurve PositionZ; public Vector3 GetPosition(int i) { return new Vector3( @@ -27,9 +27,9 @@ namespace UniHumanoid PositionZ.Keys[i]); } - public ChannelCurve RotationX; - public ChannelCurve RotationY; - public ChannelCurve RotationZ; + public BvhChannelCurve RotationX; + public BvhChannelCurve RotationY; + public BvhChannelCurve RotationZ; public Quaternion GetRotation(int i) { if (EulerToRotation == null) @@ -43,7 +43,7 @@ namespace UniHumanoid ); } - static void AddCurve(Bvh bvh, AnimationClip clip, ChannelCurve ch, float scaling) + static void AddCurve(Bvh bvh, AnimationClip clip, BvhChannelCurve ch, float scaling) { if (ch == null) return; var pathWithProp = default(Bvh.PathWithProperty); @@ -106,12 +106,12 @@ namespace UniHumanoid var curve = bvh.Channels[j]; switch (node.Channels[i]) { - case Channel.Xposition: set.PositionX = curve; break; - case Channel.Yposition: set.PositionY = curve; break; - case Channel.Zposition: set.PositionZ = curve; break; - case Channel.Xrotation: set.RotationX = curve; break; - case Channel.Yrotation: set.RotationY = curve; break; - case Channel.Zrotation: set.RotationZ = curve; break; + case BvhChannel.Xposition: set.PositionX = curve; break; + case BvhChannel.Yposition: set.PositionY = curve; break; + case BvhChannel.Zposition: set.PositionZ = curve; break; + case BvhChannel.Xrotation: set.RotationX = curve; break; + case BvhChannel.Yrotation: set.RotationY = curve; break; + case BvhChannel.Zrotation: set.RotationZ = curve; break; default: throw new Exception(); } } diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhImporterContext.cs b/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhImporterContext.cs index 378810f81..5bdf53d8b 100644 --- a/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhImporterContext.cs +++ b/Assets/UniGLTF/Runtime/UniHumanoid/IO/BvhImporterContext.cs @@ -75,11 +75,15 @@ namespace UniHumanoid // // scaling. reposition // + var yCh = Bvh.Root.GetChannelIndex(BvhChannel.Yposition); + var curve = Bvh.Channels[yCh]; + var hipHeight = curve.Keys[0]; + float scaling = 1.0f; { //var foot = animator.GetBoneTransform(HumanBodyBones.LeftFoot); - var foot = hips.Traverse().Skip(skeleton.GetBoneIndex(HumanBodyBones.LeftFoot)).First(); - var hipHeight = hips.position.y - foot.position.y; + // var foot = hips.Traverse().Skip(skeleton.GetBoneIndex(HumanBodyBones.LeftFoot)).First(); + // var hipHeight = hips.position.y - foot.position.y; // hips height to a meter scaling = 1.0f / hipHeight; foreach (var x in Root.transform.Traverse()) diff --git a/Assets/UniGLTF/Runtime/UniHumanoid/IO/Extensions/BvhExtensions.cs b/Assets/UniGLTF/Runtime/UniHumanoid/IO/Extensions/BvhExtensions.cs index 2b80293f2..b9d737cd6 100644 --- a/Assets/UniGLTF/Runtime/UniHumanoid/IO/Extensions/BvhExtensions.cs +++ b/Assets/UniGLTF/Runtime/UniHumanoid/IO/Extensions/BvhExtensions.cs @@ -9,7 +9,7 @@ namespace UniHumanoid { public static Func GetEulerToRotation(this BvhNode bvh) { - var order = bvh.Channels.Where(x => x == Channel.Xrotation || x == Channel.Yrotation || x == Channel.Zrotation).ToArray(); + var order = bvh.Channels.Where(x => x == BvhChannel.Xrotation || x == BvhChannel.Yrotation || x == BvhChannel.Zrotation).ToArray(); return (x, y, z) => { @@ -22,9 +22,9 @@ namespace UniHumanoid { switch (ch) { - case Channel.Xrotation: r = r * xRot; break; - case Channel.Yrotation: r = r * yRot; break; - case Channel.Zrotation: r = r * zRot; break; + case BvhChannel.Xrotation: r = r * xRot; break; + case BvhChannel.Yrotation: r = r * yRot; break; + case BvhChannel.Zrotation: r = r * zRot; break; default: throw new BvhException("no rotation"); } } @@ -32,12 +32,12 @@ namespace UniHumanoid }; } - public static Vector3 ToVector3(this Single3 s3) + public static Vector3 ToVector3(this Vector3 s3) { return new Vector3(s3.x, s3.y, s3.z); } - public static Vector3 ToXReversedVector3(this Single3 s3) + public static Vector3 ToXReversedVector3(this Vector3 s3) { return new Vector3(-s3.x, s3.y, s3.z); } diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs index eec7aa06a..80c41055a 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10ControlBone.cs @@ -33,10 +33,11 @@ namespace UniVRM10 private readonly Quaternion _initialTargetLocalRotation; private readonly Quaternion _initialTargetGlobalRotation; + public Vector3 InitialTargetGlobalPosition { get; } private readonly List _children = new List(); public IReadOnlyList Children => _children; - private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent) + private Vrm10ControlBone(Transform controlTarget, HumanBodyBones boneType, Vrm10ControlBone parent, Transform root) { if (boneType == HumanBodyBones.LastBone) { @@ -62,6 +63,7 @@ namespace UniVRM10 _initialTargetLocalRotation = controlTarget.localRotation; _initialTargetGlobalRotation = controlTarget.rotation; + InitialTargetGlobalPosition = root.worldToLocalMatrix.MultiplyPoint(controlTarget.position); } /// @@ -76,32 +78,32 @@ namespace UniVRM10 } } - public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, out Dictionary boneMap) + public static Vrm10ControlBone Build(UniHumanoid.Humanoid humanoid, out Dictionary boneMap, Transform root) { - var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null); + var hips = new Vrm10ControlBone(humanoid.Hips, HumanBodyBones.Hips, null, root); boneMap = new Dictionary(); boneMap.Add(HumanBodyBones.Hips, hips); foreach (Transform child in humanoid.Hips) { - BuildRecursively(humanoid, child, hips, boneMap); + BuildRecursively(humanoid, child, hips, boneMap, root); } return hips; } - private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, Dictionary boneMap) + private static void BuildRecursively(UniHumanoid.Humanoid humanoid, Transform current, Vrm10ControlBone parent, Dictionary boneMap, Transform root) { if (humanoid.TryGetBoneForTransform(current, out var bone)) { - var newBone = new Vrm10ControlBone(current, bone, parent); + var newBone = new Vrm10ControlBone(current, bone, parent, root); parent = newBone; boneMap.Add(bone, newBone); } foreach (Transform child in current) { - BuildRecursively(humanoid, child, parent, boneMap); + BuildRecursively(humanoid, child, parent, boneMap, root); } } } diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs index 9abd96c5d..5af0d7732 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/ControlRig/Vrm10RuntimeControlRig.cs @@ -23,8 +23,6 @@ namespace UniVRM10 public IReadOnlyDictionary Bones => _bones; public Animator ControlRigAnimator { get; } - public float InitialHipsHeight => HipTPoseWorldPosition.y; - /// /// humanoid に対して ControlRig を生成します /// @@ -35,11 +33,9 @@ namespace UniVRM10 _controlRigRoot = new GameObject("Runtime Control Rig").transform; _controlRigRoot.SetParent(vrmRoot); - _hipBone = Vrm10ControlBone.Build(humanoid, out _bones); + _hipBone = Vrm10ControlBone.Build(humanoid, out _bones, _controlRigRoot); _hipBone.ControlBone.SetParent(_controlRigRoot); - HipTPoseWorldPosition = vrmRoot.worldToLocalMatrix.MultiplyPoint(_hipBone.ControlTarget.position); - var transformBonePairs = _bones.Select(kv => (kv.Value.ControlBone, kv.Key)); _controlRigAvatar = HumanoidLoader.LoadHumanoidAvatar(vrmRoot, transformBonePairs); _controlRigAvatar.name = "Runtime Control Rig"; @@ -72,19 +68,11 @@ namespace UniVRM10 } } - public void EnforceTPose() + #region INormalizedPoseApplicable + public void SetRawHipsPosition(Vector3 position) { - SetHipsPosition(_hipBone.ControlBone.position); - foreach (var bone in _bones.Values) - { - bone.ControlBone.localRotation = Quaternion.identity; - } - } - - public void SetHipsPosition(Vector3 position) - { - // TODO: scale model local position - _hipBone.ControlTarget.position = position; + var world = _controlRigRoot.TransformPoint(position); + _hipBone.ControlBone.position = world; } public void SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation) @@ -94,6 +82,7 @@ namespace UniVRM10 t.ControlBone.localRotation = normalizedLocalRotation; } } + #endregion IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> Traverse(Vrm10ControlBone bone, Vrm10ControlBone parent = null) { @@ -108,7 +97,8 @@ namespace UniVRM10 } } - public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones() + #region ITPoseProvider + public IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs() { foreach (var headParent in Traverse(_hipBone)) { @@ -116,11 +106,15 @@ namespace UniVRM10 } } - public Vector3 HipTPoseWorldPosition { get; } - - public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone) + public Quaternion? GetBoneWorldRotation(HumanBodyBones bone) { return Quaternion.identity; } + + public Vector3? GetBoneWorldPosition(HumanBodyBones bone) + { + return _bones[bone].InitialTargetGlobalPosition; + } + #endregion } } diff --git a/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs new file mode 100644 index 000000000..90742e54a --- /dev/null +++ b/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UniVRM10 +{ + /// + /// Animator から Pose 供給する。 + /// この Animator は以下の条件を満たすこと。 + /// + /// * TPoseであること + /// * 正規化済みであること + /// + /// + public sealed class AnimatorPoseProvider : INormalizedPoseProvider, ITPoseProvider + { + Transform m_root; + Animator m_animator; + Dictionary m_posMap = new Dictionary(); + + public AnimatorPoseProvider(Transform root, Animator animator) + { + m_root = root; + m_animator = animator; + + var hips = animator.GetBoneTransform(HumanBodyBones.Hips); + HipTPoseWorldPosition = root.worldToLocalMatrix.MultiplyPoint(hips.position); + + foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones))) + { + if(bone==HumanBodyBones.LastBone) + { + continue; + } + var t = animator.GetBoneTransform(bone); + if (t != null) + { + m_posMap[bone] = root.worldToLocalMatrix.MultiplyPoint(t.position); + } + } + } + + #region INormalizedPoseProvider + public Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) + { + if (m_animator.GetBoneTransform(bone) is Transform t) + { + // TODO: parentBone relative + return t.localRotation; + } + else + { + // Debug.LogWarning($"{bone} not found"); + return Quaternion.identity; + } + } + + public Vector3 GetRawHipsPosition() + { + // TODO: from model root ? + return m_animator.GetBoneTransform(HumanBodyBones.Hips).localPosition; + } + #endregion + + #region ITPoseProvider + public Vector3 HipTPoseWorldPosition { get; } + + public IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs() + { + throw new System.NotImplementedException(); + } + + public Quaternion? GetBoneWorldRotation(HumanBodyBones bone) + { + throw new System.NotImplementedException(); + } + + public Vector3? GetBoneWorldPosition(HumanBodyBones bone) + { + return m_posMap[bone]; + } + #endregion + } +} diff --git a/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs.meta b/Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs.meta similarity index 100% rename from Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs.meta rename to Assets/VRM10/Runtime/ControlRig/AnimatorPoseProvider.cs.meta diff --git a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs index 6d265a60f..30367b223 100644 --- a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs +++ b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseApplicable.cs @@ -5,7 +5,7 @@ namespace UniVRM10 { public interface INormalizedPoseApplicable { - void SetHipsPosition(Vector3 position); + void SetRawHipsPosition(Vector3 position); void SetNormalizedLocalRotation(HumanBodyBones bone, Quaternion normalizedLocalRotation); } diff --git a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs index 798b3e610..a4b6b6b9c 100644 --- a/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs +++ b/Assets/VRM10/Runtime/ControlRig/INormalizedPoseProvider.cs @@ -7,7 +7,7 @@ namespace UniVRM10 /// /// Get hips position in model root space /// - Vector3 GetHipsPosition(); + Vector3 GetRawHipsPosition(); /// /// Get normalized local rotation diff --git a/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs index eb355237d..1ffe5f60a 100644 --- a/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs +++ b/Assets/VRM10/Runtime/ControlRig/ITPoseProvider.cs @@ -6,22 +6,15 @@ namespace UniVRM10 public interface ITPoseProvider { /// - /// * world は ModelRoot を意図していることに注意 - /// - Vector3 HipTPoseWorldPosition { get; } - - /// - /// * world は ModelRoot を意図していることに注意 - /// * bone 無いときはどう振る舞うべきか - /// - /// - /// - Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone); - - /// - /// ボーンを親ボーンとセットで列挙する + /// このTPoseに含まれるボーンと親ボーンの組み合わせを列挙する。 + /// + /// * Humanoidの木構造を深さ優先の順番で列挙する + /// /// /// hips の場合は parent は HumanBodyBones.LastBone で null を表す - IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones(); + IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs(); + + Quaternion? GetBoneWorldRotation(HumanBodyBones bone); + Vector3? GetBoneWorldPosition(HumanBodyBones bone); } } diff --git a/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs index 6b4c0b325..a2d01d06a 100644 --- a/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs +++ b/Assets/VRM10/Runtime/ControlRig/InitialRotationPoseProvider.cs @@ -44,7 +44,7 @@ namespace UniVRM10 } } - public Vector3 GetHipsPosition() + public Vector3 GetRawHipsPosition() { if (m_hips.parent == m_root) { @@ -56,12 +56,17 @@ namespace UniVRM10 } } - public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone) + public IEnumerable<(HumanBodyBones Bone, HumanBodyBones Parent)> EnumerateBoneParentPairs() { throw new System.NotImplementedException(); } - public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones() + public Quaternion? GetBoneWorldRotation(HumanBodyBones bone) + { + throw new System.NotImplementedException(); + } + + public Vector3? GetBoneWorldPosition(HumanBodyBones bone) { throw new System.NotImplementedException(); } diff --git a/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs b/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs deleted file mode 100644 index 7d2ba7874..000000000 --- a/Assets/VRM10/Runtime/ControlRig/NormalizedPoseProvider.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace UniVRM10 -{ - /// - /// 正規化済みのヒエラルキーの getter。 - /// localRotation をコピーするだけなのだが、 - /// GetNormalizedLocalRotation にて、ボーンの有無(upperChestなど)の違いの対応をする予定(未実装)。 - /// - public sealed class NormalizedPoseProvider : INormalizedPoseProvider, ITPoseProvider - { - Transform m_root; - Animator m_animator; - - public Vector3 HipTPoseWorldPosition => throw new System.NotImplementedException(); - - public NormalizedPoseProvider(Transform root, Animator animator) - { - m_root = root; - m_animator = animator; - } - - public Quaternion GetNormalizedLocalRotation(HumanBodyBones bone, HumanBodyBones parentBone) - { - if (m_animator.GetBoneTransform(bone) is Transform t) - { - // TODO: parentBone relative - return t.localRotation; - } - else - { - // Debug.LogWarning($"{bone} not found"); - return Quaternion.identity; - } - } - - public Vector3 GetHipsPosition() - { - // TODO: from model root ? - return m_animator.GetBoneTransform(HumanBodyBones.Hips).localPosition; - } - - public Quaternion GetBoneTPoseWorldRotation(HumanBodyBones bone) - { - throw new System.NotImplementedException(); - } - - public IEnumerable<(HumanBodyBones Head, HumanBodyBones Parent)> EnumerateBones() - { - throw new System.NotImplementedException(); - } - } -} diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Loaded.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Loaded.cs index 60dc9e012..c2e2198dc 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; @@ -87,115 +88,5 @@ namespace UniVRM10.VRM10Viewer // destroy GameObject 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; - controlRig.EnforceTPose(); - } } } diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs new file mode 100644 index 000000000..b165fd69f --- /dev/null +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Motion.cs @@ -0,0 +1,72 @@ +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 AnimatorPoseProvider(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; + } + } + } + } +} 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/VRM10Retarget.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs index 3af5fc621..7825fd36e 100644 --- a/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs +++ b/Assets/VRM10_Samples/VRM10Viewer/VRM10Retarget.cs @@ -1,15 +1,31 @@ -namespace UniVRM10 +using UnityEngine; + +namespace UniVRM10.VRM10Viewer { public static class VRM10Retarget { public static void Retarget((INormalizedPoseProvider Pose, ITPoseProvider TPose) source, (INormalizedPoseApplicable Pose, ITPoseProvider TPose) sink) { - foreach (var (head, parent) in sink.TPose.EnumerateBones()) + foreach (var (head, parent) in sink.TPose.EnumerateBoneParentPairs()) { var q = source.Pose.GetNormalizedLocalRotation(head, parent); sink.Pose.SetNormalizedLocalRotation(head, q); } - sink.Pose.SetHipsPosition(source.Pose.GetHipsPosition()); + + // scaling hips position + var scaling = sink.TPose.GetBoneWorldPosition(HumanBodyBones.LeftUpperLeg).Value.y / source.TPose.GetBoneWorldPosition(HumanBodyBones.LeftUpperLeg).Value.y; + var delta = source.Pose.GetRawHipsPosition() - source.TPose.GetBoneWorldPosition(HumanBodyBones.Hips).Value; + sink.Pose.SetRawHipsPosition(sink.TPose.GetBoneWorldPosition(HumanBodyBones.Hips).Value + delta * scaling); + } + + public static void EnforceTPose((INormalizedPoseApplicable Pose, ITPoseProvider TPose) sink) + { + foreach (var (bone, parent) in sink.TPose.EnumerateBoneParentPairs()) + { + sink.Pose.SetNormalizedLocalRotation(bone, Quaternion.identity); + } + + sink.Pose.SetRawHipsPosition(sink.TPose.GetBoneWorldPosition(HumanBodyBones.Hips).Value); } } } diff --git a/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs b/Assets/VRM10_Samples/VRM10Viewer/VRM10ViewerUI.cs index e73e89caf..2c8c9bf3a 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,11 +237,11 @@ namespace UniVRM10.VRM10Viewer { if (m_ui.IsBvhEnabled && m_src != null) { - m_loaded.UpdateControlRigImplicit(m_src); + VRM10Retarget.Retarget(m_src.ControlRig, (m_loaded.ControlRig, m_loaded.ControlRig)); } else { - m_loaded.TPoseControlRig(); + VRM10Retarget.EnforceTPose((m_loaded.ControlRig, m_loaded.ControlRig)); } } } @@ -280,7 +263,7 @@ namespace UniVRM10.VRM10Viewer var ext = Path.GetExtension(path).ToLower(); if (ext == ".bvh") { - LoadMotion(path); + m_src = VRM10Motion.LoadBvhFromPath(path); return; }