218 lines
5.7 KiB
C#
218 lines
5.7 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
namespace RootMotion.FinalIK
|
|
{
|
|
[Serializable]
|
|
public class IKSolverAim : IKSolverHeuristic
|
|
{
|
|
public Transform transform;
|
|
|
|
public Vector3 axis = Vector3.forward;
|
|
|
|
public Vector3 poleAxis = Vector3.up;
|
|
|
|
public Vector3 polePosition;
|
|
|
|
[Range(0f, 1f)]
|
|
public float poleWeight;
|
|
|
|
public Transform poleTarget;
|
|
|
|
[Range(0f, 1f)]
|
|
public float clampWeight = 0.1f;
|
|
|
|
[Range(0f, 2f)]
|
|
public int clampSmoothing = 2;
|
|
|
|
public IterationDelegate OnPreIteration;
|
|
|
|
private float step;
|
|
|
|
private Vector3 clampedIKPosition;
|
|
|
|
private RotationLimit transformLimit;
|
|
|
|
private Transform lastTransform;
|
|
|
|
public Vector3 transformAxis => transform.rotation * axis;
|
|
|
|
public Vector3 transformPoleAxis => transform.rotation * poleAxis;
|
|
|
|
protected override int minBones => 1;
|
|
|
|
protected override Vector3 localDirection => bones[0].transform.InverseTransformDirection(bones[bones.Length - 1].transform.forward);
|
|
|
|
public float GetAngle()
|
|
{
|
|
return Vector3.Angle(transformAxis, IKPosition - transform.position);
|
|
}
|
|
|
|
protected override void OnInitiate()
|
|
{
|
|
if ((firstInitiation || !Application.isPlaying) && transform != null)
|
|
{
|
|
IKPosition = transform.position + transformAxis * 3f;
|
|
polePosition = transform.position + transformPoleAxis * 3f;
|
|
}
|
|
for (int i = 0; i < bones.Length; i++)
|
|
{
|
|
if (bones[i].rotationLimit != null)
|
|
{
|
|
bones[i].rotationLimit.Disable();
|
|
}
|
|
}
|
|
step = 1f / (float)bones.Length;
|
|
if (Application.isPlaying)
|
|
{
|
|
axis = axis.normalized;
|
|
}
|
|
}
|
|
|
|
protected override void OnUpdate()
|
|
{
|
|
if (axis == Vector3.zero)
|
|
{
|
|
if (!Warning.logged)
|
|
{
|
|
LogWarning("IKSolverAim axis is Vector3.zero.");
|
|
}
|
|
return;
|
|
}
|
|
if (poleAxis == Vector3.zero && poleWeight > 0f)
|
|
{
|
|
if (!Warning.logged)
|
|
{
|
|
LogWarning("IKSolverAim poleAxis is Vector3.zero.");
|
|
}
|
|
return;
|
|
}
|
|
if (target != null)
|
|
{
|
|
IKPosition = target.position;
|
|
}
|
|
if (poleTarget != null)
|
|
{
|
|
polePosition = poleTarget.position;
|
|
}
|
|
if (XY)
|
|
{
|
|
IKPosition.z = bones[0].transform.position.z;
|
|
}
|
|
if (IKPositionWeight <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
|
|
if (transform != lastTransform)
|
|
{
|
|
transformLimit = transform.GetComponent<RotationLimit>();
|
|
if (transformLimit != null)
|
|
{
|
|
transformLimit.enabled = false;
|
|
}
|
|
lastTransform = transform;
|
|
}
|
|
if (transformLimit != null)
|
|
{
|
|
transformLimit.Apply();
|
|
}
|
|
if (transform == null)
|
|
{
|
|
if (!Warning.logged)
|
|
{
|
|
LogWarning("Aim Transform unassigned in Aim IK solver. Please Assign a Transform (lineal descendant to the last bone in the spine) that you want to be aimed at IKPosition");
|
|
}
|
|
return;
|
|
}
|
|
clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
|
|
clampedIKPosition = GetClampedIKPosition();
|
|
Vector3 b = clampedIKPosition - transform.position;
|
|
b = Vector3.Slerp(transformAxis * b.magnitude, b, IKPositionWeight);
|
|
clampedIKPosition = transform.position + b;
|
|
for (int i = 0; i < maxIterations && (i < 1 || !(tolerance > 0f) || !(GetAngle() < tolerance)); i++)
|
|
{
|
|
lastLocalDirection = localDirection;
|
|
if (OnPreIteration != null)
|
|
{
|
|
OnPreIteration(i);
|
|
}
|
|
Solve();
|
|
}
|
|
lastLocalDirection = localDirection;
|
|
}
|
|
|
|
private void Solve()
|
|
{
|
|
for (int i = 0; i < bones.Length - 1; i++)
|
|
{
|
|
RotateToTarget(clampedIKPosition, bones[i], step * (float)(i + 1) * IKPositionWeight * bones[i].weight);
|
|
}
|
|
RotateToTarget(clampedIKPosition, bones[bones.Length - 1], IKPositionWeight * bones[bones.Length - 1].weight);
|
|
}
|
|
|
|
private Vector3 GetClampedIKPosition()
|
|
{
|
|
if (clampWeight <= 0f)
|
|
{
|
|
return IKPosition;
|
|
}
|
|
if (clampWeight >= 1f)
|
|
{
|
|
return transform.position + transformAxis * (IKPosition - transform.position).magnitude;
|
|
}
|
|
float num = Vector3.Angle(transformAxis, IKPosition - transform.position);
|
|
float num2 = 1f - num / 180f;
|
|
float num3 = ((clampWeight > 0f) ? Mathf.Clamp(1f - (clampWeight - num2) / (1f - num2), 0f, 1f) : 1f);
|
|
float num4 = ((clampWeight > 0f) ? Mathf.Clamp(num2 / clampWeight, 0f, 1f) : 1f);
|
|
for (int i = 0; i < clampSmoothing; i++)
|
|
{
|
|
num4 = Mathf.Sin(num4 * MathF.PI * 0.5f);
|
|
}
|
|
return transform.position + Vector3.Slerp(transformAxis * 10f, IKPosition - transform.position, num4 * num3);
|
|
}
|
|
|
|
private void RotateToTarget(Vector3 targetPosition, Bone bone, float weight)
|
|
{
|
|
if (XY)
|
|
{
|
|
if (weight >= 0f)
|
|
{
|
|
Vector3 vector = transformAxis;
|
|
Vector3 vector2 = targetPosition - transform.position;
|
|
float current = Mathf.Atan2(vector.x, vector.y) * 57.29578f;
|
|
float num = Mathf.Atan2(vector2.x, vector2.y) * 57.29578f;
|
|
bone.transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(current, num), Vector3.back) * bone.transform.rotation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (weight >= 0f)
|
|
{
|
|
Quaternion quaternion = Quaternion.FromToRotation(transformAxis, targetPosition - transform.position);
|
|
if (weight >= 1f)
|
|
{
|
|
bone.transform.rotation = quaternion * bone.transform.rotation;
|
|
}
|
|
else
|
|
{
|
|
bone.transform.rotation = Quaternion.Lerp(Quaternion.identity, quaternion, weight) * bone.transform.rotation;
|
|
}
|
|
}
|
|
if (poleWeight > 0f)
|
|
{
|
|
Vector3 tangent = polePosition - transform.position;
|
|
Vector3 normal = transformAxis;
|
|
Vector3.OrthoNormalize(ref normal, ref tangent);
|
|
Quaternion b = Quaternion.FromToRotation(transformPoleAxis, tangent);
|
|
bone.transform.rotation = Quaternion.Lerp(Quaternion.identity, b, weight * poleWeight) * bone.transform.rotation;
|
|
}
|
|
}
|
|
if (useRotationLimits && bone.rotationLimit != null)
|
|
{
|
|
bone.rotationLimit.Apply();
|
|
}
|
|
}
|
|
}
|
|
}
|