diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs index 50fcc713c..8fe124cb9 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntime.cs @@ -31,6 +31,8 @@ namespace UniVRM10 set => m_fastSpringBoneBuffer.IsSpringBoneEnabled = value; } + public float DeltaTime => throw new NotImplementedException(); + /// VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫 public void Initialize(Vrm10Instance instance) { @@ -143,5 +145,10 @@ namespace UniVRM10 // TODO: } + + public void Process() + { + // FastSpringBoneService が実行するので何もしない + } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs new file mode 100644 index 000000000..9c9152ba6 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs @@ -0,0 +1,162 @@ +using System; +using System.Linq; +using UniGLTF; +using UniGLTF.Utils; +using UnityEngine; +using UniGLTF.SpringBoneJobs.Blittables; +using UniGLTF.SpringBoneJobs.InputPorts; +using UniGLTF.SpringBoneJobs; + +namespace UniVRM10 +{ + /// + /// FastSpringbone(job) で動作します。 + /// FastSpringBoneService(Singleton)を経由せずに直接実行します。 + /// + 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; + + public Vector3 ExternalForce + { + get => m_fastSpringBoneBuffer.ExternalForce; + set => m_fastSpringBoneBuffer.ExternalForce = value; + } + public bool IsSpringBoneEnabled + { + get => m_fastSpringBoneBuffer.IsSpringBoneEnabled; + set => m_fastSpringBoneBuffer.IsSpringBoneEnabled = value; + } + + public float DeltaTime => Time.deltaTime; + + public Vrm10FastSpringboneRuntimeStandalone() + { + m_fastSpringBoneScheduler = new(m_bufferCombiner); + } + + /// VRMの初期姿勢(T-Pose)状態。instanceがT-Poseから変化していても大丈夫 + public void Initialize(Vrm10Instance instance) + { + m_instance = instance; + + // NOTE: FastSpringBoneService は UnitTest などでは動作しない + if (Application.isPlaying) + { + ReconstructSpringBone(); + } + } + + public void Dispose() + { + m_bufferCombiner.Unregister(m_fastSpringBoneBuffer); + m_fastSpringBoneBuffer.Dispose(); + m_bufferCombiner.Dispose(); + } + + /// + /// このVRMに紐づくSpringBone関連のバッファを再構築する + /// ランタイム実行時にSpringBoneに対して変更を行いたいときは、このメソッドを明示的に呼ぶ必要がある + /// + public void ReconstructSpringBone() + { + // release + if (m_fastSpringBoneBuffer != null) + { + m_bufferCombiner.Unregister(m_fastSpringBoneBuffer); + m_fastSpringBoneBuffer.Dispose(); + } + + // 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)) + { + return defaultTransformState; + } + + Debug.LogWarning($"{tf.name} does not exist on load."); + return new TransformState(null); + } + + 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(); + } + } + + public void RestoreInitialTransform() + { + // Spring の joint に対応する transform の回転を初期状態 + for (int i = 0; i < m_fastSpringBoneBuffer.Transforms.Length; ++i) + { + var transform = m_fastSpringBoneBuffer.Transforms[i]; + transform.localRotation = m_initialLocalRotations[i]; + } + + // TODO: + } + + public void Process() + { + m_fastSpringBoneScheduler.Schedule(DeltaTime).Complete(); + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs.meta b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs.meta new file mode 100644 index 000000000..1ef24d6c7 --- /dev/null +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10FastSpringboneRuntimeStandalone.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2db989bb92a9a64b98516b30db3902a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs index 3f873e8cf..513668482 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/Vrm10Runtime.cs @@ -129,6 +129,9 @@ namespace UniVRM10 // 5. Apply Expression // LookAt の角度制限などはこちらで処理されます。 Expression.Process(eyeDirection); + + // 6. SpringBone + SpringBone.Process(); } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs index e1c1c2b49..e755f9442 100644 --- a/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs +++ b/Assets/VRM10/Runtime/IO/IVrm10SpringBoneRuntime.cs @@ -27,5 +27,9 @@ namespace UniVRM10 public Vector3 ExternalForce { get; set; } public bool IsSpringBoneEnabled { get; set; } + + public float DeltaTime { get; } + + public void Process(); } } \ No newline at end of file