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