diff --git a/Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs b/Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs index a59c36e8c..880ebeb49 100644 --- a/Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs +++ b/Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs @@ -14,5 +14,20 @@ namespace VRM.SpringBone var scale = Mathf.Max(Mathf.Max(ls.x, ls.y), ls.z); Radius = scale * collider.Radius; } + + public Vector3 Collide(SpringBoneSettings settings, Transform m_transform, SpringBoneJointInit init, Vector3 nextTail) + { + var m_radius = settings.HitRadius * m_transform.UniformedLossyScale(); + var r = m_radius + Radius; + if (Vector3.SqrMagnitude(nextTail - Position) <= (r * r)) + { + // ヒット。Colliderの半径方向に押し出す + var normal = (nextTail - Position).normalized; + var posFromCollider = Position + normal * (m_radius + Radius); + // 長さをboneLengthに強制 + nextTail = m_transform.position + (posFromCollider - m_transform.position).normalized * init.Length; + } + return nextTail; + } } } \ No newline at end of file diff --git a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs index d4cad9ab3..59f8ce7e3 100644 --- a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs +++ b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs @@ -1,49 +1,57 @@ +using System.Collections.Generic; using UnityEngine; namespace VRM.SpringBone { - struct JointState + /// + /// original from + /// http://rocketjump.skr.jp/unity3d/109/ + /// + /// この型のフィールドはSpringBoneのライフサイクルを通じて不変。 + /// + struct SpringBoneJointInit { - public Vector3 CurrentTail; - public Vector3 PrevTail; + public Vector3 BoneAxis; + public float Length; + public Quaternion LocalRotation; - public static JointState Init(Transform center, Transform transform, Vector3 localChildPosition) + public Quaternion CalcRotation(Transform m_transform, Vector3 nextTail) { - var worldChildPosition = transform.TransformPoint(localChildPosition); - var tail = center != null - ? center.InverseTransformPoint(worldChildPosition) - : worldChildPosition; - return new JointState - { - CurrentTail = tail, - PrevTail = tail, - }; + var rotation = (m_transform.parent != null ? m_transform.parent.rotation : Quaternion.identity) * LocalRotation; + return Quaternion.FromToRotation(rotation * BoneAxis, + nextTail - m_transform.position) * rotation; } - public static JointState Make(Transform center, Vector3 currentTail, Vector3 nextTail) + public Vector3 CalcNextTail(float deltaTime, Transform center, Transform m_transform, + SpringBoneSettings settings, SpringBoneJointState _state) { - return new JointState - { - PrevTail = center != null - ? center.InverseTransformPoint(currentTail) - : currentTail, - CurrentTail = center != null - ? center.InverseTransformPoint(nextTail) - : nextTail, - }; + var state = _state.ToWorld(center); + + // verlet積分で次の位置を計算 + var nextTail = state.CurrentTail + + (state.CurrentTail - state.PrevTail) * (1.0f - settings.DragForce) // 前フレームの移動を継続する(減衰もあるよ) + + (m_transform.parent != null ? m_transform.parent.rotation : Quaternion.identity) * LocalRotation * BoneAxis * settings.StiffnessForce * deltaTime // 親の回転による子ボーンの移動目標 + + settings.GravityDir * (settings.GravityPower * deltaTime); // 外力による移動量 + + // 長さをboneLengthに強制 + var position = m_transform.position; + nextTail = position + (nextTail - position).normalized * Length; + return nextTail; } - public JointState ToWorld(Transform center) + public void DrawGizmo(Transform center, Transform m_transform, SpringBoneSettings settings, Color color, SpringBoneJointState m_state) { - return new JointState - { - CurrentTail = center != null - ? center.TransformPoint(CurrentTail) - : CurrentTail, - PrevTail = center != null - ? center.TransformPoint(PrevTail) - : PrevTail, - }; + var state = m_state.ToWorld(center); + var m_radius = settings.HitRadius * m_transform.UniformedLossyScale(); + + Gizmos.color = Color.gray; + Gizmos.DrawLine(state.CurrentTail, state.PrevTail); + Gizmos.DrawWireSphere(state.PrevTail, m_radius); + + Gizmos.color = color; + Gizmos.DrawLine(state.CurrentTail, m_transform.position); + Gizmos.DrawWireSphere(state.CurrentTail, m_radius); } + }; } \ No newline at end of file diff --git a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs.meta b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs.meta index b68add524..70dd7502d 100644 --- a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs.meta +++ b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointInit.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3bbcafd66c035184b8aa868b65c01c1d +guid: 2602939771f9eb2428b140e10fa899f2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs index 873bd7b22..c54b74a5a 100644 --- a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs +++ b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs @@ -1,80 +1,52 @@ -using System.Collections.Generic; using UnityEngine; namespace VRM.SpringBone { /// - /// original from - /// http://rocketjump.skr.jp/unity3d/109/ - /// - /// Joint 状態(初期・フレーム)を管理する + /// 毎フレーム更新される Verlet 積分の位置状態 /// - struct SpringBoneJointInit + struct SpringBoneJointState { - public Vector3 BoneAxis; - public float Length; - public Quaternion LocalRotation; + public Vector3 CurrentTail; + public Vector3 PrevTail; - public Quaternion CalcRotation(Transform m_transform, Vector3 nextTail) + public static SpringBoneJointState Init(Transform center, Transform transform, Vector3 localChildPosition) { - var rotation = (m_transform.parent != null ? m_transform.parent.rotation : Quaternion.identity) * LocalRotation; - return Quaternion.FromToRotation(rotation * BoneAxis, - nextTail - m_transform.position) * rotation; - } - - public JointState Update(float deltaTime, Transform center, Transform m_transform, - SpringBoneSettings settings, - List colliders, JointState _state) - { - var state = _state.ToWorld(center); - - // verlet積分で次の位置を計算 - var nextTail = state.CurrentTail - + (state.CurrentTail - state.PrevTail) * (1.0f - settings.DragForce) // 前フレームの移動を継続する(減衰もあるよ) - + (m_transform.parent != null ? m_transform.parent.rotation : Quaternion.identity) * LocalRotation * BoneAxis * settings.StiffnessForce * deltaTime // 親の回転による子ボーンの移動目標 - + settings.GravityDir * (settings.GravityPower * deltaTime); // 外力による移動量 - - // 長さをboneLengthに強制 - var position = m_transform.position; - nextTail = position + (nextTail - position).normalized * Length; - - // Collisionで移動 - nextTail = Collision(m_transform, settings, colliders, nextTail); - - return JointState.Make(center, currentTail: state.CurrentTail, nextTail: nextTail); - } - - Vector3 Collision(Transform m_transform, SpringBoneSettings settings, List colliders, Vector3 nextTail) - { - foreach (var collider in colliders) + var worldChildPosition = transform.TransformPoint(localChildPosition); + var tail = center != null + ? center.InverseTransformPoint(worldChildPosition) + : worldChildPosition; + return new SpringBoneJointState { - var m_radius = settings.HitRadius * m_transform.UniformedLossyScale(); - var r = m_radius + collider.Radius; - if (Vector3.SqrMagnitude(nextTail - collider.Position) <= (r * r)) - { - // ヒット。Colliderの半径方向に押し出す - var normal = (nextTail - collider.Position).normalized; - var posFromCollider = collider.Position + normal * (m_radius + collider.Radius); - // 長さをboneLengthに強制 - nextTail = m_transform.position + (posFromCollider - m_transform.position).normalized * Length; - } - } - return nextTail; + CurrentTail = tail, + PrevTail = tail, + }; } - public void DrawGizmo(Transform center, Transform m_transform, SpringBoneSettings settings, Color color, JointState m_state) + public static SpringBoneJointState Make(Transform center, Vector3 currentTail, Vector3 nextTail) { - var state = m_state.ToWorld(center); - var m_radius = settings.HitRadius * m_transform.UniformedLossyScale(); - - Gizmos.color = Color.gray; - Gizmos.DrawLine(state.CurrentTail, state.PrevTail); - Gizmos.DrawWireSphere(state.PrevTail, m_radius); - - Gizmos.color = color; - Gizmos.DrawLine(state.CurrentTail, m_transform.position); - Gizmos.DrawWireSphere(state.CurrentTail, m_radius); + return new SpringBoneJointState + { + PrevTail = center != null + ? center.InverseTransformPoint(currentTail) + : currentTail, + CurrentTail = center != null + ? center.InverseTransformPoint(nextTail) + : nextTail, + }; } + public SpringBoneJointState ToWorld(Transform center) + { + return new SpringBoneJointState + { + CurrentTail = center != null + ? center.TransformPoint(CurrentTail) + : CurrentTail, + PrevTail = center != null + ? center.TransformPoint(PrevTail) + : PrevTail, + }; + } }; } \ No newline at end of file diff --git a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs.meta b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs.meta index 70dd7502d..b68add524 100644 --- a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs.meta +++ b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJointState.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2602939771f9eb2428b140e10fa899f2 +guid: 3bbcafd66c035184b8aa868b65c01c1d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs index d7c6e17fe..84154bd18 100644 --- a/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs +++ b/Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs @@ -4,18 +4,10 @@ using UnityEngine; namespace VRM.SpringBone { - /// - /// 同じ設定のスプリングをまとめて処理する。 - /// - /// root o-o-o-x tail - /// - /// [vrm0] tail は 7cm 遠にダミーの joint があるようにふるまう。 - /// - /// class SpringBoneSystem { Dictionary m_initialLocalRotationMap; - List<(Transform, SpringBoneJointInit, JointState)> m_joints = new(); + List<(Transform, SpringBoneJointInit, SpringBoneJointState)> m_joints = new(); List m_colliders = new(); public void Setup(SceneInfo scene, bool force) @@ -79,7 +71,7 @@ namespace VRM.SpringBone BoneAxis = localChildPosition.normalized, Length = localChildPosition.magnitude, }, - JointState.Init(center, parent, localChildPosition))); + SpringBoneJointState.Init(center, parent, localChildPosition))); foreach (Transform child in parent) SetupRecursive(center, child); } @@ -95,6 +87,7 @@ namespace VRM.SpringBone Setup(scene, false); } + // collider の収集 m_colliders.Clear(); if (scene.ColliderGroups != null) { @@ -113,11 +106,21 @@ namespace VRM.SpringBone for (int i = 0; i < m_joints.Count; ++i) { var (transform, init, state) = m_joints[i]; - var nextState = init.Update(deltaTime, scene.Center, transform, settings, m_colliders, state); - m_joints[i] = (transform, init, nextState); + + // Spring処理 + var nextTail = init.CalcNextTail(deltaTime, scene.Center, transform, settings, state); + + // Collision + foreach (var collider in m_colliders) + { + nextTail = collider.Collide(settings, transform, init, nextTail); + } + + // 状態更新 + m_joints[i] = (transform, init, SpringBoneJointState.Make(scene.Center, currentTail: state.CurrentTail, nextTail: nextTail)); //回転を適用 - var r = init.CalcRotation(transform, nextState.CurrentTail); + var r = init.CalcRotation(transform, nextTail); transform.rotation = r; } }