150 lines
5.9 KiB
C#
150 lines
5.9 KiB
C#
using UnityEngine;
|
|
|
|
namespace VRM.FastSpringBones.Blittables
|
|
{
|
|
/// <summary>
|
|
/// VRMSpringBoneLogicをBlittableにしたもの
|
|
/// ベルレ積分など、コアな計算を行う
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|