123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- using UnityEngine;
- using System.Collections;
- namespace RootMotion.FinalIK {
- /// <summary>
- /// The base abstract class for all %IK solvers
- /// </summary>
- [System.Serializable]
- public abstract class IKSolver {
- #region Main Interface
- [HideInInspector] public bool executedInEditor;
- /// <summary>
- /// Determines whether this instance is valid or not.
- /// </summary>
- public bool IsValid() {
- string message = string.Empty;
- return IsValid(ref message);
- }
- /// <summary>
- /// Determines whether this instance is valid or not. If returns false, also fills in an error message.
- /// </summary>
- public abstract bool IsValid(ref string message);
-
- /// <summary>
- /// Initiate the solver with specified root Transform. Use only if this %IKSolver is not a member of an %IK component.
- /// </summary>
- 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, false);
- return;
- }
- OnInitiate();
- StoreDefaultLocalState();
- initiated = true;
- firstInitiation = false;
- if (OnPostInitiate != null) OnPostInitiate();
- }
-
- /// <summary>
- /// Updates the %IK solver. Use only if this %IKSolver is not a member of an %IK component or the %IK component has been disabled and you intend to manually control the updating.
- /// </summary>
- public void Update() {
- if (OnPreUpdate != null) OnPreUpdate();
- if (firstInitiation) Initiate(root); // when the IK component has been disabled in Awake, this will initiate it.
- if (!initiated) return;
- OnUpdate();
- if (OnPostUpdate != null) OnPostUpdate();
- }
-
- /// <summary>
- /// The %IK position.
- /// </summary>
- [HideInInspector] public Vector3 IKPosition;
- [Tooltip("The positional or the master weight of the solver.")]
- /// <summary>
- /// The %IK position weight or the master weight of the solver.
- /// </summary>
- [Range(0f, 1f)]
- public float IKPositionWeight = 1f;
-
- /// <summary>
- /// Gets the %IK position. NOTE: You are welcome to read IKPosition directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public virtual Vector3 GetIKPosition() {
- return IKPosition;
- }
-
- /// <summary>
- /// Sets the %IK position. NOTE: You are welcome to set IKPosition directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetIKPosition(Vector3 position) {
- IKPosition = position;
- }
-
- /// <summary>
- /// Gets the %IK position weight. NOTE: You are welcome to read IKPositionWeight directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public float GetIKPositionWeight() {
- return IKPositionWeight;
- }
-
- /// <summary>
- /// Sets the %IK position weight. NOTE: You are welcome to set IKPositionWeight directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetIKPositionWeight(float weight) {
- IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- }
-
- /// <summary>
- /// Gets the root Transform.
- /// </summary>
- public Transform GetRoot() {
- return root;
- }
- /// <summary>
- /// Gets a value indicating whether this <see cref="IKSolver"/> has successfully initiated.
- /// </summary>
- public bool initiated { get; private set; }
- /// <summary>
- /// Gets all the points used by the solver.
- /// </summary>
- public abstract IKSolver.Point[] GetPoints();
-
- /// <summary>
- /// Gets the point with the specified Transform.
- /// </summary>
- public abstract IKSolver.Point GetPoint(Transform transform);
- /// <summary>
- /// Fixes all the Transforms used by the solver to their initial state.
- /// </summary>
- public abstract void FixTransforms();
- /// <summary>
- /// Stores the default local state for the bones used by the solver.
- /// </summary>
- public abstract void StoreDefaultLocalState();
-
- /// <summary>
- /// The most basic element type in the %IK chain that all other types extend from.
- /// </summary>
- [System.Serializable]
- public class Point {
- /// <summary>
- /// The transform.
- /// </summary>
- public Transform transform;
- /// <summary>
- /// The weight of this bone in the solver.
- /// </summary>
- [Range(0f, 1f)]
- public float weight = 1f;
- /// <summary>
- /// Virtual position in the %IK solver.
- /// </summary>
- public Vector3 solverPosition;
- /// <summary>
- /// Virtual rotation in the %IK solver.
- /// </summary>
- public Quaternion solverRotation = Quaternion.identity;
- /// <summary>
- /// The default local position of the Transform.
- /// </summary>
- public Vector3 defaultLocalPosition;
- /// <summary>
- /// The default local rotation of the Transform.
- /// </summary>
- public Quaternion defaultLocalRotation;
- /// <summary>
- /// Stores the default local state of the point.
- /// </summary>
- public void StoreDefaultLocalState() {
- defaultLocalPosition = transform.localPosition;
- defaultLocalRotation = transform.localRotation;
- }
- /// <summary>
- /// Fixes the transform to it's default local state.
- /// </summary>
- public void FixTransform() {
- if (transform.localPosition != defaultLocalPosition) transform.localPosition = defaultLocalPosition;
- if (transform.localRotation != defaultLocalRotation) transform.localRotation = defaultLocalRotation;
- }
- /// <summary>
- /// Updates the solverPosition (in world space).
- /// </summary>
- public void UpdateSolverPosition() {
- solverPosition = transform.position;
- }
- /// <summary>
- /// Updates the solverPosition (in local space).
- /// </summary>
- public void UpdateSolverLocalPosition() {
- solverPosition = transform.localPosition;
- }
- /// <summary>
- /// Updates the solverPosition/Rotation (in world space).
- /// </summary>
- public void UpdateSolverState() {
- solverPosition = transform.position;
- solverRotation = transform.rotation;
- }
- /// <summary>
- /// Updates the solverPosition/Rotation (in local space).
- /// </summary>
- public void UpdateSolverLocalState() {
- solverPosition = transform.localPosition;
- solverRotation = transform.localRotation;
- }
- }
-
- /// <summary>
- /// %Bone type of element in the %IK chain. Used in the case of skeletal Transform hierarchies.
- /// </summary>
- [System.Serializable]
- public class Bone: Point {
-
- /// <summary>
- /// The length of the bone.
- /// </summary>
- public float length;
- /// <summary>
- /// The sqr mag of the bone.
- /// </summary>
- public float sqrMag;
- /// <summary>
- /// Local axis to target/child bone.
- /// </summary>
- public Vector3 axis = -Vector3.right;
-
- /// <summary>
- /// Gets the rotation limit component from the Transform if there is any.
- /// </summary>
- 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;
- }
- }
-
- /*
- * Swings the Transform's axis towards the swing target
- * */
- public void Swing(Vector3 swingTarget, float weight = 1f) {
- if (weight <= 0f) return;
- Quaternion r = Quaternion.FromToRotation(transform.rotation * axis, swingTarget - transform.position);
- if (weight >= 1f) {
- transform.rotation = r * transform.rotation;
- return;
- }
- transform.rotation = Quaternion.Lerp(Quaternion.identity, r, weight) * transform.rotation;
- }
- public static void SolverSwing(Bone[] bones, int index, Vector3 swingTarget, float weight = 1f) {
- if (weight <= 0f) return;
-
- Quaternion r = 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 = r * bones[i].solverRotation;
- }
- return;
- }
-
- for (int i = index; i < bones.Length; i++) {
- bones[i].solverRotation = Quaternion.Lerp(Quaternion.identity, r, weight) * bones[i].solverRotation;
- }
- }
- /*
- * Swings the Transform's axis towards the swing target on the XY plane only
- * */
- public void Swing2D(Vector3 swingTarget, float weight = 1f) {
- if (weight <= 0f) return;
- Vector3 from = transform.rotation * axis;
- Vector3 to = swingTarget - transform.position;
- float angleFrom = Mathf.Atan2(from.x, from.y) * Mathf.Rad2Deg;
- float angleTo = Mathf.Atan2(to.x, to.y) * Mathf.Rad2Deg;
- transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(angleFrom, angleTo) * weight, Vector3.back) * transform.rotation;
- }
-
- /*
- * Moves the bone to the solver position
- * */
- public void SetToSolverPosition() {
- transform.position = solverPosition;
- }
-
- public Bone() {}
-
- public Bone (Transform transform) {
- this.transform = transform;
- }
-
- public Bone (Transform transform, float weight) {
- this.transform = transform;
- this.weight = weight;
- }
-
- private RotationLimit _rotationLimit;
- private bool isLimited = true;
- }
-
- /// <summary>
- /// %Node type of element in the %IK chain. Used in the case of mixed/non-hierarchical %IK systems
- /// </summary>
- [System.Serializable]
- public class Node: Point {
-
- /// <summary>
- /// Distance to child node.
- /// </summary>
- public float length;
- /// <summary>
- /// The effector position weight.
- /// </summary>
- public float effectorPositionWeight;
- /// <summary>
- /// The effector rotation weight.
- /// </summary>
- public float effectorRotationWeight;
- /// <summary>
- /// Position offset.
- /// </summary>
- public Vector3 offset;
-
- public Node() {}
-
- public Node (Transform transform) {
- this.transform = transform;
- }
-
- public Node (Transform transform, float weight) {
- this.transform = transform;
- this.weight = weight;
- }
- }
- /// <summary>
- /// Delegates solver update events.
- /// </summary>
- public delegate void UpdateDelegate();
- /// <summary>
- /// Delegates solver iteration events.
- /// </summary>
- public delegate void IterationDelegate(int i);
- /// <summary>
- /// Called before initiating the solver.
- /// </summary>
- public UpdateDelegate OnPreInitiate;
- /// <summary>
- /// Called after initiating the solver.
- /// </summary>
- public UpdateDelegate OnPostInitiate;
- /// <summary>
- /// Called before updating.
- /// </summary>
- public UpdateDelegate OnPreUpdate;
- /// <summary>
- /// Called after writing the solved pose
- /// </summary>
- public UpdateDelegate OnPostUpdate;
-
- #endregion Main Interface
-
- protected abstract void OnInitiate();
- protected abstract void OnUpdate();
- protected bool firstInitiation = true;
- [SerializeField][HideInInspector] protected Transform root;
-
- protected void LogWarning(string message) {
- Warning.Log(message, root, true);
- }
- #region Class Methods
- /// <summary>
- /// Checks if an array of objects contains any duplicates.
- /// </summary>
- public static Transform ContainsDuplicateBone(Bone[] bones) {
- for (int i = 0; i < bones.Length; i++) {
- for (int i2 = 0; i2 < bones.Length; i2++) {
- if (i != i2 && bones[i].transform == bones[i2].transform) return bones[i].transform;
- }
- }
- return null;
- }
- /*
- * Make sure the bones are in valid Hierarchy
- * */
- public static bool HierarchyIsValid(IKSolver.Bone[] bones) {
- for (int i = 1; i < bones.Length; i++) {
- // If parent bone is not an ancestor of bone, the hierarchy is invalid
- if (!Hierarchy.IsAncestor(bones[i].transform, bones[i - 1].transform)) {
- return false;
- }
- }
- return true;
- }
- // Calculates bone lengths and axes, returns the length of the entire chain
- protected static float PreSolveBones(ref Bone[] bones) {
- float length = 0;
-
- for (int i = 0; i < bones.Length; i++) {
- bones[i].solverPosition = bones[i].transform.position;
- bones[i].solverRotation = bones[i].transform.rotation;
- }
-
- for (int i = 0; i < bones.Length; i++) {
- if (i < bones.Length - 1) {
- bones[i].sqrMag = (bones[i + 1].solverPosition - bones[i].solverPosition).sqrMagnitude;
- bones[i].length = Mathf.Sqrt(bones[i].sqrMag);
- length += bones[i].length;
-
- bones[i].axis = Quaternion.Inverse(bones[i].solverRotation) * (bones[i + 1].solverPosition - bones[i].solverPosition);
- } else {
- bones[i].sqrMag = 0f;
- bones[i].length = 0f;
- }
- }
-
- return length;
- }
- #endregion Class Methods
- }
- }
|