using System.Collections.Generic; using System.Linq; using UnityEngine; namespace VRM.SpringBone { /// /// 同じ設定のスプリングをまとめて処理する。 /// /// root o-o-o-x tail /// /// [vrm0] tail は 7cm 遠にダミーの joint があるようにふるまう。 /// /// class SpringBoneSystem { Dictionary m_initialLocalRotationMap; List m_verlet = new(); List 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(); } 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(true)) m_initialLocalRotationMap[x] = x.localRotation; SetupRecursive(scene.m_center, go); } } } private static IEnumerable 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); } } }