using System;
using UnityEngine;
namespace FIMSpace.FTools
{
// TODO -> Limiting, Weights, Goal Modes
///
/// FC: Class for processing IK logics for 3-bones inverse kinematics
///
[System.Serializable]
public partial class FimpIK_Limb : FIK_ProcessorBase
{
[NonSerialized][Tooltip("3-Bones limb array")] private IKBone[] IKBones;
[Tooltip("Blend value for goal position")][Space(4)][Range(0f, 1f)] public float IKPositionWeight = 1f;
[Tooltip("Blend value for end bone rotation")][Range(0f, 1f)] public float FootRotationWeight = 1f;
[Tooltip("Flex style algorithm for different limbs")] public FIK_HintMode AutoHintMode = FIK_HintMode.MiddleForward;
private Vector3 targetElbowNormal = Vector3.right;
private Quaternion lateEndBoneRotation;
private Quaternion postIKAnimatorEndBoneRot;
/// For custom slight adjustements of the IK knee/elbow hints
public Vector3 ExtraHintAdjustementOffset = Vector3.zero;
/// Updating processor with 3-bones oriented inverse kinematics
public override void Update()
{
if (!Initialized) return;
Refresh();
// Foot IK Position ---------------------------------------------------
float posWeight = IKPositionWeight * IKWeight;
StartIKBone.sqrMagn = (MiddleIKBone.transform.position - StartIKBone.transform.position).sqrMagnitude;
MiddleIKBone.sqrMagn = (EndIKBone.transform.position - MiddleIKBone.transform.position).sqrMagnitude;
targetElbowNormal = GetDefaultFlexNormal();
if (ExtraHintAdjustementOffset != Vector3.zero)
{
targetElbowNormal = Vector3.Lerp( targetElbowNormal, CalculateElbowNormalToPosition(EndIKBone.transform.position + EndIKBone.transform.rotation * ExtraHintAdjustementOffset), ExtraHintAdjustementOffset.magnitude).normalized;
}
Vector3 orientationDirection = GetOrientationDirection(IKTargetPosition, targetElbowNormal);
if (orientationDirection == Vector3.zero) orientationDirection = MiddleIKBone.transform.position - StartIKBone.transform.position;
if (posWeight > 0f)
{
Quaternion sBoneRot = StartIKBone.GetRotation(orientationDirection, targetElbowNormal) * StartBoneRotationOffset;
if (posWeight < 1f) sBoneRot = Quaternion.LerpUnclamped(StartIKBone.srcRotation, sBoneRot, posWeight);
StartIKBone.transform.rotation = sBoneRot;
Quaternion sMidBoneRot = MiddleIKBone.GetRotation(IKTargetPosition - MiddleIKBone.transform.position, MiddleIKBone.GetCurrentOrientationNormal());
if (posWeight < 1f) sMidBoneRot = Quaternion.LerpUnclamped(MiddleIKBone.srcRotation, sMidBoneRot, posWeight);
MiddleIKBone.transform.rotation = sMidBoneRot;
}
postIKAnimatorEndBoneRot = EndIKBone.transform.rotation;
EndBoneRotation();
}
///
/// Calculating IK pole position normal for desired flexing bend
///
private Vector3 GetAutomaticFlexNormal()
{
Vector3 bendNormal = StartIKBone.GetCurrentOrientationNormal();
switch (AutoHintMode)
{
case FIK_HintMode.Leg:
Vector3 offsets = IKTargetRotation * (EndIKBone.forward * internalRotationWeightMul * 2f);
if (hasRoot)
{
offsets += Root.forward * 0.06f;
Vector3 toGoal = Root.InverseTransformPoint( IKTargetPosition);
toGoal.y = 0f;
offsets += (Root.TransformPoint(toGoal) - Root.position) * 0.025f;
}
float refScale = Vector3.Distance(MiddleIKBone.transform.position, EndIKBone.transform.position) * 0.1f;
Vector3 legHint = CalculateElbowNormalToPosition(MiddleIKBone.srcPosition + offsets * refScale);
return Vector3.LerpUnclamped(bendNormal.normalized, legHint, 0.85f);
case FIK_HintMode.MiddleForward: return Vector3.LerpUnclamped(bendNormal.normalized, MiddleIKBone.srcRotation * MiddleIKBone.right, 0.5f);
case FIK_HintMode.MiddleBack: return MiddleIKBone.srcRotation * -MiddleIKBone.right;
case FIK_HintMode.EndForward:
Vector3 hintPos = MiddleIKBone.srcPosition + EndIKBone.srcRotation * EndIKBone.forward;
Vector3 normal = Vector3.Cross(hintPos - StartIKBone.srcPosition, IKTargetPosition - StartIKBone.srcPosition);
if (normal == Vector3.zero) return bendNormal;
return normal;
case FIK_HintMode.OnGoal: return Vector3.LerpUnclamped(bendNormal, lateEndBoneRotation * EndIKBone.right, 0.5f);
}
return bendNormal;
}
// Drawing helper gizmos for identifying IK process and setup
public void OnDrawGizmos()
{
if (!Initialized) return;
}
}
}