using UnityEngine; namespace VRM.FastSpringBones.Blittables { /// /// VRMSpringBoneLogicをBlittableにしたもの /// ベルレ積分など、コアな計算を行う /// public unsafe struct BlittablePoint { private readonly float _length; private readonly Quaternion _localRotation; private readonly Vector3 _boneAxis; private readonly float _radius; private Vector3 _prevPosition; private readonly BlittableColliderGroups* _blittableColliderGroups; private readonly BlittableTransform* _center; public Vector3 CurrentPosition { get; private set; } private readonly BlittableTransform* _transform; public BlittablePoint( Transform transform, float radius, BlittableTransform* center, BlittableColliderGroups* blittableColliderGroups, BlittableTransform* blittableTransform) { Vector3 localPosition; if (transform.childCount == 0) { var delta = transform.position - transform.parent.position; var childPosition = transform.position + delta.normalized * 0.07f; localPosition = transform.worldToLocalMatrix.MultiplyPoint(childPosition); } else { var firstChild = transform.GetChild(0); var scale = firstChild.lossyScale; localPosition = firstChild.localPosition; localPosition.x *= scale.x; localPosition.y *= scale.y; localPosition.z *= scale.z; } var worldChildPosition = (Vector3)transform.TransformPoint(localPosition); _prevPosition = CurrentPosition = center != null ? center->LocalToWorld.inverse.MultiplyPoint3x4(worldChildPosition) : worldChildPosition; _localRotation = transform.localRotation; _boneAxis = localPosition.normalized; _length = localPosition.magnitude; _radius = radius; _blittableColliderGroups = blittableColliderGroups; _transform = blittableTransform; _center = center; } public void Update(float stiffnessForce, float dragForce, Vector3 external) { // 親のRotationが変わっている可能性があるので更新する _transform->UpdateLocalToWorldMatrix(); Vector3 currentPosition; Vector3 prevPosition; if (_center == null) { currentPosition = CurrentPosition; prevPosition = _prevPosition; } else { var centerLocalToWorld = _center->LocalToWorld; currentPosition = centerLocalToWorld.MultiplyPoint3x4(CurrentPosition); prevPosition = centerLocalToWorld.MultiplyPoint3x4(_prevPosition); } // verlet積分で次の位置を計算 var nextPosition = currentPosition + (currentPosition - prevPosition) * (1.0f - dragForce) // 前フレームの移動を継続する(減衰もあるよ) + _transform->ParentWorldRotation * _localRotation * _boneAxis * stiffnessForce // 親の回転による子ボーンの移動目標 + external; // 外力による移動量 // 長さをboneLengthに強制 var position = _transform->WorldPosition; nextPosition = position + (nextPosition - position).normalized * _length; nextPosition = Collision(nextPosition, position); if (_center == null) { _prevPosition = currentPosition; CurrentPosition = nextPosition; } else { var centerWorldToLocal = _center->LocalToWorld.inverse; _prevPosition = centerWorldToLocal.MultiplyPoint3x4(currentPosition); CurrentPosition = centerWorldToLocal.MultiplyPoint3x4(nextPosition); } //回転を適用 _transform->SetWorldRotation(ApplyRotation(nextPosition)); } private Vector3 Collision(Vector3 nextPosition, Vector3 position) { for (var i = 0; i < _blittableColliderGroups->Length; ++i) { var colliderGroup = (*_blittableColliderGroups)[i]; for (var j = 0; j < colliderGroup.Colliders.Count; ++j) { var collider = colliderGroup.Colliders[j]; var colliderPosition = colliderGroup.Transform->TransformPoint(collider.Offset); var r = _radius + collider.Radius; if (!((nextPosition - colliderPosition).sqrMagnitude <= (r * r))) continue; // ヒット。Colliderの半径方向に押し出す var normal = (nextPosition - colliderPosition).normalized; var posFromCollider = colliderPosition + normal * (_radius + collider.Radius); // 長さをboneLengthに強制 nextPosition = position + (posFromCollider - position).normalized * _length; } } return nextPosition; } private static Quaternion FromToRotation(Vector3 from, Vector3 to) => Quaternion.AngleAxis( angle: Mathf.Acos(Mathf.Clamp(Vector3.Dot(from.normalized, to.normalized), -1f, 1f)) * Mathf.Rad2Deg, axis: Vector3.Cross(from, to).normalized ); private Quaternion ApplyRotation(Vector3 nextTail) { var rotation = _transform->ParentWorldRotation * _localRotation; return FromToRotation( rotation * _boneAxis, nextTail - _transform->WorldPosition) * rotation; } } }