From 0b08507f7aef04e1ba68666ca8e04c701685c611 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Tue, 24 Sep 2024 14:37:31 +0900 Subject: [PATCH] IVrm10SpringBoneRuntime.InitializeAsync --- .../Components/Vrm10Instance/Vrm10Instance.cs | 1 + .../FastSpringBoneBufferFactory.cs | 100 +++++++++++++++++ .../FastSpringBoneBufferFactory.cs.meta | 11 ++ .../Vrm10FastSpringboneRuntime.cs | 105 +++++------------- .../Vrm10FastSpringboneRuntimeStandalone.cs | 97 ++++------------ .../Components/Vrm10Runtime/Vrm10Runtime.cs | 1 - .../Runtime/IO/IVrm10SpringBoneRuntime.cs | 4 +- Assets/VRM10/Runtime/IO/Vrm10Importer.cs | 2 + .../VRM10Viewer/VRM10Viewer.unity | 1 + 9 files changed, 166 insertions(+), 156 deletions(-) create mode 100644 Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs create mode 100644 Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs.meta diff --git a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs index 17b9665b93..41a7cede0a 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Instance/Vrm10Instance.cs @@ -122,6 +122,7 @@ internal Vrm10Runtime MakeRuntime(bool useControlRig) // deafult に fallback // TODO: scene に配置した prefab に SpringRuntime をカスタムする手段 m_springBoneRuntime = new Vrm10FastSpringboneRuntime(); + m_springBoneRuntime.InitializeAsync(this, new ImmediateCaller()); } return new Vrm10Runtime(this, useControlRig, m_springBoneRuntime); } diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs new file mode 100644 index 0000000000..7d7033f4b8 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs @@ -0,0 +1,100 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using UniGLTF; +using UniGLTF.SpringBoneJobs.Blittables; +using UniGLTF.SpringBoneJobs.InputPorts; +using UniGLTF.Utils; +using UnityEngine; + +namespace UniVRM10 +{ + public static class FastSpringBoneBufferFactory + { + /// + /// このVRMに紐づくSpringBone関連のバッファを構築する。 + /// + /// + /// TODO: 再利用する + /// + public static async Task ConstructSpringBoneAsync(IAwaitCaller awaitCaller, Vrm10Instance vrm, + FastSpringBoneBuffer fastSpringBoneBuffer = null) + { + // TODO: Dispose せずに再利用する最適化 + // new FastSpringBoneBuffer にも構築ロジックがあるので合体して整理する必要あり。 + // GC 軽減と await 挟み込み + if (fastSpringBoneBuffer != null) + { + fastSpringBoneBuffer.Dispose(); + } + + Func GetOrAddDefaultTransformState = (Transform tf) => + { + if (vrm.DefaultTransformStates.TryGetValue(tf, out var defaultTransformState)) + { + return defaultTransformState; + } + + Debug.LogWarning($"{tf.name} does not exist on load."); + return new TransformState(null); + }; + + // create(Spring情報の再収集。設定変更の反映) + var springs = vrm.SpringBone.Springs.Select(spring => new FastSpringBoneSpring + { + center = spring.Center, + colliders = spring.ColliderGroups + .SelectMany(group => group.Colliders) + .Select(collider => new FastSpringBoneCollider + { + Transform = collider.transform, + Collider = new BlittableCollider + { + offset = collider.Offset, + radius = collider.Radius, + tailOrNormal = collider.TailOrNormal, + colliderType = TranslateColliderType(collider.ColliderType) + } + }).ToArray(), + joints = spring.Joints + .Select(joint => new FastSpringBoneJoint + { + Transform = joint.transform, + Joint = new BlittableJointMutable + { + radius = joint.m_jointRadius, + dragForce = joint.m_dragForce, + gravityDir = joint.m_gravityDir, + gravityPower = joint.m_gravityPower, + stiffnessForce = joint.m_stiffnessForce + }, + DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation, + }).ToArray(), + }).ToArray(); + + await awaitCaller.NextFrame(); + + fastSpringBoneBuffer = new FastSpringBoneBuffer(springs); + return fastSpringBoneBuffer; + } + + private static BlittableColliderType TranslateColliderType(VRM10SpringBoneColliderTypes colliderType) + { + switch (colliderType) + { + case VRM10SpringBoneColliderTypes.Sphere: + return BlittableColliderType.Sphere; + case VRM10SpringBoneColliderTypes.Capsule: + return BlittableColliderType.Capsule; + case VRM10SpringBoneColliderTypes.Plane: + return BlittableColliderType.Plane; + case VRM10SpringBoneColliderTypes.SphereInside: + return BlittableColliderType.SphereInside; + case VRM10SpringBoneColliderTypes.CapsuleInside: + return BlittableColliderType.CapsuleInside; + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs.meta new file mode 100644 index 0000000000..6b41eaf815 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f46ef09901938a64aa8a6ce21d1ae044 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs index 8fe124cb9a..96d190bf9b 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs @@ -1,10 +1,8 @@ using System; -using System.Linq; using UniGLTF; -using UniGLTF.Utils; using UnityEngine; -using UniGLTF.SpringBoneJobs.Blittables; using UniGLTF.SpringBoneJobs.InputPorts; +using System.Threading.Tasks; namespace UniVRM10 { @@ -17,9 +15,12 @@ public class Vrm10FastSpringboneRuntime : IVrm10SpringBoneRuntime { private Vrm10Instance m_instance; private readonly FastSpringBones.FastSpringBoneService m_fastSpringBoneService = FastSpringBones.FastSpringBoneService.Instance; - private FastSpringBoneSpring[] m_springs; - private Quaternion[] m_initialLocalRotations; private FastSpringBoneBuffer m_fastSpringBoneBuffer; + /// + /// 多重実行防止 + /// + private bool m_building = false; + public Vector3 ExternalForce { get => m_fastSpringBoneBuffer.ExternalForce; @@ -33,15 +34,16 @@ public bool IsSpringBoneEnabled public float DeltaTime => throw new NotImplementedException(); - /// VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫 - public void Initialize(Vrm10Instance instance) + public async Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller) { m_instance = instance; // NOTE: FastSpringBoneService は UnitTest などでは動作しない if (Application.isPlaying) { - ReconstructSpringBone(); + m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(awaitCaller, m_instance); + // 登録 + m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer); } } @@ -57,93 +59,40 @@ public void Dispose() /// public void ReconstructSpringBone() { - // release - if (m_fastSpringBoneBuffer != null) + if (m_building) { - m_fastSpringBoneService.BufferCombiner.Unregister(m_fastSpringBoneBuffer); - m_fastSpringBoneBuffer.Dispose(); + Debug.LogWarning("already building"); + return; } + m_building = true; - // create(Spring情報の再収集。設定変更の反映) - m_springs = m_instance.SpringBone.Springs.Select(spring => new FastSpringBoneSpring - { - center = spring.Center, - colliders = spring.ColliderGroups - .SelectMany(group => group.Colliders) - .Select(collider => new FastSpringBoneCollider - { - Transform = collider.transform, - Collider = new BlittableCollider - { - offset = collider.Offset, - radius = collider.Radius, - tailOrNormal = collider.TailOrNormal, - colliderType = TranslateColliderType(collider.ColliderType) - } - }).ToArray(), - joints = spring.Joints - .Select(joint => new FastSpringBoneJoint - { - Transform = joint.transform, - Joint = new BlittableJointMutable - { - radius = joint.m_jointRadius, - dragForce = joint.m_dragForce, - gravityDir = joint.m_gravityDir, - gravityPower = joint.m_gravityPower, - stiffnessForce = joint.m_stiffnessForce - }, - DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation, - }).ToArray(), - }).ToArray(); - - // DOTS buffer 構築 - m_fastSpringBoneBuffer = new FastSpringBoneBuffer(m_springs); - m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer); - // reset 用の初期状態の記録 - m_initialLocalRotations = m_fastSpringBoneBuffer.Transforms.Select(x => x.localRotation).ToArray(); - } - - private TransformState GetOrAddDefaultTransformState(Transform tf) - { - if (m_instance.DefaultTransformStates.TryGetValue(tf, out var defaultTransformState)) + // 登録削除 + if (m_fastSpringBoneBuffer != null) { - return defaultTransformState; + m_fastSpringBoneService.BufferCombiner.Unregister(m_fastSpringBoneBuffer); } - Debug.LogWarning($"{tf.name} does not exist on load."); - return new TransformState(null); - } + // new ImmediateCaller() により即時実行して結果を得る。 + // スパイクは許容する。 + var task = FastSpringBoneBufferFactory.ConstructSpringBoneAsync(new ImmediateCaller(), m_instance, m_fastSpringBoneBuffer); + m_fastSpringBoneBuffer = task.Result; - private static BlittableColliderType TranslateColliderType(VRM10SpringBoneColliderTypes colliderType) - { - switch (colliderType) - { - case VRM10SpringBoneColliderTypes.Sphere: - return BlittableColliderType.Sphere; - case VRM10SpringBoneColliderTypes.Capsule: - return BlittableColliderType.Capsule; - case VRM10SpringBoneColliderTypes.Plane: - return BlittableColliderType.Plane; - case VRM10SpringBoneColliderTypes.SphereInside: - return BlittableColliderType.SphereInside; - case VRM10SpringBoneColliderTypes.CapsuleInside: - return BlittableColliderType.CapsuleInside; - default: - throw new ArgumentOutOfRangeException(); - } + // 登録 + m_fastSpringBoneService.BufferCombiner.Register(m_fastSpringBoneBuffer); + m_building = false; } public void RestoreInitialTransform() { // Spring の joint に対応する transform の回転を初期状態 + var instance = m_instance.GetComponent(); for (int i = 0; i < m_fastSpringBoneBuffer.Transforms.Length; ++i) { var transform = m_fastSpringBoneBuffer.Transforms[i]; - transform.localRotation = m_initialLocalRotations[i]; + transform.localRotation = instance.InitialTransformStates[transform].LocalRotation; } - // TODO: + // TODO: jobs のバッファにも反映する必要あり } public void Process() diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs index 780ae12d12..6a2e2e83ea 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs @@ -1,11 +1,11 @@ using System; -using System.Linq; using UniGLTF; using UniGLTF.Utils; using UnityEngine; using UniGLTF.SpringBoneJobs.Blittables; using UniGLTF.SpringBoneJobs.InputPorts; using UniGLTF.SpringBoneJobs; +using System.Threading.Tasks; namespace UniVRM10 { @@ -18,11 +18,10 @@ namespace UniVRM10 public class Vrm10FastSpringboneRuntimeStandalone : IVrm10SpringBoneRuntime { private Vrm10Instance m_instance; - private FastSpringBoneSpring[] m_springs; - private Quaternion[] m_initialLocalRotations; private FastSpringBoneBuffer m_fastSpringBoneBuffer; public FastSpringBoneBufferCombiner m_bufferCombiner = new(); private FastSpringBoneScheduler m_fastSpringBoneScheduler; + private bool m_building = false; public Vector3 ExternalForce { @@ -42,15 +41,15 @@ public Vrm10FastSpringboneRuntimeStandalone() m_fastSpringBoneScheduler = new(m_bufferCombiner); } - /// VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫 - public void Initialize(Vrm10Instance instance) + public async Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller) { m_instance = instance; // NOTE: FastSpringBoneService は UnitTest などでは動作しない if (Application.isPlaying) { - ReconstructSpringBone(); + m_fastSpringBoneBuffer = await FastSpringBoneBufferFactory.ConstructSpringBoneAsync(awaitCaller, m_instance, m_fastSpringBoneBuffer); + m_bufferCombiner.Register(m_fastSpringBoneBuffer); } } @@ -69,93 +68,39 @@ public void Dispose() /// public void ReconstructSpringBone() { - // release - if (m_fastSpringBoneBuffer != null) + if (m_building) { - m_bufferCombiner.Unregister(m_fastSpringBoneBuffer); - m_fastSpringBoneBuffer.Dispose(); + Debug.LogWarning("already building"); + return; } + m_building = true; - // create(Spring情報の再収集。設定変更の反映) - m_springs = m_instance.SpringBone.Springs.Select(spring => new FastSpringBoneSpring - { - center = spring.Center, - colliders = spring.ColliderGroups - .SelectMany(group => group.Colliders) - .Select(collider => new FastSpringBoneCollider - { - Transform = collider.transform, - Collider = new BlittableCollider - { - offset = collider.Offset, - radius = collider.Radius, - tailOrNormal = collider.TailOrNormal, - colliderType = TranslateColliderType(collider.ColliderType) - } - }).ToArray(), - joints = spring.Joints - .Select(joint => new FastSpringBoneJoint - { - Transform = joint.transform, - Joint = new BlittableJointMutable - { - radius = joint.m_jointRadius, - dragForce = joint.m_dragForce, - gravityDir = joint.m_gravityDir, - gravityPower = joint.m_gravityPower, - stiffnessForce = joint.m_stiffnessForce - }, - DefaultLocalRotation = GetOrAddDefaultTransformState(joint.transform).LocalRotation, - }).ToArray(), - }).ToArray(); - - // DOTS buffer 構築 - m_fastSpringBoneBuffer = new FastSpringBoneBuffer(m_springs); - m_bufferCombiner.Register(m_fastSpringBoneBuffer); - // reset 用の初期状態の記録 - m_initialLocalRotations = m_fastSpringBoneBuffer.Transforms.Select(x => x.localRotation).ToArray(); - } - - private TransformState GetOrAddDefaultTransformState(Transform tf) - { - if (m_instance.DefaultTransformStates.TryGetValue(tf, out var defaultTransformState)) + // 登録解除 + if (m_fastSpringBoneBuffer != null) { - return defaultTransformState; + m_bufferCombiner.Unregister(m_fastSpringBoneBuffer); } - Debug.LogWarning($"{tf.name} does not exist on load."); - return new TransformState(null); - } + // new ImmediateCaller() により即時実行して結果を得る。 + // スパイクは許容する。 + var task = FastSpringBoneBufferFactory.ConstructSpringBoneAsync(new ImmediateCaller(), m_instance, m_fastSpringBoneBuffer); + m_fastSpringBoneBuffer = task.Result; - private static BlittableColliderType TranslateColliderType(VRM10SpringBoneColliderTypes colliderType) - { - switch (colliderType) - { - case VRM10SpringBoneColliderTypes.Sphere: - return BlittableColliderType.Sphere; - case VRM10SpringBoneColliderTypes.Capsule: - return BlittableColliderType.Capsule; - case VRM10SpringBoneColliderTypes.Plane: - return BlittableColliderType.Plane; - case VRM10SpringBoneColliderTypes.SphereInside: - return BlittableColliderType.SphereInside; - case VRM10SpringBoneColliderTypes.CapsuleInside: - return BlittableColliderType.CapsuleInside; - default: - throw new ArgumentOutOfRangeException(); - } + // 登録 + m_bufferCombiner.Register(m_fastSpringBoneBuffer); } public void RestoreInitialTransform() { // Spring の joint に対応する transform の回転を初期状態 + var instance = m_instance.GetComponent(); for (int i = 0; i < m_fastSpringBoneBuffer.Transforms.Length; ++i) { var transform = m_fastSpringBoneBuffer.Transforms[i]; - transform.localRotation = m_initialLocalRotations[i]; + transform.localRotation = instance.InitialTransformStates[transform].LocalRotation; } - // TODO: + // TODO: jobs のバッファにも反映する必要あり } public void Process() diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index 513668482a..1e74ccca6d 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -59,7 +59,6 @@ public Vrm10Runtime(Vrm10Instance instance, bool useControlRig, IVrm10SpringBone LookAt = new Vrm10RuntimeLookAt(instance.Vrm.LookAt, instance.Humanoid, ControlRig); Expression = new Vrm10RuntimeExpression(instance, LookAt.EyeDirectionApplicable); SpringBone = springBoneRuntime; - SpringBone.Initialize(instance); } public void Dispose() diff --git a/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs index 4ed48cb4f9..0cbc906a31 100644 --- a/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs +++ b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs @@ -1,11 +1,13 @@ using System; +using System.Threading.Tasks; +using UniGLTF; using UnityEngine; namespace UniVRM10 { public interface IVrm10SpringBoneRuntime : IDisposable { - public void Initialize(Vrm10Instance instance); + public Task InitializeAsync(Vrm10Instance instance, IAwaitCaller awaitCaller); /// /// 主に singleton のバッチング更新。 diff --git a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs index a657b53f67..ed77743949 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs @@ -273,6 +273,8 @@ protected override async Task OnLoadHierarchy(IAwaitCaller awaitCaller, Func