阶段性完成

This commit is contained in:
SoulliesOfficial
2025-12-08 05:27:53 -05:00
parent ef7b479712
commit f7af60351b
8770 changed files with 15637030 additions and 208354 deletions

View File

@@ -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); }
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e29c94d3cd70634bb6c45c4bba17847
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ddd02cd37415abc47bd58f3e372c9399
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 13282c710c3d16049889a138542eaaea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68d0934cf641a424cafdbba6ac73a4e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6d778043df1faf41a59d7a14401ea16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: