阶段性完成
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
|
||||
public partial class FimpIK_Arm
|
||||
{
|
||||
private Vector3 lastIKBasePosition;
|
||||
private Quaternion lastIKBaseRotation;
|
||||
|
||||
public Vector3 IKTargetPosition;
|
||||
public Quaternion IKTargetRotation;
|
||||
|
||||
/// <summary> Length of whole bones chain (squared and not scalled) </summary>
|
||||
public float FullLength { get; protected set; }
|
||||
|
||||
public bool Initialized { get; set; }
|
||||
|
||||
public void PreCalibrate(float blend = 1f)
|
||||
{
|
||||
IKBone child = IKBones[0];
|
||||
|
||||
if (blend >= 1f)
|
||||
{
|
||||
while (child != null)
|
||||
{
|
||||
child.transform.localRotation = child.InitialLocalRotation;
|
||||
child = child.Child;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (child != null)
|
||||
{
|
||||
child.transform.localRotation = Quaternion.LerpUnclamped(child.transform.localRotation, child.InitialLocalRotation, blend);
|
||||
child = child.Child;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshScaleReference();
|
||||
}
|
||||
|
||||
private float sd_ikBlend = 0f;
|
||||
public void User_SmoothIKBlend(float target, float duration, float delta, float maxSpeed = 1000f)
|
||||
{
|
||||
IKWeight = Mathf.SmoothDamp(IKWeight, target, ref sd_ikBlend, duration, maxSpeed, delta);
|
||||
}
|
||||
|
||||
|
||||
private Vector3 sd_ikTargetPosition = Vector3.zero;
|
||||
public void User_SmoothPositionTowards(Vector3 newIKPos, float duration, float delta, float maxSpeed = 1000f)
|
||||
{
|
||||
IKTargetPosition = Vector3.SmoothDamp(IKTargetPosition, newIKPos, ref sd_ikTargetPosition, duration, maxSpeed, delta);
|
||||
}
|
||||
|
||||
public Vector3 GetHintDefaultPosition()
|
||||
{
|
||||
return ForeArmIKBone.transform.position + ForeArmIKBone.transform.rotation * UpperArmIKBone.GetDefaultPoleNormal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f474b4b6ec8ec5440a2af7c0db0923d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Arm
|
||||
{
|
||||
// Foot/End Bone rotation helper with root reference
|
||||
public Quaternion HandIKBoneMapping { get; protected set; }
|
||||
public void SetCustomIKRotationMappingOffset(Quaternion mappingCorrection) { HandIKBoneMapping = mappingCorrection; }
|
||||
|
||||
[NonSerialized] public Vector3 HandMiddleOffset;
|
||||
private Vector3 shoulderForward;
|
||||
private UniRotateBone shoulderRotate;
|
||||
private Vector3 initHandRootSpaceFlatTowards;
|
||||
|
||||
internal bool UseRotationMapping = true;
|
||||
|
||||
/// <summary> >= 1.2 is max </summary>
|
||||
[NonSerialized] public float MaxStretching = 1.2f;
|
||||
|
||||
/// <summary> Assigning helpful reference to main root transform of body to help IK rotations </summary>
|
||||
public virtual void SetRootReference(Transform mainParentTransform)
|
||||
{
|
||||
Root = mainParentTransform;
|
||||
Quaternion preRot = Root.transform.rotation;
|
||||
Root.transform.rotation = Quaternion.identity;
|
||||
|
||||
Vector3 handForwardWorld = (HandIKBone.transform.position - ForeArmIKBone.transform.position).normalized;
|
||||
Vector3 handLocalForward = HandIKBone.transform.InverseTransformDirection(handForwardWorld);
|
||||
Vector3 handLocalRight = mainParentTransform.forward;
|
||||
Vector3 handLocalUp = Vector3.Cross(handLocalForward, handLocalRight);
|
||||
|
||||
Vector3 shoulderForwardWorld = (ShoulderIKBone.transform.position - ShoulderIKBone.transform.parent.position).normalized;
|
||||
shoulderForward = ShoulderIKBone.transform.InverseTransformDirection(shoulderForwardWorld);
|
||||
|
||||
HandIKBoneMapping = Quaternion.FromToRotation(handLocalRight, Vector3.right);
|
||||
HandIKBoneMapping *= Quaternion.FromToRotation(handLocalUp, Vector3.up);
|
||||
shoulderRotate = new UniRotateBone(ShoulderTransform, mainParentTransform);
|
||||
Root.transform.rotation = preRot;
|
||||
|
||||
initHandRootSpaceFlatTowards = Root.InverseTransformPoint(HandTransform.position);
|
||||
initHandRootSpaceFlatTowards.y = 0f;
|
||||
initHandRootSpaceFlatTowards.Normalize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Put here any euler rotation (like 0,90,0) which will be mapped for correct hand rotation no matter how bones are rotated in skeleton rig (but root reference needed)
|
||||
/// </summary>
|
||||
/// <param name="rotation"></param>
|
||||
public void SetCustomIKRotation(Quaternion rotation, float blend = 1f, bool fromDefault = false)
|
||||
{
|
||||
if (blend == 1f)
|
||||
{
|
||||
if (UseRotationMapping)
|
||||
IKTargetRotation = rotation * HandIKBoneMapping;
|
||||
else
|
||||
IKTargetRotation = rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UseRotationMapping)
|
||||
{
|
||||
if (fromDefault)
|
||||
IKTargetRotation = Quaternion.LerpUnclamped(IKTargetRotation, rotation * HandIKBoneMapping, blend);
|
||||
else
|
||||
IKTargetRotation = Quaternion.LerpUnclamped(rotation, rotation * HandIKBoneMapping, blend);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fromDefault)
|
||||
IKTargetRotation = Quaternion.LerpUnclamped(IKTargetRotation, rotation, blend);
|
||||
else
|
||||
IKTargetRotation = Quaternion.LerpUnclamped(rotation, rotation, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CaptureKeyframeAnimation()
|
||||
{
|
||||
shoulderRotate.CaptureKeyframeAnimation();
|
||||
|
||||
IKBone child = IKBones[0];
|
||||
while (child != null)
|
||||
{
|
||||
child.CaptureSourceAnimation();
|
||||
child = (IKBone)child.Child;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Reference scale for computations - active length from start bone to middle knee </summary>
|
||||
public float ScaleReference { get; protected set; }
|
||||
|
||||
public void RefreshLength()
|
||||
{
|
||||
ScaleReference = (UpperArmIKBone.transform.position - ForeArmIKBone.transform.position).magnitude;
|
||||
}
|
||||
|
||||
public void RefreshScaleReference()
|
||||
{
|
||||
ScaleReference = (UpperArmIKBone.transform.position - ForeArmIKBone.transform.position).magnitude;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returning >= 1f when max range for IK point is reached </summary>
|
||||
public float GetStretchValue(Vector3 targetPos)
|
||||
{
|
||||
float toGoal = (UpperArmIKBone.transform.position - targetPos).magnitude;
|
||||
return toGoal / limbLength;
|
||||
}
|
||||
|
||||
public float GetStretchValueSrc(Vector3 targetPos)
|
||||
{
|
||||
float toGoal = (UpperArmIKBone.srcPosition - targetPos).magnitude;
|
||||
return toGoal / limbLength;
|
||||
}
|
||||
|
||||
protected virtual void CalculateLimbLength()
|
||||
{
|
||||
limbLength = Mathf.Epsilon;
|
||||
|
||||
//if (ShoulderIKBone.transform)
|
||||
//{
|
||||
// float shouldLen = (ShoulderIKBone.transform.position - UpperArmIKBone.transform.position).magnitude;
|
||||
// limbLength += shouldLen * ShoulderBlend;
|
||||
//}
|
||||
|
||||
limbLength += (UpperArmIKBone.transform.position - ForeArmIKBone.transform.position).magnitude;
|
||||
limbLength += (ForeArmIKBone.transform.position - HandIKBone.transform.position).magnitude;
|
||||
}
|
||||
|
||||
public bool PreventShoulderThirdQuat { get; set; } = true;
|
||||
/// <summary> By default value is 0.75 </summary>
|
||||
public float ShoulderSensitivity { get; set; } = 0.75f;
|
||||
public float PreventShoulderThirdQuatFactor { get; set; } = 0.01f;
|
||||
public float limbLength { get; private set; } = 0.1f;
|
||||
|
||||
// Shoulder -----------------------
|
||||
void ComputeShoulder(Vector3 finalIKPos)
|
||||
{
|
||||
if (!Initialized) return;
|
||||
if (ShoulderBlend <= 0f) return;
|
||||
|
||||
Vector3 toGoal = (finalIKPos - shoulderRotate.transform.position);
|
||||
Quaternion nRot;
|
||||
|
||||
if (Root)
|
||||
{
|
||||
//nRot =(Root.rotation) * Quaternion.Euler(rrr);
|
||||
//nRot *= shoulderRotate.transform.rotation;
|
||||
|
||||
Quaternion preRot = shoulderRotate.transform.rotation;
|
||||
Quaternion q = Quaternion.FromToRotation(Root.InverseTransformDirection(toGoal).normalized, initHandRootSpaceFlatTowards);
|
||||
|
||||
Vector3 mappedRotation = -q.eulerAngles;
|
||||
|
||||
shoulderRotate.RotateXBy(mappedRotation.x);
|
||||
shoulderRotate.RotateYBy(mappedRotation.y);
|
||||
shoulderRotate.RotateZBy(mappedRotation.z);
|
||||
|
||||
nRot = shoulderRotate.transform.rotation;
|
||||
shoulderRotate.transform.rotation = preRot;
|
||||
}
|
||||
else
|
||||
{
|
||||
nRot = (ShoulderIKBone.GetRotation(toGoal.normalized, ShoulderIKBone.srcRotation * shoulderRotate.upReference));
|
||||
}
|
||||
|
||||
float blend = IKWeight * ShoulderBlend;
|
||||
float armStretch = GetStretchValue(finalIKPos);
|
||||
|
||||
armStretch *= 0.85f;
|
||||
if (armStretch > 1f) armStretch = 1f;
|
||||
|
||||
blend *= Mathf.InverseLerp(0.6f, 1f, armStretch) * 0.9f;
|
||||
|
||||
ShoulderIKBone.transform.rotation = Quaternion.Slerp(shoulderRotate.transform.rotation, nRot, blend);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb216e3cea30e5e49b43d367c59d01a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Arm
|
||||
{
|
||||
[System.Serializable]
|
||||
public class IKBone
|
||||
{
|
||||
// Base variables -----------------------
|
||||
public IKBone Child { get; private set; }
|
||||
public Transform transform { get; protected set; }
|
||||
public float sqrMagn = 0.1f;
|
||||
public float BoneLength = 0.1f;
|
||||
public float MotionWeight = 1f;
|
||||
|
||||
public Vector3 InitialLocalPosition;
|
||||
public Quaternion InitialLocalRotation;
|
||||
public Quaternion LastKeyLocalRotation;
|
||||
|
||||
|
||||
// Arm ik specific -----------------------
|
||||
[SerializeField] private Quaternion targetToLocalSpace;
|
||||
[SerializeField] private Vector3 defaultLocalPoleNormal;
|
||||
public Vector3 GetDefaultPoleNormal() { return 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)
|
||||
{
|
||||
if (t == null) return;
|
||||
transform = t;
|
||||
InitialLocalPosition = transform.localPosition;
|
||||
InitialLocalRotation = transform.localRotation;
|
||||
LastKeyLocalRotation = t.localRotation;
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetChild(IKBone child)
|
||||
{
|
||||
if (child.transform == null) return;
|
||||
|
||||
Child = child;
|
||||
sqrMagn = (child.transform.position - transform.position).sqrMagnitude;
|
||||
BoneLength = (child.transform.position - transform.position).magnitude;
|
||||
}
|
||||
|
||||
|
||||
public Vector3 Dir(Vector3 local)
|
||||
{
|
||||
return transform.TransformDirection(local);
|
||||
}
|
||||
|
||||
|
||||
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 (transform == null) return;
|
||||
Vector3 dir = childPosition - transform.position;
|
||||
|
||||
Quaternion defaultTargetRotation;
|
||||
if (dir == Vector3.zero) defaultTargetRotation = Quaternion.identity;
|
||||
else
|
||||
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: d8206dbc5aff4ee468e860be374a777a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Arm
|
||||
{
|
||||
public Transform Root { get; protected set; }
|
||||
|
||||
public IKBone ChestIKBone;
|
||||
public IKBone ShoulderIKBone { get { return IKBones[0]; } }
|
||||
public IKBone UpperArmIKBone { get { return IKBones[1]; } }
|
||||
public IKBone ForeArmIKBone { get { return IKBones[2]; } }
|
||||
public IKBone HandIKBone { get { return IKBones[3]; } }
|
||||
public IKBone GetBone(int index) { return IKBones[index]; }
|
||||
public int BonesCount { get { return IKBones.Length; } }
|
||||
|
||||
public enum FIK_HintMode { Default, MiddleForward, MiddleBack, OnGoal, EndForward }
|
||||
private bool everyIsChild;
|
||||
|
||||
public void Init(Transform root)
|
||||
{
|
||||
if (Initialized) return;
|
||||
if (IKBones == null) return;
|
||||
|
||||
if (IKBones.Length == 0)
|
||||
{
|
||||
SetBones(ShoulderTransform, UpperarmTransform, LowerarmTransform, HandTransform);
|
||||
}
|
||||
|
||||
UpperarmRotationOffset = Quaternion.identity;
|
||||
TargetElbowNormal = Vector3.right;
|
||||
|
||||
Vector3 preNormal = Vector3.Cross(ForeArmIKBone.transform.position - UpperArmIKBone.transform.position, HandIKBone.transform.position - ForeArmIKBone.transform.position);
|
||||
if (preNormal != Vector3.zero) TargetElbowNormal = preNormal;
|
||||
|
||||
FullLength = 0f;
|
||||
|
||||
ShoulderIKBone.Init(root, UpperArmIKBone.transform.position, TargetElbowNormal);
|
||||
UpperArmIKBone.Init(root, ForeArmIKBone.transform.position, TargetElbowNormal);
|
||||
ForeArmIKBone.Init(root, HandIKBone.transform.position, TargetElbowNormal);
|
||||
HandIKBone.Init(root, HandIKBone.transform.position + (HandIKBone.transform.position - ForeArmIKBone.transform.position), TargetElbowNormal);
|
||||
|
||||
FullLength = IKBones[1].BoneLength + IKBones[2].BoneLength;
|
||||
RefreshDefaultFlexNormal();
|
||||
|
||||
// Checking if bones hierarchy is fully connected and straight forward direct
|
||||
if (HandIKBone.transform.parent != ForeArmIKBone.transform) everyIsChild = false;
|
||||
else
|
||||
if (ForeArmIKBone.transform.parent != UpperArmIKBone.transform) everyIsChild = false;
|
||||
else everyIsChild = true;
|
||||
|
||||
ChestIKBone = new IKBone(ShoulderIKBone.transform.parent);
|
||||
ChestIKBone.Init(root, ShoulderIKBone.transform.position, TargetElbowNormal);
|
||||
|
||||
SetRootReference(root);
|
||||
|
||||
// Calculating Hand middle
|
||||
HandMiddleOffset = Vector3.zero;
|
||||
|
||||
if (HandIKBone.transform.childCount > 0)
|
||||
{
|
||||
HandMiddleOffset = HandIKBone.transform.GetChild(0).position;
|
||||
for (int i = 1; i < HandIKBone.transform.childCount; i++)
|
||||
{
|
||||
HandMiddleOffset = Vector3.Lerp(HandMiddleOffset, HandIKBone.transform.GetChild(i).position, 0.5f);
|
||||
}
|
||||
|
||||
HandMiddleOffset = Vector3.Lerp(HandMiddleOffset, HandIKBone.transform.position, 0.4f);
|
||||
HandMiddleOffset = HandIKBone.transform.InverseTransformPoint(HandMiddleOffset);
|
||||
}
|
||||
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
|
||||
public void SetBones(Transform shoulder, Transform upperArm, Transform forearm, Transform hand)
|
||||
{
|
||||
if (upperArm == null || forearm == null || hand == null) return;
|
||||
|
||||
ShoulderTransform = shoulder;
|
||||
UpperarmTransform = upperArm;
|
||||
LowerarmTransform = forearm;
|
||||
HandTransform = hand;
|
||||
|
||||
int i = 0;
|
||||
if ( shoulder == null)
|
||||
{
|
||||
IKBones = new IKBone[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
IKBones = new IKBone[4];
|
||||
IKBones[0] = new IKBone(shoulder);
|
||||
i = 1;
|
||||
}
|
||||
|
||||
IKBones[i] = new IKBone(upperArm);
|
||||
IKBones[i+1] = new IKBone(forearm);
|
||||
IKBones[i+2] = new IKBone(hand);
|
||||
|
||||
IKBones[0].SetChild(IKBones[1]);
|
||||
IKBones[1].SetChild(IKBones[2]);
|
||||
|
||||
if ( shoulder != null) IKBones[2].SetChild(IKBones[3]);
|
||||
|
||||
IKTargetPosition = hand.position; IKTargetRotation = hand.rotation;
|
||||
}
|
||||
|
||||
|
||||
public void SetBones()
|
||||
{
|
||||
SetBones(ShoulderTransform, UpperarmTransform, LowerarmTransform, HandTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3db5be43a8560824cb8442841962e470
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public partial class FimpIK_Arm
|
||||
{
|
||||
/*[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)
|
||||
{
|
||||
UpperArmIKBone.RefreshOrientations(ForeArmIKBone.transform.position, TargetElbowNormal);
|
||||
ForeArmIKBone.RefreshOrientations(HandIKBone.transform.position, TargetElbowNormal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected virtual void HandBoneRotation()
|
||||
{
|
||||
float rotWeight = HandRotationWeight * IKWeight * _internalIKWeight;
|
||||
|
||||
if (rotWeight > 0f)
|
||||
{
|
||||
if (rotWeight < 1f)
|
||||
HandIKBone.transform.rotation = Quaternion.LerpUnclamped(HandIKBone.transform.rotation, IKTargetRotation, rotWeight);
|
||||
else
|
||||
HandIKBone.transform.rotation = IKTargetRotation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void RefreshAnimatorCoords()
|
||||
{
|
||||
if (ShoulderIKBone != null) ShoulderIKBone.CaptureSourceAnimation();
|
||||
UpperArmIKBone.CaptureSourceAnimation();
|
||||
ForeArmIKBone.CaptureSourceAnimation();
|
||||
HandIKBone.CaptureSourceAnimation();
|
||||
}
|
||||
|
||||
|
||||
private 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 - UpperArmIKBone.transform.position, HandIKBone.transform.position - UpperArmIKBone.transform.position);
|
||||
}
|
||||
|
||||
|
||||
public void RefreshDefaultFlexNormal()
|
||||
{
|
||||
Vector3 normal = Vector3.Cross(ForeArmIKBone.transform.position - UpperArmIKBone.transform.position, HandIKBone.transform.position - ForeArmIKBone.transform.position);
|
||||
if (normal != Vector3.zero) TargetElbowNormal = normal;
|
||||
}
|
||||
|
||||
|
||||
private Vector3 GetOrientationDirection(Vector3 ikPosition, Vector3 orientationNormal)
|
||||
{
|
||||
Vector3 direction = ikPosition - UpperArmIKBone.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 + UpperArmIKBone.sqrMagn - ForeArmIKBone.sqrMagn) / 2f / distStartToGoal;
|
||||
float upLen = Mathf.Sqrt(Mathf.Clamp(UpperArmIKBone.sqrMagn - forwardLen * forwardLen, 0, Mathf.Infinity));
|
||||
|
||||
Vector3 perpendicularUp = Vector3.Cross(direction / distStartToGoal, orientationNormal);
|
||||
return Quaternion.LookRotation(direction, perpendicularUp) * new Vector3(0f, upLen, forwardLen);
|
||||
}
|
||||
|
||||
private float sd_targetIKRotation = 0f;
|
||||
public void IKHandRotationWeightFadeTo(float to, float duration, float delta)
|
||||
{
|
||||
HandRotationWeight = Mathf.SmoothDamp(HandRotationWeight, to, ref sd_targetIKRotation, duration, Mathf.Infinity, delta);
|
||||
}
|
||||
|
||||
private float sd_positionWeight = 0f;
|
||||
|
||||
public bool IsCorrect
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Initialized == false) return false;
|
||||
if (UpperarmTransform == null) return false;
|
||||
if (LowerarmTransform == null) return false;
|
||||
if (shoulderRotate == null) return false;
|
||||
if (shoulderRotate.transform == null) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void IKHandPositionWeightFadeTo(float to, float duration, float delta)
|
||||
{
|
||||
IKPositionWeight = Mathf.SmoothDamp(IKPositionWeight, to, ref sd_positionWeight, duration, Mathf.Infinity, delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IK position offsetted with hand middle position
|
||||
/// </summary>
|
||||
public Vector3 GetMiddleHandPosition(Vector3 tgt)
|
||||
{
|
||||
Matrix4x4 mx = Matrix4x4.TRS(IKTargetPosition, IKTargetRotation, HandIKBone.transform.lossyScale);
|
||||
return tgt - mx.MultiplyVector(HandMiddleOffset);
|
||||
}
|
||||
|
||||
public Vector3 GetLimitedIKPosToMax(Vector3 targetIKPos, float lengthFactor = 1f)
|
||||
{
|
||||
Vector3 dir = targetIKPos - UpperArmIKBone.transform.position;
|
||||
return UpperArmIKBone.transform.position + dir.normalized * lengthFactor * limbLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60a2d59ca83150b428c3e0b5e351ee72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,130 @@
|
||||
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_Arm
|
||||
{
|
||||
[NonSerialized] public float _internalIKWeight = 1f;
|
||||
[Range(0f, 1f)] public float IKWeight = 1f;
|
||||
[Tooltip("Blend value for goal position")] [Space(4)] [Range(0f, 1f)] public float IKPositionWeight = 1f;
|
||||
[Tooltip("Blend value hand rotation")] [Range(0f, 1f)] public float HandRotationWeight = 1f;
|
||||
[Tooltip("Blend value for shoulder rotation")] [Range(0f, 1f)] public float ShoulderBlend = 1f;
|
||||
[Tooltip("Flex style algorithm for different limbs")] public FIK_HintMode AutoHintMode = FIK_HintMode.MiddleForward;
|
||||
[Tooltip("If left limb behaves wrong in comparison to right one")] public bool MirrorMaths = false;
|
||||
|
||||
[FPD_Header("Bones References")]
|
||||
public Transform ShoulderTransform;
|
||||
public Transform UpperarmTransform;
|
||||
public Transform LowerarmTransform;
|
||||
public Transform HandTransform;
|
||||
|
||||
[SerializeField] [HideInInspector] private IKBone[] IKBones;
|
||||
|
||||
public Vector3 TargetElbowNormal { get; private set; }
|
||||
public Quaternion UpperarmRotationOffset { get; set; }
|
||||
|
||||
|
||||
/// <summary> Updating processor with 3-bones oriented inverse kinematics </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (!Initialized) return;
|
||||
|
||||
CalculateLimbLength();
|
||||
Refresh();
|
||||
|
||||
ComputeShoulder(IKTargetPosition);
|
||||
|
||||
Vector3 targetIKPos = IKTargetPosition;
|
||||
|
||||
#region Max Stretching Feature
|
||||
|
||||
if (MaxStretching < 1.2f)
|
||||
{
|
||||
CalculateLimbLength();
|
||||
float stretch = GetStretchValue(targetIKPos);
|
||||
|
||||
if (stretch > MaxStretching)
|
||||
{
|
||||
float len = (MaxStretching * limbLength);
|
||||
targetIKPos = UpperArmIKBone.transform.position + (targetIKPos - UpperArmIKBone.transform.position).normalized * len;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
// Arm IK Position ---------------------------------------------------
|
||||
|
||||
float posWeight = IKPositionWeight * IKWeight * _internalIKWeight;
|
||||
UpperArmIKBone.sqrMagn = (ForeArmIKBone.transform.position - UpperArmIKBone.transform.position).sqrMagnitude;
|
||||
ForeArmIKBone.sqrMagn = (HandIKBone.transform.position - ForeArmIKBone.transform.position).sqrMagnitude;
|
||||
|
||||
TargetElbowNormal = GetDefaultFlexNormal();
|
||||
|
||||
Vector3 orientationDirection = GetOrientationDirection(targetIKPos, TargetElbowNormal);
|
||||
if (orientationDirection == Vector3.zero) orientationDirection = ForeArmIKBone.transform.position - UpperArmIKBone.transform.position;
|
||||
|
||||
if (posWeight > 0f)
|
||||
{
|
||||
Quaternion sBoneRot = UpperArmIKBone.GetRotation(orientationDirection, TargetElbowNormal) * UpperarmRotationOffset;
|
||||
if (posWeight < 1f) sBoneRot = Quaternion.LerpUnclamped(UpperArmIKBone.transform.rotation, sBoneRot, posWeight);
|
||||
UpperArmIKBone.transform.rotation = sBoneRot;
|
||||
|
||||
Quaternion sMidBoneRot = ForeArmIKBone.GetRotation(targetIKPos - ForeArmIKBone.transform.position, ForeArmIKBone.GetCurrentOrientationNormal());
|
||||
if (posWeight < 1f) sMidBoneRot = Quaternion.LerpUnclamped(ForeArmIKBone.transform.rotation, sMidBoneRot, posWeight);
|
||||
ForeArmIKBone.transform.rotation = sMidBoneRot;
|
||||
}
|
||||
|
||||
HandBoneRotation();
|
||||
}
|
||||
|
||||
[NonSerialized] public Vector3 ikCustomHintOffset = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Calculating IK pole position normal for desired flexing bend
|
||||
/// </summary>
|
||||
private Vector3 GetAutomaticFlexNormal()
|
||||
{
|
||||
Vector3 bendNormal = UpperArmIKBone.GetCurrentOrientationNormal() ;
|
||||
if (ikCustomHintOffset != Vector3.zero) bendNormal = (bendNormal + ikCustomHintOffset).normalized;
|
||||
|
||||
switch (AutoHintMode)
|
||||
{
|
||||
case FIK_HintMode.MiddleForward:
|
||||
return Vector3.LerpUnclamped(bendNormal.normalized, ForeArmIKBone.srcRotation * ForeArmIKBone.forward, 0.5f);
|
||||
|
||||
|
||||
case FIK_HintMode.MiddleBack: return ForeArmIKBone.srcRotation * -ForeArmIKBone.right + ikCustomHintOffset;
|
||||
|
||||
case FIK_HintMode.EndForward:
|
||||
|
||||
Vector3 hintPos = ForeArmIKBone.srcPosition + HandIKBone.srcRotation * HandIKBone.forward * (MirrorMaths ? -1f : 1f);
|
||||
Vector3 normal = Vector3.Cross(hintPos - UpperArmIKBone.srcPosition, IKTargetPosition - UpperArmIKBone.srcPosition);
|
||||
if (normal == Vector3.zero) return bendNormal;
|
||||
|
||||
return normal;
|
||||
|
||||
case FIK_HintMode.OnGoal: return Vector3.LerpUnclamped(bendNormal, IKTargetRotation * HandIKBone.right, 0.5f);
|
||||
}
|
||||
|
||||
return bendNormal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Drawing helper gizmos for identifying IK process and setup
|
||||
public void OnDrawGizmos()
|
||||
{
|
||||
if (!Initialized) return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd8229b9af7c27d4cb030c4ea1034566
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user