123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- using UnityEngine;
- using System.Collections;
- namespace RootMotion.FinalIK {
- /// <summary>
- /// Mapping a bone hierarchy to 2 triangles defined by the hip and chest planes.
- /// </summary>
- [System.Serializable]
- public class IKMappingSpine: IKMapping {
-
- #region Main Interface
- /// <summary>
- /// The spine bones.
- /// </summary>
- public Transform[] spineBones;
- /// <summary>
- /// The left upper arm bone.
- /// </summary>
- public Transform leftUpperArmBone;
- /// <summary>
- /// The right upper arm bone.
- /// </summary>
- public Transform rightUpperArmBone;
- /// <summary>
- /// The left thigh bone.
- /// </summary>
- public Transform leftThighBone;
- /// <summary>
- /// The right thigh bone.
- /// </summary>
- public Transform rightThighBone;
- /// <summary>
- /// The number of iterations of the %FABRIK algorithm. Not used if there are 2 bones assigned to Spine in the References.
- /// </summary>
- [Range(1, 3)]
- public int iterations = 3;
- /// <summary>
- /// The weight of twisting the spine bones gradually to the orientation of the chest triangle. Relatively expensive, so set this to 0 if there is not much spine twisting going on.
- /// </summary>
- [Range(0f, 1f)]
- public float twistWeight = 1f;
- /// <summary>
- /// Determines whether this IKMappingSpine is valid
- /// </summary>
- public override bool IsValid(IKSolver solver, ref string message) {
- if (!base.IsValid(solver, ref message)) return false;
-
- foreach (Transform spineBone in spineBones) if (spineBone == null) {
- message = "Spine bones contains a null reference.";
- return false;
- }
-
- int nodes = 0;
- for (int i = 0; i < spineBones.Length; i++) {
- if (solver.GetPoint(spineBones[i]) != null) nodes ++;
- }
-
- if (nodes == 0) {
- message = "IKMappingSpine does not contain any nodes.";
- return false;
- }
-
- if (leftUpperArmBone == null) {
- message = "IKMappingSpine is missing the left upper arm bone.";
- return false;
- }
-
- if (rightUpperArmBone == null) {
- message = "IKMappingSpine is missing the right upper arm bone.";
- return false;
- }
-
- if (leftThighBone == null) {
- message = "IKMappingSpine is missing the left thigh bone.";
- return false;
- }
-
- if (rightThighBone == null) {
- message = "IKMappingSpine is missing the right thigh bone.";
- return false;
- }
-
- if (solver.GetPoint(leftUpperArmBone) == null) {
- message = "Full Body IK is missing the left upper arm node.";
- return false;
- }
-
- if (solver.GetPoint(rightUpperArmBone) == null) {
- message = "Full Body IK is missing the right upper arm node.";
- return false;
- }
-
- if (solver.GetPoint(leftThighBone) == null) {
- message = "Full Body IK is missing the left thigh node.";
- return false;
- }
-
- if (solver.GetPoint(rightThighBone) == null) {
- message = "Full Body IK is missing the right thigh node.";
- return false;
- }
- return true;
- }
- #endregion Main Interface
-
- private int rootNodeIndex;
- private BoneMap[] spine = new BoneMap[0];
- private BoneMap leftUpperArm = new BoneMap(), rightUpperArm = new BoneMap(), leftThigh = new BoneMap(), rightThigh = new BoneMap();
- private bool useFABRIK;
-
- public IKMappingSpine() {}
-
- public IKMappingSpine(Transform[] spineBones, Transform leftUpperArmBone, Transform rightUpperArmBone, Transform leftThighBone, Transform rightThighBone) {
- SetBones(spineBones, leftUpperArmBone, rightUpperArmBone, leftThighBone, rightThighBone);
- }
-
- public void SetBones(Transform[] spineBones, Transform leftUpperArmBone, Transform rightUpperArmBone, Transform leftThighBone, Transform rightThighBone) {
- this.spineBones = spineBones;
- this.leftUpperArmBone = leftUpperArmBone;
- this.rightUpperArmBone = rightUpperArmBone;
- this.leftThighBone = leftThighBone;
- this.rightThighBone = rightThighBone;
- }
- public void StoreDefaultLocalState() {
- for (int i = 0; i < spine.Length; i++) {
- spine[i].StoreDefaultLocalState();
- }
- }
-
- public void FixTransforms() {
- for (int i = 0; i < spine.Length; i++) {
- spine[i].FixTransform(i == 0 || i == spine.Length - 1);
- }
- }
-
- /*
- * Initiating and setting defaults
- * */
- public override void Initiate(IKSolverFullBody solver) {
- if (iterations <= 0) iterations = 3;
-
- // Creating the bone maps
- if (spine == null || spine.Length != spineBones.Length) spine = new BoneMap[spineBones.Length];
- rootNodeIndex = -1;
-
- for (int i = 0; i < spineBones.Length; i++) {
- if (spine[i] == null) spine[i] = new BoneMap();
- spine[i].Initiate(spineBones[i], solver);
- // Finding the root node
- if (spine[i].isNodeBone) rootNodeIndex = i;
- }
- if (leftUpperArm == null) leftUpperArm = new BoneMap();
- if (rightUpperArm == null) rightUpperArm = new BoneMap();
- if (leftThigh == null) leftThigh = new BoneMap();
- if (rightThigh == null) rightThigh = new BoneMap();
-
- leftUpperArm.Initiate(leftUpperArmBone, solver);
- rightUpperArm.Initiate(rightUpperArmBone, solver);
- leftThigh.Initiate(leftThighBone, solver);
- rightThigh.Initiate(rightThighBone, solver);
- for (int i = 0; i < spine.Length; i++) spine[i].SetIKPosition();
-
- // Defining the plane for the first bone
- spine[0].SetPlane(solver, spine[rootNodeIndex].transform, leftThigh.transform, rightThigh.transform);
-
- // Finding bone lengths and axes
- for (int i = 0; i < spine.Length - 1; i++) {
- spine[i].SetLength(spine[i + 1]);
- spine[i].SetLocalSwingAxis(spine[i + 1]);
- spine[i].SetLocalTwistAxis(leftUpperArm.transform.position - rightUpperArm.transform.position, spine[i + 1].transform.position - spine[i].transform.position);
- }
-
- // Defining the plane for the last bone
- spine[spine.Length - 1].SetPlane(solver, spine[rootNodeIndex].transform, leftUpperArm.transform, rightUpperArm.transform);
- spine[spine.Length - 1].SetLocalSwingAxis(leftUpperArm, rightUpperArm);
- useFABRIK = UseFABRIK();
- }
- // Should the spine mapping use the FABRIK algorithm
- private bool UseFABRIK() {
- if (spine.Length > 3) return true;
- if (rootNodeIndex != 1) return true;
- return false;
- }
- /*
- * Updating the bone maps to the current animated state of the character
- * */
- public void ReadPose() {
- spine[0].UpdatePlane(true, true);
-
- for (int i = 0; i < spine.Length - 1; i++) {
- spine[i].SetLength(spine[i + 1]);
- spine[i].SetLocalSwingAxis(spine[i + 1]);
- spine[i].SetLocalTwistAxis(leftUpperArm.transform.position - rightUpperArm.transform.position, spine[i + 1].transform.position - spine[i].transform.position);
- }
-
- spine[spine.Length - 1].UpdatePlane(true, true);
- spine[spine.Length - 1].SetLocalSwingAxis(leftUpperArm, rightUpperArm);
- }
- /*
- * Mapping the spine to the hip and chest planes
- * */
- public void WritePose(IKSolverFullBody solver) {
- Vector3 firstPosition = spine[0].GetPlanePosition(solver);
- Vector3 rootPosition = solver.GetNode(spine[rootNodeIndex].chainIndex, spine[rootNodeIndex].nodeIndex).solverPosition;
- Vector3 lastPosition = spine[spine.Length - 1].GetPlanePosition(solver);
- // If we have more than 3 bones, use the FABRIK algorithm
- if (useFABRIK) {
- Vector3 offset = solver.GetNode(spine[rootNodeIndex].chainIndex, spine[rootNodeIndex].nodeIndex).solverPosition - spine[rootNodeIndex].transform.position;
-
- for (int i = 0; i < spine.Length; i++) {
- spine[i].ikPosition = spine[i].transform.position + offset;
- }
-
- // Iterating the FABRIK algorithm
- for (int i = 0; i < iterations; i++) {
- ForwardReach(lastPosition);
- BackwardReach(firstPosition);
- spine[rootNodeIndex].ikPosition = rootPosition;
- }
- } else {
- // When we have just 3 bones, we know their positions already
- spine[0].ikPosition = firstPosition;
- spine[rootNodeIndex].ikPosition = rootPosition;
- }
- spine[spine.Length - 1].ikPosition = lastPosition;
- // Mapping the spine bones to the solver
- MapToSolverPositions(solver);
- }
-
- /*
- * Stage 1 of the FABRIK algorithm.
- * */
- public void ForwardReach(Vector3 position) {
- // Lerp last bone's ikPosition to position
- spine[spineBones.Length - 1].ikPosition = position;
-
- for (int i = spine.Length - 2; i > -1; i--) {
- // Finding joint positions
- spine[i].ikPosition = SolveFABRIKJoint(spine[i].ikPosition, spine[i + 1].ikPosition, spine[i].length);
- }
- }
-
- /*
- * Stage 2 of the FABRIK algorithm
- * */
- private void BackwardReach(Vector3 position) {
- spine[0].ikPosition = position;
-
- // Finding joint positions
- for (int i = 1; i < spine.Length; i++) {
- spine[i].ikPosition = SolveFABRIKJoint(spine[i].ikPosition, spine[i - 1].ikPosition, spine[i - 1].length);
- }
- }
-
- /*
- * Positioning and rotating the spine bones to match the solver positions
- * */
- private void MapToSolverPositions(IKSolverFullBody solver) {
- // Translating the first bone
- // Note: spine here also includes the pelvis
- spine[0].SetToIKPosition();
- spine[0].RotateToPlane(solver, 1f);
- // Translating all the bones between the first and the last
- for (int i = 1; i < spine.Length - 1; i++) {
- spine[i].Swing(spine[i + 1].ikPosition, 1f);
- if (twistWeight > 0) {
- float bWeight = (float)i / ((float)spine.Length - 2);
- Vector3 s1 = solver.GetNode(leftUpperArm.chainIndex, leftUpperArm.nodeIndex).solverPosition;
- Vector3 s2 = solver.GetNode(rightUpperArm.chainIndex, rightUpperArm.nodeIndex).solverPosition;
- spine[i].Twist(s1 - s2, spine[i + 1].ikPosition - spine[i].transform.position, bWeight * twistWeight);
- }
- }
-
- // Translating the last bone
- spine[spine.Length - 1].SetToIKPosition();
- spine[spine.Length - 1].RotateToPlane(solver, 1f);
- }
- }
- }
|