阶段性完成
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Limb : FIK_ProcessorBase
|
||||
{
|
||||
public class IKBone : FIK_IKBoneBase
|
||||
{
|
||||
[SerializeField] private Quaternion targetToLocalSpace;
|
||||
[SerializeField] private Vector3 defaultLocalPoleNormal;
|
||||
|
||||
public Vector3 right { get; private set; }
|
||||
public Vector3 up { get; private set; }
|
||||
public Vector3 forward { get; private set; }
|
||||
|
||||
public Vector3 srcPosition { get; private set; }
|
||||
public Quaternion srcRotation { get; private set; }
|
||||
|
||||
public IKBone(Transform t) : base(t) { }
|
||||
|
||||
public void Init(Transform root, Vector3 childPosition, Vector3 orientationNormal)
|
||||
{
|
||||
RefreshOrientations(childPosition, orientationNormal);
|
||||
|
||||
sqrMagn = (childPosition - transform.position).sqrMagnitude;
|
||||
LastKeyLocalRotation = transform.localRotation;
|
||||
|
||||
right = transform.InverseTransformDirection(root.right);
|
||||
up = transform.InverseTransformDirection(root.up);
|
||||
forward = transform.InverseTransformDirection(root.forward);
|
||||
|
||||
CaptureSourceAnimation();
|
||||
}
|
||||
|
||||
public void RefreshOrientations(Vector3 childPosition, Vector3 orientationNormal)
|
||||
{
|
||||
if (orientationNormal == Vector3.zero) return;
|
||||
|
||||
Vector3 dir = childPosition - transform.position;
|
||||
dir.Normalize();
|
||||
if (dir == Vector3.zero) return;
|
||||
|
||||
Quaternion defaultTargetRotation = Quaternion.LookRotation(dir, orientationNormal);
|
||||
targetToLocalSpace = RotationToLocal(transform.rotation, defaultTargetRotation);
|
||||
defaultLocalPoleNormal = Quaternion.Inverse(transform.rotation) * orientationNormal;
|
||||
}
|
||||
|
||||
public void CaptureSourceAnimation()
|
||||
{
|
||||
srcPosition = transform.position;
|
||||
srcRotation = transform.rotation;
|
||||
}
|
||||
|
||||
public static Quaternion RotationToLocal(Quaternion parent, Quaternion rotation)
|
||||
{ return Quaternion.Inverse(Quaternion.Inverse(parent) * rotation); }
|
||||
|
||||
public Quaternion GetRotation(Vector3 direction, Vector3 orientationNormal)
|
||||
{ return Quaternion.LookRotation(direction, orientationNormal) * targetToLocalSpace; }
|
||||
|
||||
public Vector3 GetCurrentOrientationNormal()
|
||||
{ return transform.rotation * (defaultLocalPoleNormal); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e29c94d3cd70634bb6c45c4bba17847
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Limb : FIK_ProcessorBase
|
||||
{
|
||||
// Foot/End Bone rotation helper with root reference
|
||||
public Quaternion EndBoneMapping { get; protected set; }
|
||||
public IKBone FeetIKBone { get { return IKBones[3]; } }
|
||||
|
||||
|
||||
/// <summary> Assigning helpful reference to main root transform of body to help IK rotations </summary>
|
||||
public virtual void SetRootReference(Transform mainParentTransform)
|
||||
{
|
||||
Root = mainParentTransform;
|
||||
EndBoneMapping = Quaternion.FromToRotation(EndIKBone.right, Vector3.right);
|
||||
EndBoneMapping *= Quaternion.FromToRotation(EndIKBone.up, Vector3.up);
|
||||
if (mainParentTransform) hasRoot = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Reference scale for computations - active length from start bone to middle knee </summary>
|
||||
public float ScaleReference { get; protected set; }
|
||||
|
||||
public void RefreshLength()
|
||||
{
|
||||
ScaleReference = (StartIKBone.transform.position - MiddleIKBone.transform.position).magnitude;
|
||||
}
|
||||
|
||||
public void RefreshScaleReference()
|
||||
{
|
||||
ScaleReference = (StartIKBone.transform.position - MiddleIKBone.transform.position).magnitude;
|
||||
}
|
||||
|
||||
float GetCurrentLegToAnkleLength()
|
||||
{
|
||||
float fullLength = Mathf.Epsilon;
|
||||
fullLength += (StartIKBone.transform.position - MiddleIKBone.transform.position).magnitude;
|
||||
fullLength += (MiddleIKBone.transform.position - EndIKBone.transform.position).magnitude;
|
||||
return fullLength;
|
||||
}
|
||||
|
||||
/// <summary> Returning >= 1f when max range for IK point is reached </summary>
|
||||
public float GetStretchValue(Vector3 targetPos)
|
||||
{
|
||||
float toGoal = (StartIKBone.transform.position - targetPos).magnitude;
|
||||
return toGoal / GetCurrentLegToAnkleLength();
|
||||
}
|
||||
|
||||
public Vector3 GetNotStretchedPositionTowards(Vector3 targetPos, float maxStretch)
|
||||
{
|
||||
Vector3 toGoal = (targetPos - StartIKBone.transform.position);
|
||||
return StartIKBone.transform.position + toGoal.normalized * (GetCurrentLegToAnkleLength() * maxStretch);
|
||||
}
|
||||
|
||||
public void ApplyMaxStretchingPreprocessing(float maxStretch, float allowIKRotationFadeout = 2f)
|
||||
{
|
||||
if (maxStretch < 1.1f)
|
||||
{
|
||||
|
||||
float toGoal = (StartIKBone.transform.position - IKTargetPosition).magnitude;
|
||||
float limbUnitLength = GetCurrentLegToAnkleLength();
|
||||
float stretch = toGoal / limbUnitLength;
|
||||
|
||||
if (stretch > maxStretch)
|
||||
{
|
||||
|
||||
if (hasFeet && FeetStretchWeight > 0f)
|
||||
{
|
||||
|
||||
#region Feet stretch helper
|
||||
|
||||
if (maxFeetAngle > 0f)
|
||||
{
|
||||
// Feet angle factor helpers
|
||||
Vector3 thighToTarget = IKTargetPosition - StartIKBone.transform.position;
|
||||
thighToTarget.Normalize();
|
||||
Vector3 ankleToFeet = FeetIKBone.transform.position - EndIKBone.transform.position;
|
||||
ankleToFeet.Normalize();
|
||||
|
||||
float feetDot = Vector3.Dot(thighToTarget, ankleToFeet);
|
||||
feetDot = Mathf.Clamp01(feetDot);
|
||||
|
||||
// Feet bone rotation helpers
|
||||
float feetLength = (FeetIKBone.transform.position - EndIKBone.transform.position).magnitude;
|
||||
float stretchDiff = toGoal - limbUnitLength * Mathf.Min(maxStretch, 1f);
|
||||
stretchDiff /= (feetLength * FeetFadeQuicker);
|
||||
float stretchDiff2 = stretchDiff;
|
||||
stretchDiff *= maxFeetAngleFactor * FeetStretchSensitivity;
|
||||
if (stretchDiff > 1f) stretchDiff = 1f;
|
||||
|
||||
if (stretchDiff2 < 1f) stretchDiff2 = 1f; else { if (stretchDiff2 > 2f) stretchDiff2 = 2f; stretchDiff2 -= 1f; stretchDiff2 *= stretchDiff2; stretchDiff2 = 1f - stretchDiff2; }
|
||||
|
||||
// Apply
|
||||
float heelFactor = Mathf.Min(FeetStretchLimit, (1f - feetDot) * (90f / maxFeetAngle) * stretchDiff * FeetStretchWeight);
|
||||
|
||||
if (stretch > 1.09f)
|
||||
{
|
||||
stretchDiff2 *= 1f - Mathf.InverseLerp(1.09f, 1.23f, stretch);
|
||||
}
|
||||
|
||||
if (heelFactor != 0f) OffsetHeel(heelFactor, stretchDiff2);
|
||||
|
||||
// Recompute
|
||||
toGoal = (StartIKBone.transform.position - IKTargetPosition).magnitude;
|
||||
stretch = toGoal / limbUnitLength;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
if (stretch > maxStretch)
|
||||
{
|
||||
float len = (maxStretch * limbUnitLength);
|
||||
IKTargetPosition = StartIKBone.transform.position + (IKTargetPosition - StartIKBone.transform.position).normalized * len;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
float len = (maxStretch * limbUnitLength);
|
||||
IKTargetPosition = StartIKBone.transform.position + (IKTargetPosition - StartIKBone.transform.position).normalized * len;
|
||||
}
|
||||
|
||||
if (allowIKRotationFadeout > 0f)
|
||||
{
|
||||
float stretchDiff = stretch - maxStretch;
|
||||
stretchDiff = Mathf.Clamp01(stretchDiff * allowIKRotationFadeout);
|
||||
internalRotationWeightMul = (1f - stretchDiff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
internalRotationWeightMul = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Prepare Feet
|
||||
|
||||
[NonSerialized] public float FeetStretchWeight = 1f;
|
||||
[NonSerialized] public float FeetStretchSensitivity = 1f;
|
||||
[NonSerialized] public float FeetStretchLimit = 1f;
|
||||
[NonSerialized] public float FeetFadeQuicker = 1f;
|
||||
[NonSerialized] public bool disableFeet = false;
|
||||
float maxFeetAngle = 0f;
|
||||
float maxFeetAngleFactor = 0f;
|
||||
Vector3 ankleToFeet;
|
||||
void PrepareFeet()
|
||||
{
|
||||
Vector3 kneeToAnkle = EndIKBone.transform.position - MiddleIKBone.transform.position;
|
||||
kneeToAnkle.Normalize();
|
||||
|
||||
ankleToFeet = FeetIKBone.transform.position - EndIKBone.transform.position;
|
||||
ankleToFeet.Normalize();
|
||||
|
||||
maxFeetAngle = Vector3.Angle(ankleToFeet, kneeToAnkle);
|
||||
maxFeetAngleFactor = 90f / maxFeetAngle;
|
||||
|
||||
//if ( Root == null)
|
||||
//{
|
||||
// UnityEngine.Debug.Log("[IK] Feet requires Root Transform defined!");
|
||||
// hasFeet = false;
|
||||
//}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
internal void OffsetHeel(float heelRot, float feetCompensate = 1f)
|
||||
{
|
||||
if (hasFeet == false) return;
|
||||
if (disableFeet) return;
|
||||
|
||||
Quaternion preAnkleRot = IKTargetRotation;
|
||||
Vector3 toFeet = (FeetIKBone.transform.position - EndIKBone.transform.position);
|
||||
|
||||
Vector3 rotatedOffset = Quaternion.Inverse(preAnkleRot) * (toFeet);
|
||||
|
||||
Vector3 rightAxis;
|
||||
if (UseEndBoneMapping) rightAxis = IKTargetRotation * Vector3.right;
|
||||
else rightAxis = IKTargetRotation * (EndIKBone.right);// Root.right;
|
||||
|
||||
Quaternion rotationOffset = Quaternion.AngleAxis(heelRot * maxFeetAngle, rightAxis);
|
||||
Quaternion newAnkleRot = rotationOffset * preAnkleRot;
|
||||
|
||||
IKTargetRotation = newAnkleRot;
|
||||
|
||||
Vector3 newOffset = newAnkleRot * rotatedOffset;
|
||||
rotatedOffset = newOffset - toFeet;
|
||||
|
||||
if (feetCompensate > 0f)
|
||||
{
|
||||
Quaternion newFeetRot = Quaternion.Inverse(rotationOffset) * (FeetIKBone.transform.rotation);
|
||||
|
||||
if (feetCompensate >= 1f)
|
||||
FeetIKBone.transform.rotation = newFeetRot;
|
||||
else
|
||||
FeetIKBone.transform.rotation = Quaternion.Lerp(FeetIKBone.transform.rotation, newFeetRot, feetCompensate);
|
||||
}
|
||||
|
||||
IKTargetPosition -= rotatedOffset;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddd02cd37415abc47bd58f3e372c9399
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Limb : FIK_ProcessorBase
|
||||
{
|
||||
public Transform Root { get; protected set; }
|
||||
public IKBone StartIKBone { get { return IKBones[0]; } }
|
||||
public IKBone MiddleIKBone { get { return IKBones[1]; } }
|
||||
public IKBone EndIKBone { get { return IKBones[2]; } }
|
||||
|
||||
/// <summary> If there is bone between end and middle bone, it's initial info is stored there, otherwise it's simply MiddleIKBone</summary>
|
||||
public IKBone EndParentIKBone { get; private set; }
|
||||
|
||||
public IKBone GetBone(int index) { return IKBones[index]; }
|
||||
public int BonesCount { get { return IKBones.Length; } }
|
||||
|
||||
public enum FIK_HintMode { Default, MiddleForward, MiddleBack, OnGoal, EndForward, Leg, UnityHumanoidIK }
|
||||
/// <summary> Required for UnityHumanoidIK hint mode </summary>
|
||||
[NonSerialized] public Animator HumanoidAnimator;
|
||||
/// <summary> Required for UnityHumanoidIK hint mode </summary>
|
||||
[NonSerialized] public bool IsRight;
|
||||
|
||||
private bool everyIsChild = true;
|
||||
private bool hasFeet = false;
|
||||
private bool hasRoot = false;
|
||||
|
||||
public override void Init(Transform root)
|
||||
{
|
||||
if (Initialized) return;
|
||||
|
||||
Vector3 preNormal = Vector3.Cross(MiddleIKBone.transform.position - StartIKBone.transform.position, EndIKBone.transform.position - MiddleIKBone.transform.position);
|
||||
if (preNormal != Vector3.zero) targetElbowNormal = preNormal;
|
||||
|
||||
fullLength = 0f;
|
||||
|
||||
StartIKBone.Init(root, MiddleIKBone.transform.position, targetElbowNormal);
|
||||
MiddleIKBone.Init(root, EndIKBone.transform.position, targetElbowNormal);
|
||||
EndIKBone.Init(root, EndIKBone.transform.position + (EndIKBone.transform.position - MiddleIKBone.transform.position), targetElbowNormal);
|
||||
|
||||
fullLength = Bones[0].BoneLength + Bones[1].BoneLength;
|
||||
RefreshDefaultFlexNormal();
|
||||
|
||||
// Checking if bones hierarchy is fully connected and straight forward direct
|
||||
if (EndIKBone.transform.parent != MiddleIKBone.transform) everyIsChild = false;
|
||||
else
|
||||
if (MiddleIKBone.transform.parent != StartIKBone.transform) everyIsChild = false;
|
||||
else everyIsChild = true;
|
||||
|
||||
SetRootReference(root);
|
||||
|
||||
if (Application.isPlaying) Initialized = true;
|
||||
|
||||
if ( hasFeet) PrepareFeet();
|
||||
|
||||
if (everyIsChild) EndParentIKBone = MiddleIKBone;
|
||||
else EndParentIKBone = new IKBone(EndIKBone.transform.parent);
|
||||
}
|
||||
|
||||
public void SetBones(Transform startBone, Transform midBone, Transform endBone)
|
||||
{
|
||||
IKBones = new IKBone[3];
|
||||
IKBones[0] = new IKBone(startBone);
|
||||
IKBones[1] = new IKBone(midBone);
|
||||
IKBones[2] = new IKBone(endBone);
|
||||
|
||||
Bones = new FIK_IKBoneBase[3] { IKBones[0], IKBones[1], IKBones[2] };
|
||||
|
||||
IKBones[0].SetChild(IKBones[1]);
|
||||
IKBones[1].SetChild(IKBones[2]);
|
||||
|
||||
IKTargetPosition = endBone.position; IKTargetRotation = endBone.rotation;
|
||||
}
|
||||
|
||||
|
||||
public void SetLegWithFeet(Transform startBone, Transform midBone, Transform endBone, Transform feet)
|
||||
{
|
||||
IKBones = new IKBone[4];
|
||||
IKBones[0] = new IKBone(startBone);
|
||||
IKBones[1] = new IKBone(midBone);
|
||||
IKBones[2] = new IKBone(endBone);
|
||||
IKBones[3] = new IKBone(feet);
|
||||
|
||||
Bones = new FIK_IKBoneBase[4] { IKBones[0], IKBones[1], IKBones[2], IKBones[3] };
|
||||
|
||||
IKBones[0].SetChild(IKBones[1]);
|
||||
IKBones[1].SetChild(IKBones[2]);
|
||||
IKBones[2].SetChild(IKBones[3]);
|
||||
|
||||
IKTargetPosition = endBone.position; IKTargetRotation = endBone.rotation;
|
||||
|
||||
hasFeet = true;
|
||||
}
|
||||
|
||||
public void SetBones(Transform startBone, Transform endBone)
|
||||
{
|
||||
SetBones(startBone, endBone.parent, endBone);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13282c710c3d16049889a138542eaaea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Limb : FIK_ProcessorBase
|
||||
{
|
||||
[HideInInspector][Range(0f, 1f)] public float ManualHintPositionWeight = 0f;
|
||||
[HideInInspector] public Vector3 IKManualHintPosition = Vector3.zero;
|
||||
|
||||
protected virtual void Refresh()
|
||||
{
|
||||
RefreshAnimatorCoords();
|
||||
|
||||
// If limb have more than 3 point bones then we must update some data for main two bones
|
||||
if (!everyIsChild)
|
||||
{
|
||||
//StartIKBone.RefreshOrientations(MiddleIKBone.transform.position, targetElbowNormal);
|
||||
MiddleIKBone.RefreshOrientations(EndIKBone.transform.position, targetElbowNormal);
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized] public bool UseEndBoneMapping = true;
|
||||
float internalRotationWeightMul = 1f;
|
||||
|
||||
protected virtual void EndBoneRotation()
|
||||
{
|
||||
float rotWeight = FootRotationWeight * IKWeight * internalRotationWeightMul;
|
||||
|
||||
if (rotWeight > 0f)
|
||||
{
|
||||
if (UseEndBoneMapping)
|
||||
{
|
||||
if (rotWeight < 1f)
|
||||
EndIKBone.transform.rotation = Quaternion.SlerpUnclamped(postIKAnimatorEndBoneRot, IKTargetRotation * EndBoneMapping, rotWeight);
|
||||
else
|
||||
EndIKBone.transform.rotation = IKTargetRotation * EndBoneMapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rotWeight < 1f)
|
||||
EndIKBone.transform.rotation = Quaternion.SlerpUnclamped(postIKAnimatorEndBoneRot, IKTargetRotation, rotWeight);
|
||||
else
|
||||
EndIKBone.transform.rotation = IKTargetRotation;
|
||||
}
|
||||
}
|
||||
|
||||
lateEndBoneRotation = EndIKBone.transform.rotation;
|
||||
}
|
||||
|
||||
public override void PreCalibrate()
|
||||
{
|
||||
base.PreCalibrate();
|
||||
RefreshScaleReference();
|
||||
}
|
||||
|
||||
public void RefreshAnimatorCoords()
|
||||
{
|
||||
StartIKBone.CaptureSourceAnimation();
|
||||
MiddleIKBone.CaptureSourceAnimation();
|
||||
EndIKBone.CaptureSourceAnimation();
|
||||
if (!everyIsChild) { if (MiddleIKBone != EndParentIKBone) EndParentIKBone.CaptureSourceAnimation(); }
|
||||
}
|
||||
|
||||
protected Vector3 GetDefaultFlexNormal()
|
||||
{
|
||||
if (ManualHintPositionWeight > 0f)
|
||||
{
|
||||
if (ManualHintPositionWeight >= 1f)
|
||||
return CalculateElbowNormalToPosition(IKManualHintPosition);
|
||||
else
|
||||
return Vector3.LerpUnclamped(GetAutomaticFlexNormal().normalized, CalculateElbowNormalToPosition(IKManualHintPosition), ManualHintPositionWeight);
|
||||
}
|
||||
else
|
||||
return GetAutomaticFlexNormal();
|
||||
}
|
||||
|
||||
|
||||
public Vector3 CalculateElbowNormalToPosition(Vector3 targetElbowPos)
|
||||
{
|
||||
return Vector3.Cross(targetElbowPos - StartIKBone.transform.position, EndIKBone.transform.position - StartIKBone.transform.position);
|
||||
}
|
||||
|
||||
|
||||
public void RefreshDefaultFlexNormal()
|
||||
{
|
||||
Vector3 normal = Vector3.Cross(MiddleIKBone.transform.position - StartIKBone.transform.position, EndIKBone.transform.position - MiddleIKBone.transform.position);
|
||||
if (normal != Vector3.zero) targetElbowNormal = normal;
|
||||
}
|
||||
|
||||
|
||||
protected Vector3 GetOrientationDirection(Vector3 ikPosition, Vector3 orientationNormal)
|
||||
{
|
||||
Vector3 direction = ikPosition - StartIKBone.transform.position; // From start bone to target ik position
|
||||
if (direction == Vector3.zero) return Vector3.zero;
|
||||
|
||||
float distSqrStartToGoal = direction.sqrMagnitude; // Computing length for bones
|
||||
float distStartToGoal = Mathf.Sqrt(distSqrStartToGoal);
|
||||
|
||||
float forwardLen = (distSqrStartToGoal + StartIKBone.sqrMagn - MiddleIKBone.sqrMagn) / 2f / distStartToGoal;
|
||||
float upLen = Mathf.Sqrt(Mathf.Clamp(StartIKBone.sqrMagn - forwardLen * forwardLen, 0, Mathf.Infinity));
|
||||
|
||||
Vector3 perpendicularUp = Vector3.Cross(direction / distStartToGoal, orientationNormal);
|
||||
|
||||
return Quaternion.LookRotation(direction, perpendicularUp) * new Vector3(0f, upLen, forwardLen);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68d0934cf641a424cafdbba6ac73a4e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
// TODO -> Limiting, Weights, Goal Modes
|
||||
|
||||
/// <summary>
|
||||
/// FC: Class for processing IK logics for 3-bones inverse kinematics
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
|
||||
protected Vector3 targetElbowNormal = Vector3.right;
|
||||
protected Quaternion lateEndBoneRotation;
|
||||
protected Quaternion postIKAnimatorEndBoneRot;
|
||||
|
||||
/// <summary> For custom slight adjustements of the IK knee/elbow hints </summary>
|
||||
public Vector3 ExtraHintAdjustementOffset = Vector3.zero;
|
||||
/// <summary> Inverse direction of default calculated hint position </summary>
|
||||
public bool InverseHint = false;
|
||||
|
||||
/// <summary> Updating processor with 3-bones oriented inverse kinematics </summary>
|
||||
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, InverseHint ? -targetElbowNormal : 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();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculating IK pole position normal for desired flexing bend
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
case FIK_HintMode.UnityHumanoidIK:
|
||||
if (HumanoidAnimator) return CalculateElbowNormalToPosition( HumanoidAnimator.GetIKHintPosition(IsRight ? AvatarIKHint.RightKnee : AvatarIKHint.LeftKnee) );
|
||||
break;
|
||||
}
|
||||
|
||||
return bendNormal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Drawing helper gizmos for identifying IK process and setup
|
||||
public void OnDrawGizmos()
|
||||
{
|
||||
if (!Initialized) return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6d778043df1faf41a59d7a14401ea16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user