using UnityEngine; using System.Collections; using System; namespace RootMotion.FinalIK { /// /// %IK system for multiple branched %FABRIK chains. /// [System.Serializable] public class IKSolverFABRIKRoot : IKSolver { #region Main Interface /// /// Solver iterations. /// public int iterations = 4; /// /// The weight of all chains being pinned to root position. /// [Range(0f, 1f)] public float rootPin = 0f; /// /// The %FABRIK chains. /// public FABRIKChain[] chains = new FABRIKChain[0]; public override bool IsValid(ref string message) { if (chains.Length == 0) { message = "IKSolverFABRIKRoot contains no chains."; return false; } foreach (FABRIKChain chain in chains) { if (!chain.IsValid(ref message)) return false; } for (int i = 0; i < chains.Length; i++) { for (int c = 0; c < chains.Length; c++) { if (i != c && chains[i].ik == chains[c].ik) { message = chains[i].ik.name + " is represented more than once in IKSolverFABRIKRoot chain."; return false; } } } // Check the children for (int i = 0; i < chains.Length; i++) { for (int c = 0; c < chains[i].children.Length; c++) { int childIndex = chains[i].children[c]; if (childIndex < 0) { message = chains[i].ik.name + "IKSolverFABRIKRoot chain at index " + i + " has invalid children array. Child index is < 0."; return false; } if (childIndex == i) { message = chains[i].ik.name + "IKSolverFABRIKRoot chain at index " + i + " has invalid children array. Child index is referencing to itself."; return false; } if (childIndex >= chains.Length) { message = chains[i].ik.name + "IKSolverFABRIKRoot chain at index " + i + " has invalid children array. Child index > number of chains"; return false; } // Check if the child chain doesn't have this chain among it's children for (int o = 0; o < chains.Length; o++) { if (childIndex == o) { for (int n = 0; n < chains[o].children.Length; n++) { if (chains[o].children[n] == i) { message = "Circular parenting. " + chains[o].ik.name + " already has " + chains[i].ik.name + " listed as it's child."; return false; } } } } // Check for duplicates for (int n = 0; n < chains[i].children.Length; n++) { if (c != n && chains[i].children[n] == childIndex) { message = "Chain number " + childIndex + " is represented more than once in the children of " + chains[i].ik.name; return false; } } } } return true; } public override void StoreDefaultLocalState() { rootDefaultPosition = root.localPosition; for (int i = 0; i < chains.Length; i++) chains[i].ik.solver.StoreDefaultLocalState(); } public override void FixTransforms() { if (!initiated) return; root.localPosition = rootDefaultPosition; for (int i = 0; i < chains.Length; i++) chains[i].ik.solver.FixTransforms(); } #endregion Main Interface private bool zeroWeightApplied; private bool[] isRoot; private Vector3 rootDefaultPosition; protected override void OnInitiate() { for (int i = 0; i < chains.Length; i++) chains[i].Initiate(); isRoot = new bool[chains.Length]; for (int i = 0; i < chains.Length; i++) { isRoot[i] = IsRoot(i); } } // Is the chain at index a root chain (not parented by any other chains)? private bool IsRoot(int index) { for (int i = 0; i < chains.Length; i++) { for (int c = 0; c < chains[i].children.Length; c++) { if (chains[i].children[c] == index) return false; } } return true; } protected override void OnUpdate() { if (IKPositionWeight <= 0 && zeroWeightApplied) return; IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f); // Set weight of all IK solvers for (int i = 0; i < chains.Length; i++) chains[i].ik.solver.IKPositionWeight = IKPositionWeight; if (IKPositionWeight <= 0) { zeroWeightApplied = true; return; } zeroWeightApplied = false; for (int i = 0; i < iterations; i++) { // Solve trees from their targets for (int c = 0; c < chains.Length; c++) { if (isRoot[c]) chains[c].Stage1(chains); } // Get centroid of all tree roots Vector3 centroid = GetCentroid(); root.position = centroid; // Start all trees from the centroid for (int c = 0; c < chains.Length; c++) { if (isRoot[c]) chains[c].Stage2(centroid, chains); } } } public override IKSolver.Point[] GetPoints() { IKSolver.Point[] array = new IKSolver.Point[0]; for (int i = 0; i < chains.Length; i++) AddPointsToArray(ref array, chains[i]); return array; } public override IKSolver.Point GetPoint(Transform transform) { IKSolver.Point p = null; for (int i = 0; i < chains.Length; i++) { p = chains[i].ik.solver.GetPoint(transform); if (p != null) return p; } return null; } private void AddPointsToArray(ref IKSolver.Point[] array, FABRIKChain chain) { IKSolver.Point[] chainArray = chain.ik.solver.GetPoints(); Array.Resize(ref array, array.Length + chainArray.Length); int a = 0; for (int i = array.Length - chainArray.Length; i < array.Length; i++) { array[i] = chainArray[a]; a++; } } /* * Gets the centroid position of all chains respective of their pull weights * */ private Vector3 GetCentroid() { Vector3 centroid = root.position; if (rootPin >= 1) return centroid; float pullSum = 0f; for (int i = 0; i < chains.Length; i++) { if (isRoot[i]) pullSum += chains[i].pull; } for (int i = 0; i < chains.Length; i++) { if (isRoot[i] && pullSum > 0) centroid += (chains[i].ik.solver.bones[0].solverPosition - root.position) * (chains[i].pull / Mathf.Clamp(pullSum, 1f, pullSum)); } return Vector3.Lerp(centroid, root.position, rootPin); } } }