mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-04-17 10:36:18 -05:00
回転・拡縮に対応するべく SpringBone joint の TRS を質点の位置に解決するように整理する
vrm0 > vrm0.fast > vrm1.fast の順に作業する。3つとも処理内容は同じ
This commit is contained in:
parent
5a664f18c3
commit
76850ba67c
8
Assets/VRM/Runtime/SpringBone/Logic.meta
Normal file
8
Assets/VRM/Runtime/SpringBone/Logic.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b3d9c467d3da3e4aa800f2a9229768c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/VRM/Runtime/SpringBone/Logic/SceneInfo.cs
Normal file
12
Assets/VRM/Runtime/SpringBone/Logic/SceneInfo.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM.SpringBone
|
||||
{
|
||||
struct SceneInfo
|
||||
{
|
||||
public IReadOnlyList<Transform> RootBones;
|
||||
public Transform m_center;
|
||||
public VRMSpringBoneColliderGroup[] ColliderGroups;
|
||||
}
|
||||
}
|
||||
11
Assets/VRM/Runtime/SpringBone/Logic/SceneInfo.cs.meta
Normal file
11
Assets/VRM/Runtime/SpringBone/Logic/SceneInfo.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1f9f3d46865885b4a8d559a5d962db5a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
18
Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs
Normal file
18
Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace VRM.SpringBone
|
||||
{
|
||||
struct SphereCollider
|
||||
{
|
||||
public readonly Vector3 Position;
|
||||
public readonly float Radius;
|
||||
|
||||
public SphereCollider(Transform transform, VRMSpringBoneColliderGroup.SphereCollider collider)
|
||||
{
|
||||
Position = transform.TransformPoint(collider.Offset);
|
||||
var ls = transform.lossyScale;
|
||||
var scale = Mathf.Max(Mathf.Max(ls.x, ls.y), ls.z);
|
||||
Radius = scale * collider.Radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs.meta
Normal file
11
Assets/VRM/Runtime/SpringBone/Logic/SphereCollider.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 102ef8d07b335ee42b6a198b5657ef80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
129
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJoint.cs
Normal file
129
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJoint.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM.SpringBone
|
||||
{
|
||||
/// <summary>
|
||||
/// original from
|
||||
/// http://rocketjump.skr.jp/unity3d/109/
|
||||
/// </summary>
|
||||
class SpringBoneJoint
|
||||
{
|
||||
Transform m_transform;
|
||||
public Transform Head => m_transform;
|
||||
|
||||
private Vector3 m_boneAxis;
|
||||
private Vector3 m_currentTail;
|
||||
|
||||
private readonly float m_length;
|
||||
private Vector3 m_localDir;
|
||||
private Vector3 m_prevTail;
|
||||
|
||||
public SpringBoneJoint(Transform center, Transform transform, Vector3 localChildPosition)
|
||||
{
|
||||
m_transform = transform;
|
||||
var worldChildPosition = m_transform.TransformPoint(localChildPosition);
|
||||
m_currentTail = center != null
|
||||
? center.InverseTransformPoint(worldChildPosition)
|
||||
: worldChildPosition;
|
||||
m_prevTail = m_currentTail;
|
||||
LocalRotation = transform.localRotation;
|
||||
m_boneAxis = localChildPosition.normalized;
|
||||
m_length = localChildPosition.magnitude;
|
||||
}
|
||||
|
||||
public Vector3 Tail => m_transform.localToWorldMatrix.MultiplyPoint(m_boneAxis * m_length);
|
||||
|
||||
private Quaternion LocalRotation { get; }
|
||||
|
||||
float m_radius;
|
||||
public void SetRadius(float radius)
|
||||
{
|
||||
m_radius = radius * m_transform.UniformedLossyScale();
|
||||
}
|
||||
|
||||
private Quaternion ParentRotation =>
|
||||
m_transform.parent != null
|
||||
? m_transform.parent.rotation
|
||||
: Quaternion.identity;
|
||||
|
||||
public void Update(Transform center,
|
||||
float stiffnessForce, float dragForce, Vector3 external,
|
||||
List<SphereCollider> colliders)
|
||||
{
|
||||
var currentTail = center != null
|
||||
? center.TransformPoint(m_currentTail)
|
||||
: m_currentTail;
|
||||
var prevTail = center != null
|
||||
? center.TransformPoint(m_prevTail)
|
||||
: m_prevTail;
|
||||
|
||||
// verlet積分で次の位置を計算
|
||||
var nextTail = currentTail
|
||||
+ (currentTail - prevTail) * (1.0f - dragForce) // 前フレームの移動を継続する(減衰もあるよ)
|
||||
+ ParentRotation * LocalRotation * m_boneAxis * stiffnessForce // 親の回転による子ボーンの移動目標
|
||||
+ external; // 外力による移動量
|
||||
|
||||
// 長さをboneLengthに強制
|
||||
var position = m_transform.position;
|
||||
nextTail = position + (nextTail - position).normalized * m_length;
|
||||
|
||||
// Collisionで移動
|
||||
nextTail = Collision(colliders, nextTail);
|
||||
|
||||
m_prevTail = center != null
|
||||
? center.InverseTransformPoint(currentTail)
|
||||
: currentTail;
|
||||
|
||||
m_currentTail = center != null
|
||||
? center.InverseTransformPoint(nextTail)
|
||||
: nextTail;
|
||||
|
||||
//回転を適用
|
||||
m_transform.rotation = ApplyRotation(nextTail);
|
||||
}
|
||||
|
||||
protected virtual Quaternion ApplyRotation(Vector3 nextTail)
|
||||
{
|
||||
var rotation = ParentRotation * LocalRotation;
|
||||
return Quaternion.FromToRotation(rotation * m_boneAxis,
|
||||
nextTail - m_transform.position) * rotation;
|
||||
}
|
||||
|
||||
protected virtual Vector3 Collision(List<SpringBone.SphereCollider> colliders, Vector3 nextTail)
|
||||
{
|
||||
foreach (var collider in colliders)
|
||||
{
|
||||
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 * m_length;
|
||||
}
|
||||
}
|
||||
return nextTail;
|
||||
}
|
||||
|
||||
public void DrawGizmo(Transform center, Color color)
|
||||
{
|
||||
var currentTail = center != null
|
||||
? center.TransformPoint(m_currentTail)
|
||||
: m_currentTail;
|
||||
var prevTail = center != null
|
||||
? center.TransformPoint(m_prevTail)
|
||||
: m_prevTail;
|
||||
|
||||
Gizmos.color = Color.gray;
|
||||
Gizmos.DrawLine(currentTail, prevTail);
|
||||
Gizmos.DrawWireSphere(prevTail, m_radius);
|
||||
|
||||
Gizmos.color = color;
|
||||
Gizmos.DrawLine(currentTail, m_transform.position);
|
||||
Gizmos.DrawWireSphere(currentTail, m_radius);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJoint.cs.meta
Normal file
11
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneJoint.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2602939771f9eb2428b140e10fa899f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSettings.cs
Normal file
13
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSettings.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace VRM.SpringBone
|
||||
{
|
||||
struct SpringBoneSettings
|
||||
{
|
||||
public float m_stiffnessForce;
|
||||
public Vector3 m_gravityDir;
|
||||
public float m_gravityPower;
|
||||
public float m_hitRadius;
|
||||
public float m_dragForce;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c979a822d9cd3754db84ce10c42f3b56
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
158
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs
Normal file
158
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM.SpringBone
|
||||
{
|
||||
/// <summary>
|
||||
/// 同じ設定のスプリングをまとめて処理する。
|
||||
///
|
||||
/// root o-o-o-x tail
|
||||
///
|
||||
/// [vrm0] tail は 7cm 遠にダミーの joint があるようにふるまう。
|
||||
///
|
||||
/// </summary>
|
||||
class SpringBoneSystem
|
||||
{
|
||||
Dictionary<Transform, Quaternion> m_initialLocalRotationMap;
|
||||
List<SpringBoneJoint> m_verlet = new();
|
||||
List<SphereCollider> m_colliders = new();
|
||||
|
||||
public void SetLocalRotationsIdentity()
|
||||
{
|
||||
foreach (var verlet in m_verlet) verlet.Head.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
public void Setup(SceneInfo scene, bool force)
|
||||
{
|
||||
if (force || m_initialLocalRotationMap == null)
|
||||
{
|
||||
m_initialLocalRotationMap = new Dictionary<Transform, Quaternion>();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var kv in m_initialLocalRotationMap) kv.Key.localRotation = kv.Value;
|
||||
m_initialLocalRotationMap.Clear();
|
||||
}
|
||||
m_verlet.Clear();
|
||||
|
||||
foreach (var go in scene.RootBones)
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
foreach (var x in go.transform.GetComponentsInChildren<Transform>(true)) m_initialLocalRotationMap[x] = x.localRotation;
|
||||
|
||||
SetupRecursive(scene.m_center, go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Transform> GetChildren(Transform parent)
|
||||
{
|
||||
for (var i = 0; i < parent.childCount; ++i) yield return parent.GetChild(i);
|
||||
}
|
||||
|
||||
private void SetupRecursive(Transform center, Transform parent)
|
||||
{
|
||||
Vector3 localPosition = default;
|
||||
Vector3 scale = default;
|
||||
if (parent.childCount == 0)
|
||||
{
|
||||
// 子ノードが無い。7cm 固定
|
||||
var delta = parent.position - parent.parent.position;
|
||||
var childPosition = parent.position + delta.normalized * 0.07f * parent.UniformedLossyScale();
|
||||
localPosition = parent.worldToLocalMatrix.MultiplyPoint(childPosition); // cancel scale
|
||||
scale = parent.lossyScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstChild = GetChildren(parent).First();
|
||||
localPosition = firstChild.localPosition;
|
||||
scale = firstChild.lossyScale;
|
||||
}
|
||||
m_verlet.Add(new SpringBone.SpringBoneJoint(center, parent,
|
||||
new Vector3(
|
||||
localPosition.x * scale.x,
|
||||
localPosition.y * scale.y,
|
||||
localPosition.z * scale.z
|
||||
)))
|
||||
;
|
||||
|
||||
foreach (Transform child in parent) SetupRecursive(center, child);
|
||||
}
|
||||
|
||||
|
||||
public void UpdateProcess(float deltaTime,
|
||||
SceneInfo scene,
|
||||
SpringBoneSettings settings
|
||||
)
|
||||
{
|
||||
if (m_verlet == null || m_verlet.Count == 0)
|
||||
{
|
||||
if (scene.RootBones == null) return;
|
||||
Setup(scene, false);
|
||||
}
|
||||
|
||||
m_colliders.Clear();
|
||||
if (scene.ColliderGroups != null)
|
||||
{
|
||||
foreach (var group in scene.ColliderGroups)
|
||||
{
|
||||
if (group != null)
|
||||
{
|
||||
foreach (var collider in group.Colliders)
|
||||
{
|
||||
m_colliders.Add(new SphereCollider(group.transform, collider));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stiffness = settings.m_stiffnessForce * deltaTime;
|
||||
var external = settings.m_gravityDir * (settings.m_gravityPower * deltaTime);
|
||||
|
||||
foreach (var verlet in m_verlet)
|
||||
{
|
||||
verlet.SetRadius(settings.m_hitRadius);
|
||||
verlet.Update(scene.m_center,
|
||||
stiffness,
|
||||
settings.m_dragForce,
|
||||
external,
|
||||
m_colliders
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayingGizmo(Transform m_center, Color m_gizmoColor)
|
||||
{
|
||||
foreach (var verlet in m_verlet)
|
||||
{
|
||||
verlet.DrawGizmo(m_center, m_gizmoColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void EditorGizmo(Transform head, float m_hitRadius)
|
||||
{
|
||||
Vector3 childPosition;
|
||||
Vector3 scale;
|
||||
if (head.childCount == 0)
|
||||
{
|
||||
// 子ノードが無い。7cm 固定
|
||||
var delta = head.position - head.parent.position;
|
||||
childPosition = head.position + delta.normalized * 0.07f * head.UniformedLossyScale();
|
||||
scale = head.lossyScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstChild = GetChildren(head).First();
|
||||
childPosition = firstChild.position;
|
||||
scale = firstChild.lossyScale;
|
||||
}
|
||||
|
||||
Gizmos.DrawLine(head.position, childPosition);
|
||||
Gizmos.DrawWireSphere(childPosition, m_hitRadius * scale.x);
|
||||
|
||||
foreach (Transform child in head) EditorGizmo(child, m_hitRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs.meta
Normal file
11
Assets/VRM/Runtime/SpringBone/Logic/SpringBoneSystem.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9cc72abc9e20895419ca606484cc7dc0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UniGLTF;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
|
|
@ -13,30 +11,16 @@ namespace VRM
|
|||
// [RequireComponent(typeof(VCIObject))]
|
||||
public sealed class VRMSpringBone : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
public string m_comment;
|
||||
|
||||
[SerializeField] public string m_comment;
|
||||
[SerializeField] private Color m_gizmoColor = Color.yellow;
|
||||
|
||||
[SerializeField]
|
||||
public float m_stiffnessForce = 1.0f;
|
||||
|
||||
[SerializeField] public float m_stiffnessForce = 1.0f;
|
||||
[SerializeField] public float m_gravityPower;
|
||||
|
||||
[SerializeField] public Vector3 m_gravityDir = new Vector3(0, -1.0f, 0);
|
||||
|
||||
[SerializeField][Range(0, 1)] public float m_dragForce = 0.4f;
|
||||
|
||||
[SerializeField] public Transform m_center;
|
||||
|
||||
[SerializeField] public List<Transform> RootBones = new List<Transform>();
|
||||
Dictionary<Transform, Quaternion> m_initialLocalRotationMap;
|
||||
|
||||
[SerializeField]
|
||||
public float m_hitRadius = 0.02f;
|
||||
|
||||
[SerializeField]
|
||||
public VRMSpringBoneColliderGroup[] ColliderGroups;
|
||||
[SerializeField] public float m_hitRadius = 0.02f;
|
||||
[SerializeField] public VRMSpringBoneColliderGroup[] ColliderGroups;
|
||||
|
||||
public enum SpringBoneUpdateType
|
||||
{
|
||||
|
|
@ -44,211 +28,39 @@ namespace VRM
|
|||
FixedUpdate,
|
||||
Manual,
|
||||
}
|
||||
[SerializeField]
|
||||
public SpringBoneUpdateType m_updateType = SpringBoneUpdateType.LateUpdate;
|
||||
[SerializeField] public SpringBoneUpdateType m_updateType = SpringBoneUpdateType.LateUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// original from
|
||||
/// http://rocketjump.skr.jp/unity3d/109/
|
||||
/// </summary>
|
||||
private class VRMSpringBoneLogic
|
||||
{
|
||||
Transform m_transform;
|
||||
public Transform Head => m_transform;
|
||||
|
||||
private Vector3 m_boneAxis;
|
||||
private Vector3 m_currentTail;
|
||||
|
||||
private readonly float m_length;
|
||||
private Vector3 m_localDir;
|
||||
private Vector3 m_prevTail;
|
||||
|
||||
public VRMSpringBoneLogic(Transform center, Transform transform, Vector3 localChildPosition)
|
||||
{
|
||||
m_transform = transform;
|
||||
var worldChildPosition = m_transform.TransformPoint(localChildPosition);
|
||||
m_currentTail = center != null
|
||||
? center.InverseTransformPoint(worldChildPosition)
|
||||
: worldChildPosition;
|
||||
m_prevTail = m_currentTail;
|
||||
LocalRotation = transform.localRotation;
|
||||
m_boneAxis = localChildPosition.normalized;
|
||||
m_length = localChildPosition.magnitude;
|
||||
}
|
||||
|
||||
public Vector3 Tail => m_transform.localToWorldMatrix.MultiplyPoint(m_boneAxis * m_length);
|
||||
|
||||
private Quaternion LocalRotation { get; }
|
||||
|
||||
float m_radius;
|
||||
public void SetRadius(float radius)
|
||||
{
|
||||
m_radius = radius * m_transform.UniformedLossyScale();
|
||||
}
|
||||
|
||||
private Quaternion ParentRotation =>
|
||||
m_transform.parent != null
|
||||
? m_transform.parent.rotation
|
||||
: Quaternion.identity;
|
||||
|
||||
public void Update(Transform center,
|
||||
float stiffnessForce, float dragForce, Vector3 external,
|
||||
List<SphereCollider> colliders)
|
||||
{
|
||||
var currentTail = center != null
|
||||
? center.TransformPoint(m_currentTail)
|
||||
: m_currentTail;
|
||||
var prevTail = center != null
|
||||
? center.TransformPoint(m_prevTail)
|
||||
: m_prevTail;
|
||||
|
||||
// verlet積分で次の位置を計算
|
||||
var nextTail = currentTail
|
||||
+ (currentTail - prevTail) * (1.0f - dragForce) // 前フレームの移動を継続する(減衰もあるよ)
|
||||
+ ParentRotation * LocalRotation * m_boneAxis * stiffnessForce // 親の回転による子ボーンの移動目標
|
||||
+ external; // 外力による移動量
|
||||
|
||||
// 長さをboneLengthに強制
|
||||
var position = m_transform.position;
|
||||
nextTail = position + (nextTail - position).normalized * m_length;
|
||||
|
||||
// Collisionで移動
|
||||
nextTail = Collision(colliders, nextTail);
|
||||
|
||||
m_prevTail = center != null
|
||||
? center.InverseTransformPoint(currentTail)
|
||||
: currentTail;
|
||||
|
||||
m_currentTail = center != null
|
||||
? center.InverseTransformPoint(nextTail)
|
||||
: nextTail;
|
||||
|
||||
//回転を適用
|
||||
m_transform.rotation = ApplyRotation(nextTail);
|
||||
}
|
||||
|
||||
protected virtual Quaternion ApplyRotation(Vector3 nextTail)
|
||||
{
|
||||
var rotation = ParentRotation * LocalRotation;
|
||||
return Quaternion.FromToRotation(rotation * m_boneAxis,
|
||||
nextTail - m_transform.position) * rotation;
|
||||
}
|
||||
|
||||
protected virtual Vector3 Collision(List<SphereCollider> colliders, Vector3 nextTail)
|
||||
{
|
||||
foreach (var collider in colliders)
|
||||
{
|
||||
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 * m_length;
|
||||
}
|
||||
}
|
||||
return nextTail;
|
||||
}
|
||||
|
||||
public void DrawGizmo(Transform center, Color color)
|
||||
{
|
||||
var currentTail = center != null
|
||||
? center.TransformPoint(m_currentTail)
|
||||
: m_currentTail;
|
||||
var prevTail = center != null
|
||||
? center.TransformPoint(m_prevTail)
|
||||
: m_prevTail;
|
||||
|
||||
Gizmos.color = Color.gray;
|
||||
Gizmos.DrawLine(currentTail, prevTail);
|
||||
Gizmos.DrawWireSphere(prevTail, m_radius);
|
||||
|
||||
Gizmos.color = color;
|
||||
Gizmos.DrawLine(currentTail, m_transform.position);
|
||||
Gizmos.DrawWireSphere(currentTail, m_radius);
|
||||
}
|
||||
}
|
||||
|
||||
List<VRMSpringBoneLogic> m_verlet = new List<VRMSpringBoneLogic>();
|
||||
SpringBone.SpringBoneSystem m_system = new();
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Setup();
|
||||
}
|
||||
|
||||
SpringBone.SceneInfo Scene => new SpringBone.SceneInfo { RootBones = RootBones, m_center = m_center };
|
||||
SpringBone.SpringBoneSettings Settings => new SpringBone.SpringBoneSettings
|
||||
{
|
||||
m_stiffnessForce = m_stiffnessForce,
|
||||
m_gravityDir = m_gravityDir,
|
||||
m_gravityPower = m_gravityPower,
|
||||
m_hitRadius = m_hitRadius,
|
||||
m_dragForce = m_dragForce,
|
||||
};
|
||||
|
||||
[ContextMenu("Reset bones")]
|
||||
public void Setup(bool force = false)
|
||||
{
|
||||
if (RootBones != null)
|
||||
{
|
||||
if (force || m_initialLocalRotationMap == null)
|
||||
{
|
||||
m_initialLocalRotationMap = new Dictionary<Transform, Quaternion>();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var kv in m_initialLocalRotationMap) kv.Key.localRotation = kv.Value;
|
||||
m_initialLocalRotationMap.Clear();
|
||||
}
|
||||
m_verlet.Clear();
|
||||
|
||||
foreach (var go in RootBones)
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
foreach (var x in go.transform.GetComponentsInChildren<Transform>(true)) m_initialLocalRotationMap[x] = x.localRotation;
|
||||
|
||||
SetupRecursive(m_center, go);
|
||||
}
|
||||
}
|
||||
m_system.Setup(Scene, force);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLocalRotationsIdentity()
|
||||
{
|
||||
foreach (var verlet in m_verlet) verlet.Head.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
private static IEnumerable<Transform> GetChildren(Transform parent)
|
||||
{
|
||||
for (var i = 0; i < parent.childCount; ++i) yield return parent.GetChild(i);
|
||||
}
|
||||
|
||||
private void SetupRecursive(Transform center, Transform parent)
|
||||
{
|
||||
Vector3 localPosition = default;
|
||||
Vector3 scale = default;
|
||||
if (parent.childCount == 0)
|
||||
{
|
||||
// 子ノードが無い。7cm 固定
|
||||
var delta = parent.position - parent.parent.position;
|
||||
var childPosition = parent.position + delta.normalized * 0.07f * parent.UniformedLossyScale();
|
||||
localPosition = parent.worldToLocalMatrix.MultiplyPoint(childPosition); // cancel scale
|
||||
scale = parent.lossyScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstChild = GetChildren(parent).First();
|
||||
localPosition = firstChild.localPosition;
|
||||
scale = firstChild.lossyScale;
|
||||
}
|
||||
m_verlet.Add(new VRMSpringBoneLogic(center, parent,
|
||||
new Vector3(
|
||||
localPosition.x * scale.x,
|
||||
localPosition.y * scale.y,
|
||||
localPosition.z * scale.z
|
||||
)))
|
||||
;
|
||||
|
||||
foreach (Transform child in parent) SetupRecursive(center, child);
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (m_updateType == SpringBoneUpdateType.LateUpdate)
|
||||
{
|
||||
UpdateProcess(Time.deltaTime);
|
||||
m_system.UpdateProcess(Time.deltaTime, Scene, Settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +68,7 @@ namespace VRM
|
|||
{
|
||||
if (m_updateType == SpringBoneUpdateType.FixedUpdate)
|
||||
{
|
||||
UpdateProcess(Time.fixedDeltaTime);
|
||||
m_system.UpdateProcess(Time.fixedDeltaTime, Scene, Settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,96 +78,14 @@ namespace VRM
|
|||
{
|
||||
throw new System.ArgumentException("require SpringBoneUpdateType.Manual");
|
||||
}
|
||||
UpdateProcess(deltaTime);
|
||||
}
|
||||
|
||||
public struct SphereCollider
|
||||
{
|
||||
// public Transform Transform;
|
||||
public readonly Vector3 Position;
|
||||
public readonly float Radius;
|
||||
|
||||
public SphereCollider(Transform transform, VRMSpringBoneColliderGroup.SphereCollider collider)
|
||||
{
|
||||
Position = transform.TransformPoint(collider.Offset);
|
||||
var ls = transform.lossyScale;
|
||||
var scale = Mathf.Max(Mathf.Max(ls.x, ls.y), ls.z);
|
||||
Radius = scale * collider.Radius;
|
||||
}
|
||||
}
|
||||
|
||||
private List<SphereCollider> m_colliders = new List<SphereCollider>();
|
||||
private void UpdateProcess(float deltaTime)
|
||||
{
|
||||
if (m_verlet == null || m_verlet.Count == 0)
|
||||
{
|
||||
if (RootBones == null) return;
|
||||
|
||||
Setup();
|
||||
}
|
||||
|
||||
m_colliders.Clear();
|
||||
if (ColliderGroups != null)
|
||||
{
|
||||
foreach (var group in ColliderGroups)
|
||||
{
|
||||
if (group != null)
|
||||
{
|
||||
foreach (var collider in group.Colliders)
|
||||
{
|
||||
m_colliders.Add(new SphereCollider(group.transform, collider));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stiffness = m_stiffnessForce * deltaTime;
|
||||
var external = m_gravityDir * (m_gravityPower * deltaTime);
|
||||
|
||||
foreach (var verlet in m_verlet)
|
||||
{
|
||||
verlet.SetRadius(m_hitRadius);
|
||||
verlet.Update(m_center,
|
||||
stiffness,
|
||||
m_dragForce,
|
||||
external,
|
||||
m_colliders
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorGizmo(Transform head)
|
||||
{
|
||||
Vector3 childPosition;
|
||||
Vector3 scale;
|
||||
if (head.childCount == 0)
|
||||
{
|
||||
// 子ノードが無い。7cm 固定
|
||||
var delta = head.position - head.parent.position;
|
||||
childPosition = head.position + delta.normalized * 0.07f * head.UniformedLossyScale();
|
||||
scale = head.lossyScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstChild = GetChildren(head).First();
|
||||
childPosition = firstChild.position;
|
||||
scale = firstChild.lossyScale;
|
||||
}
|
||||
|
||||
Gizmos.DrawLine(head.position, childPosition);
|
||||
Gizmos.DrawWireSphere(childPosition, m_hitRadius * scale.x);
|
||||
|
||||
foreach (Transform child in head) EditorGizmo(child);
|
||||
m_system.UpdateProcess(deltaTime, Scene, Settings);
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
foreach (var verlet in m_verlet)
|
||||
{
|
||||
verlet.DrawGizmo(m_center, m_gizmoColor);
|
||||
}
|
||||
m_system.PlayingGizmo(m_center, m_gizmoColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -365,7 +95,7 @@ namespace VRM
|
|||
{
|
||||
if (root != null)
|
||||
{
|
||||
EditorGizmo(root.transform);
|
||||
m_system.EditorGizmo(root.transform, m_hitRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user