123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- using UnityEngine;
- using System.Collections;
- namespace RootMotion.FinalIK {
-
- /// <summary>
- /// Rotates a hierarchy of bones to face a target.
- /// </summary>
- [System.Serializable]
- public class IKSolverLookAt : IKSolver {
-
- #region Main Interface
- /// <summary>
- /// The target Transform.
- /// </summary>
- public Transform target;
- /// <summary>
- /// The spine hierarchy.
- /// </summary>
- public LookAtBone[] spine = new LookAtBone[0];
- /// <summary>
- /// The head bone.
- /// </summary>
- public LookAtBone head = new LookAtBone();
- /// <summary>
- /// The eye bones.
- /// </summary>
- public LookAtBone[] eyes = new LookAtBone[0];
- /// <summary>
- /// The body weight.
- /// </summary>
- [Range(0f, 1f)]
- public float bodyWeight = 0.5f;
- /// <summary>
- /// The head weight.
- /// </summary>
- [Range(0f, 1f)]
- public float headWeight = 0.5f;
- /// <summary>
- /// The eyes weight.
- /// </summary>
- [Range(0f, 1f)]
- public float eyesWeight = 1f;
- /// <summary>
- /// Clamp weight for the body.
- /// </summary>
- [Range(0f, 1f)]
- public float clampWeight = 0.5f;
- /// <summary>
- /// Clamp weight for the head.
- /// </summary>
- [Range(0f, 1f)]
- public float clampWeightHead = 0.5f;
- /// <summary>
- /// Clamp weight for the eyes.
- /// </summary>
- [Range(0f, 1f)]
- public float clampWeightEyes = 0.5f;
- /// <summary>
- /// Number of sine smoothing iterations applied on clamping to make the clamping point smoother.
- /// </summary>
- [Range(0, 2)]
- public int clampSmoothing = 2;
- /// <summary>
- /// Weight distribution between the spine bones.
- /// </summary>
- public AnimationCurve spineWeightCurve = new AnimationCurve(new Keyframe[2] { new Keyframe(0f, 0.3f), new Keyframe(1f, 1f) });
- /// <summary>
- /// Offset for the spine target in world space..
- /// </summary>
- public Vector3 spineTargetOffset;
-
- /// <summary>
- /// Sets the look at weight. NOTE: You are welcome edit the weights directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetLookAtWeight(float weight) {
- this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- }
-
- /// <summary>
- /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetLookAtWeight(float weight, float bodyWeight) {
- this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
- }
-
- /// <summary>
- /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetLookAtWeight(float weight, float bodyWeight, float headWeight) {
- this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
- this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
- }
-
- /// <summary>
- /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetLookAtWeight(float weight, float bodyWeight, float headWeight, float eyesWeight) {
- this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
- this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
- this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
- }
-
- /// <summary>
- /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetLookAtWeight(float weight, float bodyWeight, float headWeight, float eyesWeight, float clampWeight) {
- this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
- this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
- this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
- this.clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
- this.clampWeightHead = this.clampWeight;
- this.clampWeightEyes = this.clampWeight;
- }
-
- /// <summary>
- /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
- /// </summary>
- public void SetLookAtWeight(float weight, float bodyWeight = 0f, float headWeight = 1f, float eyesWeight = 0.5f, float clampWeight = 0.5f, float clampWeightHead = 0.5f, float clampWeightEyes = 0.3f) {
- this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
- this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
- this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
- this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
- this.clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
- this.clampWeightHead = Mathf.Clamp(clampWeightHead, 0f, 1f);
- this.clampWeightEyes = Mathf.Clamp(clampWeightEyes, 0f, 1f);
- }
- public override void StoreDefaultLocalState() {
- for (int i = 0; i < spine.Length; i++) spine[i].StoreDefaultLocalState();
- for (int i = 0; i < eyes.Length; i++) eyes[i].StoreDefaultLocalState();
- if (head != null && head.transform != null) head.StoreDefaultLocalState();
- }
- // Flag for Fix Transforms.
- public void SetDirty()
- {
- isDirty = true;
- }
- public override void FixTransforms() {
- if (!initiated) return;
- if (IKPositionWeight <= 0f && !isDirty) return;
- for (int i = 0; i < spine.Length; i++) spine[i].FixTransform();
- for (int i = 0; i < eyes.Length; i++) eyes[i].FixTransform();
- if (head != null && head.transform != null) head.FixTransform();
- isDirty = false;
- }
-
- public override bool IsValid (ref string message) {
- if (!spineIsValid) {
- message = "IKSolverLookAt spine setup is invalid. Can't initiate solver.";
- return false;
- }
- if (!headIsValid) {
- message = "IKSolverLookAt head transform is null. Can't initiate solver.";
- return false;
- }
- if (!eyesIsValid) {
- message = "IKSolverLookAt eyes setup is invalid. Can't initiate solver.";
- return false;
- }
- if (spineIsEmpty && headIsEmpty && eyesIsEmpty) {
- message = "IKSolverLookAt eyes setup is invalid. Can't initiate solver.";
- return false;
- }
- Transform spineDuplicate = ContainsDuplicateBone(spine);
- if (spineDuplicate != null) {
- message = spineDuplicate.name + " is represented multiple times in a single IK chain. Can't initiate solver.";
- return false;
- }
- Transform eyeDuplicate = ContainsDuplicateBone(eyes);
- if (eyeDuplicate != null) {
- message = eyeDuplicate.name + " is represented multiple times in a single IK chain. Can't initiate solver.";
- return false;
- }
- return true;
- }
-
- public override IKSolver.Point[] GetPoints() {
- IKSolver.Point[] allPoints = new IKSolver.Point[spine.Length + eyes.Length + (head.transform != null? 1: 0)];
- for (int i = 0; i < spine.Length; i++) allPoints[i] = spine[i] as IKSolver.Point;
- int eye = 0;
- for (int i = spine.Length; i < spine.Length + eyes.Length; i++)
- {
- allPoints[i] = eyes[eye] as IKSolver.Point;
- eye++;
- }
-
- if (head.transform != null) allPoints[allPoints.Length - 1] = head as IKSolver.Point;
- return allPoints;
- }
-
- public override IKSolver.Point GetPoint(Transform transform) {
- foreach (IKSolverLookAt.LookAtBone b in spine) if (b.transform == transform) return b as IKSolver.Point;
- foreach (IKSolverLookAt.LookAtBone b in eyes) if (b.transform == transform) return b as IKSolver.Point;
- if (head.transform == transform) return head as IKSolver.Point;
- return null;
- }
-
- /// <summary>
- /// Look At bone class.
- /// </summary>
- [System.Serializable]
- public class LookAtBone: IKSolver.Bone {
- #region Public methods
- public Vector3 baseForwardOffsetEuler;
- public LookAtBone() {}
- /*
- * Custom constructor
- * */
- public LookAtBone(Transform transform) {
- this.transform = transform;
- }
-
- /*
- * Initiates the bone, precalculates values.
- * */
- public void Initiate(Transform root) {
- if (transform == null) return;
-
- axis = Quaternion.Inverse(transform.rotation) * root.forward;
- }
-
- /*
- * Rotates the bone to look at a world direction.
- * */
- public void LookAt(Vector3 direction, float weight) {
- Quaternion fromTo = Quaternion.FromToRotation(forward, direction);
- Quaternion r = transform.rotation;
- transform.rotation = Quaternion.Lerp(r, fromTo * r, weight);
- }
-
- /*
- * Gets the local axis to goal in world space.
- * */
- public Vector3 forward {
- get {
- return transform.rotation * axis;
- }
- }
- #endregion Public methods
- }
-
- /// <summary>
- /// Reinitiate the solver with new bone Transforms.
- /// </summary>
- /// <returns>
- /// Returns true if the new chain is valid.
- /// </returns>
- public bool SetChain(Transform[] spine, Transform head, Transform[] eyes, Transform root) {
- // Spine
- SetBones(spine, ref this.spine);
- // Head
- this.head = new LookAtBone(head);
- // Eyes
- SetBones(eyes, ref this.eyes);
-
- Initiate(root);
- return initiated;
- }
- #endregion Main Interface
- protected Vector3[] spineForwards = new Vector3[0];
- protected Vector3[] headForwards = new Vector3[1];
- protected Vector3[] eyeForward = new Vector3[1];
- private bool isDirty;
- protected override void OnInitiate() {
- // Set IKPosition to default value
- if (firstInitiation || !Application.isPlaying) {
- if (spine.Length > 0) IKPosition = spine[spine.Length - 1].transform.position + root.forward * 3f;
- else if (head.transform != null) IKPosition = head.transform.position + root.forward * 3f;
- else if (eyes.Length > 0 && eyes[0].transform != null) IKPosition = eyes[0].transform.position + root.forward * 3f;
- }
-
- // Initiating the bones
- foreach (LookAtBone s in spine) s.Initiate(root);
- if (head != null) head.Initiate(root);
- foreach (LookAtBone eye in eyes) eye.Initiate(root);
-
- if (spineForwards == null || spineForwards.Length != spine.Length) spineForwards = new Vector3[spine.Length];
- if (headForwards == null) headForwards = new Vector3[1];
- if (eyeForward == null) eyeForward = new Vector3[1];
- }
-
- protected override void OnUpdate() {
- if (IKPositionWeight <= 0) return;
- IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
- if (target != null) IKPosition = target.position;
- // Solving the hierarchies
- SolveSpine();
- SolveHead();
- SolveEyes();
- }
- protected bool spineIsValid {
- get {
- if (spine == null) return false;
- if (spine.Length == 0) return true;
- for (int i = 0; i < spine.Length; i++) if (spine[i] == null || spine[i].transform == null) return false;
- return true;
- }
- }
- protected bool spineIsEmpty { get { return spine.Length == 0; }}
- // Solving the spine hierarchy
- protected void SolveSpine() {
- if (bodyWeight <= 0) return;
- if (spineIsEmpty) return;
-
- // Get the look at vectors for each bone
- //Vector3 targetForward = Vector3.Lerp(spine[0].forward, (IKPosition - spine[spine.Length - 1].transform.position).normalized, bodyWeight * IKPositionWeight).normalized;
- Vector3 targetForward = (IKPosition + spineTargetOffset - spine[spine.Length - 1].transform.position).normalized;
- GetForwards(ref spineForwards, spine[0].forward, targetForward, spine.Length, clampWeight);
-
- // Rotate each bone to face their look at vectors
- for (int i = 0; i < spine.Length; i++) {
- spine[i].LookAt(spineForwards[i], bodyWeight * IKPositionWeight);
- }
- }
- protected bool headIsValid {
- get {
- if (head == null) return false;
- return true;
- }
- }
- protected bool headIsEmpty { get { return head.transform == null; }}
- // Solving the head rotation
- protected void SolveHead() {
- if (headWeight <= 0) return;
- if (headIsEmpty) return;
-
- // Get the look at vector for the head
- Vector3 baseForward = spine.Length > 0 && spine[spine.Length - 1].transform != null? spine[spine.Length - 1].forward: head.forward;
- Vector3 targetForward = Vector3.Lerp(baseForward, (IKPosition - head.transform.position).normalized, headWeight * IKPositionWeight).normalized;
- GetForwards(ref headForwards, baseForward, targetForward, 1, clampWeightHead);
-
- // Rotate the head to face its look at vector
- head.LookAt(headForwards[0], headWeight * IKPositionWeight);
- }
- protected bool eyesIsValid {
- get {
- if (eyes == null) return false;
- if (eyes.Length == 0) return true;
- for (int i = 0; i < eyes.Length; i++) if (eyes[i] == null || eyes[i].transform == null) return false;
- return true;
- }
- }
- protected bool eyesIsEmpty { get { return eyes.Length == 0; }}
- // Solving the eye rotations
- protected void SolveEyes() {
- if (eyesWeight <= 0) return;
- if (eyesIsEmpty) return;
-
- for (int i = 0; i < eyes.Length; i++) {
- // Get the look at vector for the eye
- Quaternion baseRotation = head.transform != null ? head.transform.rotation : spine.Length > 0? spine[spine.Length - 1].transform.rotation: root.rotation;
- Vector3 baseAxis = head.transform != null ? head.axis : spine.Length > 0 ? spine[spine.Length - 1].axis : root.forward;
- if (eyes[i].baseForwardOffsetEuler != Vector3.zero) baseRotation *= Quaternion.Euler(eyes[i].baseForwardOffsetEuler);
- Vector3 baseForward = baseRotation * baseAxis;
- GetForwards(ref eyeForward, baseForward, (IKPosition - eyes[i].transform.position).normalized, 1, clampWeightEyes);
-
- // Rotate the eye to face its look at vector
- eyes[i].LookAt(eyeForward[0], eyesWeight * IKPositionWeight);
- }
- }
- /*
- * Returns forwards for a number of bones rotating from baseForward to targetForward.
- * NB! Make sure baseForward and targetForward are normalized.
- * */
- protected Vector3[] GetForwards(ref Vector3[] forwards, Vector3 baseForward, Vector3 targetForward, int bones, float clamp) {
- // If clamp >= 1 make all the forwards match the base
- if (clamp >= 1 || IKPositionWeight <= 0) {
- for (int i = 0; i < forwards.Length; i++) forwards[i] = baseForward;
- return forwards;
- }
-
- // Get normalized dot product.
- float angle = Vector3.Angle(baseForward, targetForward);
- float dot = 1f - (angle / 180f);
-
- // Clamping the targetForward so it doesn't exceed clamp
- float targetClampMlp = clamp > 0? Mathf.Clamp(1f - ((clamp - dot) / (1f - dot)), 0f, 1f): 1f;
-
- // Calculating the clamp multiplier
- float clampMlp = clamp > 0? Mathf.Clamp(dot / clamp, 0f, 1f): 1f;
-
- for (int i = 0; i < clampSmoothing; i++) {
- float sinF = clampMlp * Mathf.PI * 0.5f;
- clampMlp = Mathf.Sin(sinF);
- }
-
- // Rotation amount for 1 bone
- if (forwards.Length == 1) {
- forwards[0] = Vector3.Slerp(baseForward, targetForward, clampMlp * targetClampMlp);
- } else {
- float step = 1f / (float)(forwards.Length - 1);
-
- // Calculate the forward for each bone
- for (int i = 0; i < forwards.Length; i++) {
- forwards[i] = Vector3.Slerp(baseForward, targetForward, spineWeightCurve.Evaluate(step * i) * clampMlp * targetClampMlp);
- }
- }
-
- return forwards;
- }
- /*
- * Build LookAtBone[] array of a Transform array
- * */
- protected void SetBones(Transform[] array, ref LookAtBone[] bones) {
- if (array == null) {
- bones = new LookAtBone[0];
- return;
- }
-
- if (bones.Length != array.Length) bones = new LookAtBone[array.Length];
-
- for (int i = 0; i < array.Length; i++) {
- if (bones[i] == null) bones[i] = new LookAtBone(array[i]);
- else bones[i].transform = array[i];
- }
- }
- }
- }
|