You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			265 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C#
		
	
		
		
			
		
	
	
			265 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C#
		
	
| 
								 
											1 year ago
										 
									 | 
							
								using UnityEngine;
							 | 
						||
| 
								 | 
							
								using System.Collections;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace RootMotion.FinalIK {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/// <summary>
							 | 
						||
| 
								 | 
							
									/// Extends IKSolverTrigonometric to add automatic bend and rotation modes.
							 | 
						||
| 
								 | 
							
									/// </summary>
							 | 
						||
| 
								 | 
							
									[System.Serializable]
							 | 
						||
| 
								 | 
							
									public class IKSolverLimb : IKSolverTrigonometric {
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										#region Main Interface
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// The AvatarIKGoal of this solver.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public AvatarIKGoal goal;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Bend normal modifier.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public BendModifier bendModifier;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Weight of maintaining the rotation of the third bone as it was before solving.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[Range(0f, 1f)]
							 | 
						||
| 
								 | 
							
										public float maintainRotationWeight;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Weight of bend normal modifier.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[Range(0f, 1f)]
							 | 
						||
| 
								 | 
							
										public float bendModifierWeight = 1f;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// The bend goal Transform.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Transform bendGoal;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Used to record rotation of the last bone for one frame. 
							 | 
						||
| 
								 | 
							
										/// If MaintainRotation is not called and maintainRotationWeight > 0, the solver will maintain the rotation of the last bone as it was before solving the %IK.
							 | 
						||
| 
								 | 
							
										/// You will probably need this if you wanted to maintain the animated rotation of a foot despite of any other %IK solver that manipulates its parents' rotation.
							 | 
						||
| 
								 | 
							
										/// So you would call %MaintainRotation() in LateUpdate() after animation and before updating the Spine %IK solver that would change the foot's rotation.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public void MaintainRotation() {
							 | 
						||
| 
								 | 
							
											if (!initiated) return;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											maintainRotation = bone3.transform.rotation;
							 | 
						||
| 
								 | 
							
											maintainRotationFor1Frame = true;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// If Auto Bend is on "Animation', %MaintainBend() can be used to set the bend axis relative to the first bone's rotation.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public void MaintainBend() {
							 | 
						||
| 
								 | 
							
											if (!initiated) return;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											animationNormal = bone1.GetBendNormalFromCurrentRotation();
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											maintainBendFor1Frame = true;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Automatic bend modes.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[System.Serializable]
							 | 
						||
| 
								 | 
							
										public enum BendModifier {
							 | 
						||
| 
								 | 
							
											Animation, // Bending relative to the animated rotation of the first bone
							 | 
						||
| 
								 | 
							
											Target, // Bending relative to IKRotation
							 | 
						||
| 
								 | 
							
											Parent, // Bending relative to parentBone
							 | 
						||
| 
								 | 
							
											Arm, // Arm modifier tries to find the most biometrically natural and relaxed arm bend plane
							 | 
						||
| 
								 | 
							
											Goal // Use the bend goal Transform
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										#endregion Main Interface
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Override before Initiate
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										protected override void OnInitiateVirtual() {
							 | 
						||
| 
								 | 
							
											defaultRootRotation = root.rotation;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (bone1.transform.parent != null) {
							 | 
						||
| 
								 | 
							
												parentDefaultRotation = Quaternion.Inverse(defaultRootRotation) * bone1.transform.parent.rotation;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (bone3.rotationLimit != null) bone3.rotationLimit.Disable();
							 | 
						||
| 
								 | 
							
											bone3DefaultRotation = bone3.transform.rotation;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Set bend plane to current (cant use the public SetBendPlaneToCurrent() method here because the solver has not initiated yet)
							 | 
						||
| 
								 | 
							
											Vector3 normal = Vector3.Cross(bone2.transform.position - bone1.transform.position, bone3.transform.position - bone2.transform.position);
							 | 
						||
| 
								 | 
							
											if (normal != Vector3.zero) bendNormal = normal;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											animationNormal = bendNormal;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											StoreAxisDirections(ref axisDirectionsLeft);
							 | 
						||
| 
								 | 
							
											StoreAxisDirections(ref axisDirectionsRight);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Changing stuff in Update() before solving
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										protected override void OnUpdateVirtual() {
							 | 
						||
| 
								 | 
							
											if (IKPositionWeight > 0) {
							 | 
						||
| 
								 | 
							
												// Clamping weights
							 | 
						||
| 
								 | 
							
												bendModifierWeight = Mathf.Clamp(bendModifierWeight, 0f, 1f);
							 | 
						||
| 
								 | 
							
												maintainRotationWeight = Mathf.Clamp(maintainRotationWeight, 0f, 1f);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Storing the bendNormal for reverting after solving
							 | 
						||
| 
								 | 
							
												_bendNormal = bendNormal;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Modifying bendNormal
							 | 
						||
| 
								 | 
							
												bendNormal = GetModifiedBendNormal();
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											if (maintainRotationWeight * IKPositionWeight > 0) {
							 | 
						||
| 
								 | 
							
												// Storing bone3 rotation
							 | 
						||
| 
								 | 
							
												bone3RotationBeforeSolve = maintainRotationFor1Frame? maintainRotation : bone3.transform.rotation;
							 | 
						||
| 
								 | 
							
												maintainRotationFor1Frame = false;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Changing stuff in Update() after solving
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										protected override void OnPostSolveVirtual() {
							 | 
						||
| 
								 | 
							
											// Revert bendNormal to what it was before solving
							 | 
						||
| 
								 | 
							
											if (IKPositionWeight > 0) bendNormal = _bendNormal;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Auto rotation modes
							 | 
						||
| 
								 | 
							
											if (maintainRotationWeight * IKPositionWeight > 0) {
							 | 
						||
| 
								 | 
							
												bone3.transform.rotation = Quaternion.Slerp(bone3.transform.rotation, bone3RotationBeforeSolve, maintainRotationWeight * IKPositionWeight);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Axis direction contains an arm bend axis for a specific IKPosition direction from the first bone. Used in Arm BendModifier mode.
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										[System.Serializable]
							 | 
						||
| 
								 | 
							
										public struct AxisDirection {
							 | 
						||
| 
								 | 
							
											public Vector3 direction;
							 | 
						||
| 
								 | 
							
											public Vector3 axis;
							 | 
						||
| 
								 | 
							
											public float dot;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											public AxisDirection(Vector3 direction, Vector3 axis) {
							 | 
						||
| 
								 | 
							
												this.direction = direction.normalized;
							 | 
						||
| 
								 | 
							
												this.axis = axis.normalized;
							 | 
						||
| 
								 | 
							
												this.dot = 0;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										public IKSolverLimb() {}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										public IKSolverLimb(AvatarIKGoal goal) {
							 | 
						||
| 
								 | 
							
											this.goal = goal;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										private bool maintainBendFor1Frame, maintainRotationFor1Frame;
							 | 
						||
| 
								 | 
							
										private Quaternion defaultRootRotation, parentDefaultRotation, bone3RotationBeforeSolve, maintainRotation, bone3DefaultRotation;
							 | 
						||
| 
								 | 
							
										private Vector3 _bendNormal, animationNormal;
							 | 
						||
| 
								 | 
							
										private AxisDirection[] axisDirectionsLeft = new AxisDirection[4];
							 | 
						||
| 
								 | 
							
										private AxisDirection[] axisDirectionsRight = new AxisDirection[4];
							 | 
						||
| 
								 | 
							
										private AxisDirection[] axisDirections {
							 | 
						||
| 
								 | 
							
											get {
							 | 
						||
| 
								 | 
							
												if (goal == AvatarIKGoal.LeftHand) return axisDirectionsLeft;
							 | 
						||
| 
								 | 
							
												return axisDirectionsRight;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Storing Bend axes for arm directions. The directions and axes here were found empirically, feel free to edit, add or remove.
							 | 
						||
| 
								 | 
							
										 * All vectors are in default character rotation space, where x is right and z is forward.
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										private void StoreAxisDirections(ref AxisDirection[] axisDirections) {
							 | 
						||
| 
								 | 
							
											axisDirections[0] = new AxisDirection(Vector3.zero, new Vector3(-1f, 0f, 0f)); // default
							 | 
						||
| 
								 | 
							
											axisDirections[1] = new AxisDirection(new Vector3(0.5f, 0f, -0.2f), new Vector3(-0.5f, -1f, 1f)); // behind head
							 | 
						||
| 
								 | 
							
											axisDirections[2] = new AxisDirection(new Vector3(-0.5f, -1f, -0.2f), new Vector3(0f, 0.5f, -1f)); // arm twist
							 | 
						||
| 
								 | 
							
											axisDirections[3] = new AxisDirection(new Vector3(-0.5f, -0.5f, 1f), new Vector3(-1f, -1f, -1f)); // cross heart
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Modifying bendNormal
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										private Vector3 GetModifiedBendNormal() {
							 | 
						||
| 
								 | 
							
											float weight = bendModifierWeight;
							 | 
						||
| 
								 | 
							
											if (weight <= 0) return bendNormal;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											switch(bendModifier) {
							 | 
						||
| 
								 | 
							
											// Animation Bend Mode attempts to maintain the bend axis as it is in the animation
							 | 
						||
| 
								 | 
							
											case BendModifier.Animation:
							 | 
						||
| 
								 | 
							
												if (!maintainBendFor1Frame) MaintainBend();
							 | 
						||
| 
								 | 
							
												maintainBendFor1Frame = false;
							 | 
						||
| 
								 | 
							
												return Vector3.Lerp(bendNormal, animationNormal, weight);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
											// Bending relative to the parent of the first bone
							 | 
						||
| 
								 | 
							
											case BendModifier.Parent:
							 | 
						||
| 
								 | 
							
												if (bone1.transform.parent == null) return bendNormal;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												Quaternion parentRotation = bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation);
							 | 
						||
| 
								 | 
							
												return Quaternion.Slerp(Quaternion.identity, parentRotation * Quaternion.Inverse(defaultRootRotation), weight) * bendNormal;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
											// Bending relative to IKRotation
							 | 
						||
| 
								 | 
							
											case BendModifier.Target:
							 | 
						||
| 
								 | 
							
												Quaternion targetRotation = IKRotation * Quaternion.Inverse(bone3DefaultRotation);
							 | 
						||
| 
								 | 
							
												return Quaternion.Slerp(Quaternion.identity, targetRotation, weight) * bendNormal;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Anatomic Arm
							 | 
						||
| 
								 | 
							
											case BendModifier.Arm:
							 | 
						||
| 
								 | 
							
												if (bone1.transform.parent == null) return bendNormal;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Disabling this for legs
							 | 
						||
| 
								 | 
							
												if (goal == AvatarIKGoal.LeftFoot || goal == AvatarIKGoal.RightFoot) {
							 | 
						||
| 
								 | 
							
													if (!Warning.logged) LogWarning("Trying to use the 'Arm' bend modifier on a leg.");
							 | 
						||
| 
								 | 
							
													return bendNormal;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												Vector3 direction = (IKPosition - bone1.transform.position).normalized;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Converting direction to default world space
							 | 
						||
| 
								 | 
							
												direction = Quaternion.Inverse(bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation)) * direction;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Inverting direction for left hand
							 | 
						||
| 
								 | 
							
												if (goal == AvatarIKGoal.LeftHand) direction.x = -direction.x;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Calculating dot products for all AxisDirections
							 | 
						||
| 
								 | 
							
												for (int i = 1; i < axisDirections.Length; i++) {
							 | 
						||
| 
								 | 
							
													axisDirections[i].dot = Mathf.Clamp(Vector3.Dot(axisDirections[i].direction, direction), 0f, 1f);
							 | 
						||
| 
								 | 
							
													axisDirections[i].dot = Interp.Float(axisDirections[i].dot, InterpolationMode.InOutQuintic);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Summing up the arm bend axis
							 | 
						||
| 
								 | 
							
												Vector3 sum = axisDirections[0].axis;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												//for (int i = 1; i < axisDirections.Length; i++) sum = Vector3.Lerp(sum, axisDirections[i].axis, axisDirections[i].dot);
							 | 
						||
| 
								 | 
							
												for (int i = 1; i < axisDirections.Length; i++) sum = Vector3.Slerp(sum, axisDirections[i].axis, axisDirections[i].dot);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Inverting sum for left hand
							 | 
						||
| 
								 | 
							
												if (goal == AvatarIKGoal.LeftHand) {
							 | 
						||
| 
								 | 
							
													sum.x = -sum.x;
							 | 
						||
| 
								 | 
							
													sum = -sum;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Converting sum back to parent space
							 | 
						||
| 
								 | 
							
												Vector3 armBendNormal = bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation) * sum;
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												if (weight >= 1) return armBendNormal;
							 | 
						||
| 
								 | 
							
												return Vector3.Lerp(bendNormal, armBendNormal, weight);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Bending towards the bend goal Transform
							 | 
						||
| 
								 | 
							
											case BendModifier.Goal:
							 | 
						||
| 
								 | 
							
												if (bendGoal == null) {
							 | 
						||
| 
								 | 
							
													if (!Warning.logged) LogWarning("Trying to use the 'Goal' Bend Modifier, but the Bend Goal is unassigned.");
							 | 
						||
| 
								 | 
							
													return bendNormal;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												Vector3 normal = Vector3.Cross(bendGoal.position - bone1.transform.position, IKPosition - bone1.transform.position);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if (normal == Vector3.zero) return bendNormal;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if (weight >= 1f) return normal;
							 | 
						||
| 
								 | 
							
												return Vector3.Lerp(bendNormal, normal, weight);
							 | 
						||
| 
								 | 
							
											default: return bendNormal;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |