386 lines
7.7 KiB
C#
386 lines
7.7 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
namespace RootMotion.FinalIK
|
|
{
|
|
[Serializable]
|
|
public abstract class IKSolver
|
|
{
|
|
[Serializable]
|
|
public class Point
|
|
{
|
|
public Transform transform;
|
|
|
|
[Range(0f, 1f)]
|
|
public float weight = 1f;
|
|
|
|
public Vector3 solverPosition;
|
|
|
|
public Quaternion solverRotation = Quaternion.identity;
|
|
|
|
public Vector3 defaultLocalPosition;
|
|
|
|
public Quaternion defaultLocalRotation;
|
|
|
|
public void StoreDefaultLocalState()
|
|
{
|
|
defaultLocalPosition = transform.localPosition;
|
|
defaultLocalRotation = transform.localRotation;
|
|
}
|
|
|
|
public void FixTransform()
|
|
{
|
|
if (transform.localPosition != defaultLocalPosition)
|
|
{
|
|
transform.localPosition = defaultLocalPosition;
|
|
}
|
|
if (transform.localRotation != defaultLocalRotation)
|
|
{
|
|
transform.localRotation = defaultLocalRotation;
|
|
}
|
|
}
|
|
|
|
public void UpdateSolverPosition()
|
|
{
|
|
solverPosition = transform.position;
|
|
}
|
|
|
|
public void UpdateSolverLocalPosition()
|
|
{
|
|
solverPosition = transform.localPosition;
|
|
}
|
|
|
|
public void UpdateSolverState()
|
|
{
|
|
solverPosition = transform.position;
|
|
solverRotation = transform.rotation;
|
|
}
|
|
|
|
public void UpdateSolverLocalState()
|
|
{
|
|
solverPosition = transform.localPosition;
|
|
solverRotation = transform.localRotation;
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class Bone : Point
|
|
{
|
|
public float length;
|
|
|
|
public float sqrMag;
|
|
|
|
public Vector3 axis = -Vector3.right;
|
|
|
|
private RotationLimit _rotationLimit;
|
|
|
|
private bool isLimited = true;
|
|
|
|
public RotationLimit rotationLimit
|
|
{
|
|
get
|
|
{
|
|
if (!isLimited)
|
|
{
|
|
return null;
|
|
}
|
|
if (_rotationLimit == null)
|
|
{
|
|
_rotationLimit = transform.GetComponent<RotationLimit>();
|
|
}
|
|
isLimited = _rotationLimit != null;
|
|
return _rotationLimit;
|
|
}
|
|
set
|
|
{
|
|
_rotationLimit = value;
|
|
isLimited = value != null;
|
|
}
|
|
}
|
|
|
|
public void Swing(Vector3 swingTarget, float weight = 1f)
|
|
{
|
|
if (!(weight <= 0f))
|
|
{
|
|
Quaternion quaternion = Quaternion.FromToRotation(transform.rotation * axis, swingTarget - transform.position);
|
|
if (weight >= 1f)
|
|
{
|
|
transform.rotation = quaternion * transform.rotation;
|
|
}
|
|
else
|
|
{
|
|
transform.rotation = Quaternion.Lerp(Quaternion.identity, quaternion, weight) * transform.rotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void SolverSwing(Bone[] bones, int index, Vector3 swingTarget, float weight = 1f)
|
|
{
|
|
if (weight <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
Quaternion quaternion = Quaternion.FromToRotation(bones[index].solverRotation * bones[index].axis, swingTarget - bones[index].solverPosition);
|
|
if (weight >= 1f)
|
|
{
|
|
for (int i = index; i < bones.Length; i++)
|
|
{
|
|
bones[i].solverRotation = quaternion * bones[i].solverRotation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = index; j < bones.Length; j++)
|
|
{
|
|
bones[j].solverRotation = Quaternion.Lerp(Quaternion.identity, quaternion, weight) * bones[j].solverRotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Swing2D(Vector3 swingTarget, float weight = 1f)
|
|
{
|
|
if (!(weight <= 0f))
|
|
{
|
|
Vector3 vector = transform.rotation * axis;
|
|
Vector3 vector2 = swingTarget - transform.position;
|
|
float current = Mathf.Atan2(vector.x, vector.y) * 57.29578f;
|
|
float target = Mathf.Atan2(vector2.x, vector2.y) * 57.29578f;
|
|
transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(current, target) * weight, Vector3.back) * transform.rotation;
|
|
}
|
|
}
|
|
|
|
public void SetToSolverPosition()
|
|
{
|
|
transform.position = solverPosition;
|
|
}
|
|
|
|
public Bone()
|
|
{
|
|
}
|
|
|
|
public Bone(Transform transform)
|
|
{
|
|
base.transform = transform;
|
|
}
|
|
|
|
public Bone(Transform transform, float weight)
|
|
{
|
|
base.transform = transform;
|
|
base.weight = weight;
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class Node : Point
|
|
{
|
|
public float length;
|
|
|
|
public float effectorPositionWeight;
|
|
|
|
public float effectorRotationWeight;
|
|
|
|
public Vector3 offset;
|
|
|
|
public Node()
|
|
{
|
|
}
|
|
|
|
public Node(Transform transform)
|
|
{
|
|
base.transform = transform;
|
|
}
|
|
|
|
public Node(Transform transform, float weight)
|
|
{
|
|
base.transform = transform;
|
|
base.weight = weight;
|
|
}
|
|
}
|
|
|
|
public delegate void UpdateDelegate();
|
|
|
|
public delegate void IterationDelegate(int i);
|
|
|
|
[HideInInspector]
|
|
public bool executedInEditor;
|
|
|
|
[HideInInspector]
|
|
public Vector3 IKPosition;
|
|
|
|
[Tooltip("The positional or the master weight of the solver.")]
|
|
[Range(0f, 1f)]
|
|
public float IKPositionWeight = 1f;
|
|
|
|
public UpdateDelegate OnPreInitiate;
|
|
|
|
public UpdateDelegate OnPostInitiate;
|
|
|
|
public UpdateDelegate OnPreUpdate;
|
|
|
|
public UpdateDelegate OnPostUpdate;
|
|
|
|
protected bool firstInitiation = true;
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected Transform root;
|
|
|
|
public bool initiated { get; private set; }
|
|
|
|
public bool IsValid()
|
|
{
|
|
string message = string.Empty;
|
|
return IsValid(ref message);
|
|
}
|
|
|
|
public abstract bool IsValid(ref string message);
|
|
|
|
public void Initiate(Transform root)
|
|
{
|
|
if (executedInEditor)
|
|
{
|
|
return;
|
|
}
|
|
if (OnPreInitiate != null)
|
|
{
|
|
OnPreInitiate();
|
|
}
|
|
if (root == null)
|
|
{
|
|
Debug.LogError("Initiating IKSolver with null root Transform.");
|
|
}
|
|
this.root = root;
|
|
initiated = false;
|
|
string message = string.Empty;
|
|
if (!IsValid(ref message))
|
|
{
|
|
Warning.Log(message, root);
|
|
return;
|
|
}
|
|
OnInitiate();
|
|
StoreDefaultLocalState();
|
|
initiated = true;
|
|
firstInitiation = false;
|
|
if (OnPostInitiate != null)
|
|
{
|
|
OnPostInitiate();
|
|
}
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if (OnPreUpdate != null)
|
|
{
|
|
OnPreUpdate();
|
|
}
|
|
if (firstInitiation)
|
|
{
|
|
Initiate(root);
|
|
}
|
|
if (initiated)
|
|
{
|
|
OnUpdate();
|
|
if (OnPostUpdate != null)
|
|
{
|
|
OnPostUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual Vector3 GetIKPosition()
|
|
{
|
|
return IKPosition;
|
|
}
|
|
|
|
public void SetIKPosition(Vector3 position)
|
|
{
|
|
IKPosition = position;
|
|
}
|
|
|
|
public float GetIKPositionWeight()
|
|
{
|
|
return IKPositionWeight;
|
|
}
|
|
|
|
public void SetIKPositionWeight(float weight)
|
|
{
|
|
IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
|
|
}
|
|
|
|
public Transform GetRoot()
|
|
{
|
|
return root;
|
|
}
|
|
|
|
public abstract Point[] GetPoints();
|
|
|
|
public abstract Point GetPoint(Transform transform);
|
|
|
|
public abstract void FixTransforms();
|
|
|
|
public abstract void StoreDefaultLocalState();
|
|
|
|
protected abstract void OnInitiate();
|
|
|
|
protected abstract void OnUpdate();
|
|
|
|
protected void LogWarning(string message)
|
|
{
|
|
Warning.Log(message, root, logInEditMode: true);
|
|
}
|
|
|
|
public static Transform ContainsDuplicateBone(Bone[] bones)
|
|
{
|
|
for (int i = 0; i < bones.Length; i++)
|
|
{
|
|
for (int j = 0; j < bones.Length; j++)
|
|
{
|
|
if (i != j && bones[i].transform == bones[j].transform)
|
|
{
|
|
return bones[i].transform;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static bool HierarchyIsValid(Bone[] bones)
|
|
{
|
|
for (int i = 1; i < bones.Length; i++)
|
|
{
|
|
if (!Hierarchy.IsAncestor(bones[i].transform, bones[i - 1].transform))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected static float PreSolveBones(ref Bone[] bones)
|
|
{
|
|
float num = 0f;
|
|
for (int i = 0; i < bones.Length; i++)
|
|
{
|
|
bones[i].solverPosition = bones[i].transform.position;
|
|
bones[i].solverRotation = bones[i].transform.rotation;
|
|
}
|
|
for (int j = 0; j < bones.Length; j++)
|
|
{
|
|
if (j < bones.Length - 1)
|
|
{
|
|
bones[j].sqrMag = (bones[j + 1].solverPosition - bones[j].solverPosition).sqrMagnitude;
|
|
bones[j].length = Mathf.Sqrt(bones[j].sqrMag);
|
|
num += bones[j].length;
|
|
bones[j].axis = Quaternion.Inverse(bones[j].solverRotation) * (bones[j + 1].solverPosition - bones[j].solverPosition);
|
|
}
|
|
else
|
|
{
|
|
bones[j].sqrMag = 0f;
|
|
bones[j].length = 0f;
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
}
|
|
}
|