导入leg插件,完成腿部动画
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public class FElasticTransform
|
||||
{
|
||||
public Transform transform;
|
||||
|
||||
private FElasticTransform elChild;
|
||||
private FElasticTransform elParent;
|
||||
|
||||
// Position Muscle -----------
|
||||
[FPD_Suffix(0f, 1f)] public float RotationRapidness = 0.1f;
|
||||
/// <summary> Bounce effect muscle </summary>
|
||||
public FMuscle_Vector3 PositionMuscle { get; private set; }
|
||||
|
||||
public Vector3 ProceduralPosition { get; private set; }
|
||||
private Quaternion proceduralRotation;
|
||||
|
||||
/// <summary> Used for blending </summary>
|
||||
public Vector3 sourceAnimationPosition { get; private set; }
|
||||
|
||||
private float delta = 0.01f;
|
||||
|
||||
public void Initialize(Transform transform)
|
||||
{
|
||||
if (transform == null) return;
|
||||
|
||||
this.transform = transform;
|
||||
|
||||
ProceduralPosition = transform.position;
|
||||
proceduralRotation = transform.rotation;
|
||||
|
||||
sourceAnimationPosition = transform.position;
|
||||
|
||||
PositionMuscle = new FMuscle_Vector3();
|
||||
PositionMuscle.Initialize(transform.position);
|
||||
}
|
||||
|
||||
public void OverrideProceduralPosition(Vector3 newPos)
|
||||
{ ProceduralPosition = newPos; }
|
||||
|
||||
public void OverrideProceduralPositionHard(Vector3 newPos)
|
||||
{ ProceduralPosition = newPos; PositionMuscle.OverrideProceduralPosition(newPos); sourceAnimationPosition = newPos; }
|
||||
|
||||
public void OverrideProceduralRotation(Quaternion newRot)
|
||||
{ proceduralRotation = newRot; }
|
||||
|
||||
public void CaptureSourceAnimation()
|
||||
{ sourceAnimationPosition = transform.position; }
|
||||
|
||||
public void SetChild(FElasticTransform child)
|
||||
{ elChild = child; }
|
||||
|
||||
public FElasticTransform GetElasticChild()
|
||||
{ return elChild; }
|
||||
|
||||
public void SetParent(FElasticTransform parent)
|
||||
{ elParent = parent; }
|
||||
|
||||
public void UpdateElasticPosition(float delta)
|
||||
{
|
||||
this.delta = delta;
|
||||
|
||||
if (elParent != null)
|
||||
{
|
||||
FElasticTransform parent = elParent.transform == null ? elParent.elParent : elParent;
|
||||
Quaternion referenceRotation = parent.transform.rotation;
|
||||
|
||||
// Target position for elastic bones
|
||||
Vector3 targetPos = parent.ProceduralPosition + referenceRotation * transform.localPosition;
|
||||
PositionMuscle.Update(delta, targetPos);
|
||||
|
||||
ProceduralPosition = PositionMuscle.ProceduralPosition;
|
||||
}
|
||||
else
|
||||
ProceduralPosition = transform.position;
|
||||
}
|
||||
|
||||
|
||||
public void UpdateElasticPosition(float delta, Vector3 influenceOffset)
|
||||
{
|
||||
this.delta = delta;
|
||||
|
||||
if (elParent != null)
|
||||
{
|
||||
PositionMuscle.MotionInfluence(influenceOffset);
|
||||
UpdateElasticPosition(delta);
|
||||
}
|
||||
else
|
||||
ProceduralPosition = transform.position;
|
||||
}
|
||||
|
||||
|
||||
public void UpdateElasticRotation(float blending)
|
||||
{
|
||||
if (elChild != null) // We have child - procedural mixed with source animator local pos
|
||||
{
|
||||
Quaternion targetRotation;
|
||||
|
||||
if (blending < 1f)
|
||||
targetRotation = GetTargetRotation(elChild.BlendVector(elChild.ProceduralPosition, blending), transform.TransformDirection(elChild.transform.localPosition), blending);
|
||||
else
|
||||
targetRotation = GetTargetRotation(elChild.ProceduralPosition, transform.TransformDirection(elChild.transform.localPosition), ProceduralPosition);
|
||||
|
||||
if (RotationRapidness < 1f)
|
||||
{
|
||||
proceduralRotation = Quaternion.Lerp(proceduralRotation, targetRotation, Mathf.Min(1f, delta * (10f + RotationRapidness * 50f)));
|
||||
transform.rotation = proceduralRotation;
|
||||
}
|
||||
else
|
||||
transform.rotation = targetRotation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Vector3 BlendVector(Vector3 target, float blend)
|
||||
{
|
||||
return Vector3.LerpUnclamped(sourceAnimationPosition, target, blend);
|
||||
}
|
||||
|
||||
public Quaternion GetTargetRotation(Vector3 lookPos, Vector3 localOffset, float blending)
|
||||
{
|
||||
return Quaternion.FromToRotation(localOffset, (lookPos - BlendVector(ProceduralPosition, blending)).normalized) * transform.rotation;
|
||||
}
|
||||
|
||||
public Quaternion GetTargetRotation(Vector3 lookPos, Vector3 localOffset, Vector3 pos)
|
||||
{
|
||||
return Quaternion.FromToRotation(localOffset, (lookPos - pos).normalized) * transform.rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf45ab52287191e42b11af196dabcae5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,482 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public abstract class FMuscle_Motor
|
||||
{
|
||||
public float OutValue { get; protected set; }
|
||||
|
||||
protected float proceduralValue = 0f;
|
||||
protected float dampingAcceleration;
|
||||
protected float dynamicAcceleration;
|
||||
protected float accelerationSign;
|
||||
|
||||
public bool IsWorking()
|
||||
{
|
||||
return dynamicAcceleration != 0f;
|
||||
}
|
||||
|
||||
/// <summary> Value should be high (500f to 10000f) </summary>
|
||||
public void Push(float value) { dynamicAcceleration += value; }
|
||||
|
||||
public void Initialize(float initValue)
|
||||
{
|
||||
OutValue = initValue;
|
||||
proceduralValue = initValue;
|
||||
dampingAcceleration = 0f;
|
||||
dynamicAcceleration = 0f;
|
||||
accelerationSign = 0f;
|
||||
}
|
||||
|
||||
protected abstract float GetDiff(float current, float desired);
|
||||
|
||||
public void Update(float delta, float current, float desired, float acceleration, float accelerationLimit, float damping, float brakePower)
|
||||
{
|
||||
float towards = GetDiff(current, desired);
|
||||
accelerationSign = Mathf.Sign(towards);
|
||||
|
||||
// Linear fitting
|
||||
dampingAcceleration = towards;
|
||||
dampingAcceleration = Mathf.Clamp(dampingAcceleration, -damping, damping) * damping;
|
||||
|
||||
float incr = dampingAcceleration * delta;
|
||||
|
||||
if (towards > 0f) { if (incr > towards) incr = towards; } else { if (incr < towards) incr = towards; }
|
||||
proceduralValue += incr;
|
||||
|
||||
// Conditions for acceleration
|
||||
float mul = 1f;
|
||||
if (Mathf.Sign(dynamicAcceleration) != accelerationSign)
|
||||
{
|
||||
mul = 1f + Mathf.Abs(towards) / ((1f - brakePower) * 10f + 8f);
|
||||
}
|
||||
|
||||
// Difference towards target
|
||||
float difference = towards;
|
||||
if (difference < 0f) difference = -difference;
|
||||
|
||||
// Braking when near
|
||||
float brakeFactor = 5f + (1f - brakePower) * 85f;
|
||||
if (difference < brakeFactor) mul *= Mathf.Min(1f, difference / brakeFactor);
|
||||
if (mul < 0f) mul = -mul;
|
||||
|
||||
// Acceleration fitting
|
||||
if (delta > 0.04f) delta = 0.04f;
|
||||
dynamicAcceleration += acceleration * accelerationSign * delta * mul; // Increase acceleration
|
||||
dynamicAcceleration = Mathf.Clamp(dynamicAcceleration, -accelerationLimit, accelerationLimit); // Limit acceleration
|
||||
if (dynamicAcceleration < 0.000005f && dynamicAcceleration > -0.000005f) dynamicAcceleration = 0f;
|
||||
|
||||
proceduralValue += dynamicAcceleration * delta;
|
||||
|
||||
OutValue = proceduralValue;
|
||||
}
|
||||
|
||||
public void OverrideValue(float newValue)
|
||||
{
|
||||
proceduralValue = newValue;
|
||||
}
|
||||
|
||||
public void OffsetValue(float off)
|
||||
{
|
||||
proceduralValue += off;
|
||||
}
|
||||
}
|
||||
|
||||
public class FMuscle_Float : FMuscle_Motor
|
||||
{
|
||||
protected override float GetDiff(float current, float desired)
|
||||
{
|
||||
return desired - current;
|
||||
}
|
||||
}
|
||||
|
||||
public class FMuscle_Angle : FMuscle_Motor
|
||||
{
|
||||
|
||||
protected override float GetDiff(float current, float desired)
|
||||
{
|
||||
return Mathf.DeltaAngle(current, desired);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[System.Serializable]
|
||||
public class FMuscle_Vector3
|
||||
{
|
||||
[HideInInspector] public Vector3 DesiredPosition;
|
||||
public Vector3 ProceduralPosition { get; private set; }
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
private FMuscle_Float x;
|
||||
private FMuscle_Float y;
|
||||
private FMuscle_Float z;
|
||||
|
||||
public void Initialize(Vector3 initPosition)
|
||||
{
|
||||
x = new FMuscle_Float();
|
||||
y = new FMuscle_Float();
|
||||
z = new FMuscle_Float();
|
||||
|
||||
x.Initialize(initPosition.x);
|
||||
y.Initialize(initPosition.y);
|
||||
z.Initialize(initPosition.z);
|
||||
|
||||
ProceduralPosition = initPosition;
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
public bool IsWorking()
|
||||
{
|
||||
return x.IsWorking() || y.IsWorking() || z.IsWorking();
|
||||
}
|
||||
|
||||
public void Push(Vector3 value)
|
||||
{
|
||||
x.Push(value.x);
|
||||
y.Push(value.y);
|
||||
z.Push(value.z);
|
||||
}
|
||||
|
||||
public void Reset(Vector3 value)
|
||||
{
|
||||
x.Initialize(value.x);
|
||||
y.Initialize(value.y);
|
||||
z.Initialize(value.z);
|
||||
}
|
||||
|
||||
public void Push(float v)
|
||||
{
|
||||
x.Push(v);
|
||||
y.Push(v);
|
||||
z.Push(v);
|
||||
}
|
||||
|
||||
public void MotionInfluence(Vector3 offset)
|
||||
{
|
||||
x.OffsetValue(offset.x);
|
||||
y.OffsetValue(offset.y);
|
||||
z.OffsetValue(offset.z);
|
||||
ProceduralPosition += offset;
|
||||
}
|
||||
|
||||
public void Update(float delta, Vector3 desired, float acceleration, float accelerationLimit, float damping, float brakePower)
|
||||
{
|
||||
x.Update(delta, ProceduralPosition.x, desired.x, acceleration, accelerationLimit, damping, brakePower);
|
||||
y.Update(delta, ProceduralPosition.y, desired.y, acceleration, accelerationLimit, damping, brakePower);
|
||||
z.Update(delta, ProceduralPosition.z, desired.z, acceleration, accelerationLimit, damping, brakePower);
|
||||
|
||||
ProceduralPosition = new Vector3(x.OutValue, y.OutValue, z.OutValue);
|
||||
}
|
||||
|
||||
|
||||
[FPD_Suffix(0f, 10000)] public float Acceleration = 10000f;
|
||||
[FPD_Suffix(0f, 10000)] public float AccelerationLimit = 5000f;
|
||||
[FPD_Suffix(0f, 50f)] public float Damping = 10f;
|
||||
[FPD_Suffix(0f, 1f)] public float BrakePower = 0.2f;
|
||||
public Vector3 Update(float delta, Vector3 desired)
|
||||
{
|
||||
x.Update(delta, ProceduralPosition.x, desired.x, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
y.Update(delta, ProceduralPosition.y, desired.y, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
z.Update(delta, ProceduralPosition.z, desired.z, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
|
||||
ProceduralPosition = new Vector3(x.OutValue, y.OutValue, z.OutValue);
|
||||
return ProceduralPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding push force to vector
|
||||
/// </summary>
|
||||
public IEnumerator PushImpulseCoroutine(Vector3 power, float duration, bool fadeOutPower = false, float delay = 0f)
|
||||
{
|
||||
if (delay > 0f) yield return new WaitForSeconds(delay);
|
||||
|
||||
float elapsed = 0f;
|
||||
Push(0.0001f);
|
||||
while (elapsed / duration < 1f)
|
||||
{
|
||||
if (!fadeOutPower) Push(power * Time.deltaTime * 60f); else Push(power * (1f - elapsed / duration) * Time.deltaTime * 60f);
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static void Lerp(ref FMuscle_Vector3 source, FMuscle_Vector3 a, FMuscle_Vector3 b, float t)
|
||||
{
|
||||
if (a == null || b == null || source == null) return;
|
||||
|
||||
source.Acceleration = Mathf.LerpUnclamped(a.Acceleration, b.Acceleration, t);
|
||||
source.AccelerationLimit = Mathf.LerpUnclamped(a.AccelerationLimit, b.AccelerationLimit, t);
|
||||
source.BrakePower = Mathf.LerpUnclamped(a.BrakePower, b.BrakePower, t);
|
||||
source.Damping = Mathf.LerpUnclamped(a.Damping, b.Damping, t);
|
||||
}
|
||||
|
||||
public void OverrideProceduralPosition(Vector3 newPos)
|
||||
{
|
||||
ProceduralPosition = newPos;
|
||||
DesiredPosition = newPos;
|
||||
x.OverrideValue(newPos.x);
|
||||
y.OverrideValue(newPos.y);
|
||||
z.OverrideValue(newPos.z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[System.Serializable]
|
||||
public class FMuscle_Quaternion
|
||||
{
|
||||
[HideInInspector] public Quaternion DesiredRotation;
|
||||
public Quaternion ProceduralRotation { get; private set; }
|
||||
public bool IsCorrect { get { return x != null; } }
|
||||
|
||||
private FMuscle_Float x;
|
||||
private FMuscle_Float y;
|
||||
private FMuscle_Float z;
|
||||
private FMuscle_Float w;
|
||||
|
||||
public void Initialize(Quaternion initRotation)
|
||||
{
|
||||
x = new FMuscle_Float();
|
||||
y = new FMuscle_Float();
|
||||
z = new FMuscle_Float();
|
||||
w = new FMuscle_Float();
|
||||
|
||||
x.Initialize(initRotation.x);
|
||||
y.Initialize(initRotation.y);
|
||||
z.Initialize(initRotation.z);
|
||||
w.Initialize(initRotation.w);
|
||||
|
||||
ProceduralRotation = initRotation;
|
||||
}
|
||||
|
||||
public bool IsWorking()
|
||||
{
|
||||
return x.IsWorking() || y.IsWorking() || z.IsWorking() || w.IsWorking();
|
||||
}
|
||||
|
||||
public void Push(Quaternion value)
|
||||
{
|
||||
x.Push(value.x);
|
||||
y.Push(value.y);
|
||||
z.Push(value.z);
|
||||
w.Push(value.w);
|
||||
}
|
||||
|
||||
public void Push(float v)
|
||||
{
|
||||
x.Push(v);
|
||||
y.Push(v);
|
||||
z.Push(v);
|
||||
w.Push(v);
|
||||
}
|
||||
|
||||
public void Push(Quaternion value, float multiply)
|
||||
{
|
||||
x.Push(value.x * multiply);
|
||||
y.Push(value.y * multiply);
|
||||
z.Push(value.z * multiply);
|
||||
w.Push(value.w * multiply);
|
||||
}
|
||||
|
||||
public void Update(float delta, Quaternion desired, float acceleration, float accelerationLimit, float damping, float brakePower)
|
||||
{
|
||||
x.Update(delta, ProceduralRotation.x, desired.x, acceleration, accelerationLimit, damping, brakePower);
|
||||
y.Update(delta, ProceduralRotation.y, desired.y, acceleration, accelerationLimit, damping, brakePower);
|
||||
z.Update(delta, ProceduralRotation.z, desired.z, acceleration, accelerationLimit, damping, brakePower);
|
||||
w.Update(delta, ProceduralRotation.w, desired.w, acceleration, accelerationLimit, damping, brakePower);
|
||||
|
||||
ProceduralRotation = new Quaternion(x.OutValue, y.OutValue, z.OutValue, w.OutValue);
|
||||
}
|
||||
|
||||
[FPD_Suffix(0f, 10000)] public float Acceleration = 5000f;
|
||||
[FPD_Suffix(0f, 10000)] public float AccelerationLimit = 1000f;
|
||||
[FPD_Suffix(0f, 50f)] public float Damping = 10f;
|
||||
[FPD_Suffix(0f, 1f)] public float BrakePower = 0.2f;
|
||||
public void Update(float delta, Quaternion desired)
|
||||
{
|
||||
x.Update(delta, ProceduralRotation.x, desired.x, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
y.Update(delta, ProceduralRotation.y, desired.y, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
z.Update(delta, ProceduralRotation.z, desired.z, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
w.Update(delta, ProceduralRotation.w, desired.w, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
|
||||
ProceduralRotation = new Quaternion(x.OutValue, y.OutValue, z.OutValue, w.OutValue);
|
||||
}
|
||||
|
||||
public void UpdateEnsured(float delta, Quaternion desired)
|
||||
{
|
||||
Update(delta, EnsureQuaternionContinuity(ProceduralRotation, desired));
|
||||
}
|
||||
|
||||
public static Quaternion EnsureQuaternionContinuity(Quaternion latestRot, Quaternion targetRot)
|
||||
{
|
||||
Quaternion flipped = new Quaternion(-targetRot.x, -targetRot.y, -targetRot.z, -targetRot.w);
|
||||
Quaternion midQ = new Quaternion ( Mathf.LerpUnclamped(latestRot.x, targetRot.x, 0.5f), Mathf.LerpUnclamped(latestRot.y, targetRot.y, 0.5f), Mathf.LerpUnclamped(latestRot.z, targetRot.z, 0.5f), Mathf.LerpUnclamped(latestRot.w, targetRot.w, 0.5f) );
|
||||
Quaternion midQFlipped = new Quaternion(Mathf.LerpUnclamped(latestRot.x, flipped.x, 0.5f),Mathf.LerpUnclamped(latestRot.y, flipped.y, 0.5f),Mathf.LerpUnclamped(latestRot.z, flipped.z, 0.5f),Mathf.LerpUnclamped(latestRot.w, flipped.w, 0.5f));
|
||||
|
||||
float angle = Quaternion.Angle(latestRot, midQ);
|
||||
float angleTreshold = Quaternion.Angle(latestRot, midQFlipped);
|
||||
|
||||
return angleTreshold < angle ? flipped : targetRot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding push force to quaternion
|
||||
/// </summary>
|
||||
public IEnumerator PushImpulseCoroutine(Quaternion power, float duration, bool fadeOutPower = false, float delay = 0f)
|
||||
{
|
||||
if (delay > 0f) yield return new WaitForSeconds(delay);
|
||||
|
||||
float elapsed = 0f;
|
||||
Push(0.001f);
|
||||
|
||||
while (elapsed / duration < 1f)
|
||||
{
|
||||
if (!fadeOutPower) Push(power, Time.deltaTime * 60f); else Push(power, (1f - elapsed / duration) * Time.deltaTime * 60f);
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static void Lerp(ref FMuscle_Quaternion source, FMuscle_Quaternion a, FMuscle_Quaternion b, float t)
|
||||
{
|
||||
if (a == null || b == null || source == null) return;
|
||||
|
||||
source.Acceleration = Mathf.LerpUnclamped(a.Acceleration, b.Acceleration, t);
|
||||
source.AccelerationLimit = Mathf.LerpUnclamped(a.AccelerationLimit, b.AccelerationLimit, t);
|
||||
source.BrakePower = Mathf.LerpUnclamped(a.BrakePower, b.BrakePower, t);
|
||||
source.Damping = Mathf.LerpUnclamped(a.Damping, b.Damping, t);
|
||||
}
|
||||
|
||||
public void OverrideProceduralRotation(Quaternion rotation)
|
||||
{
|
||||
ProceduralRotation = rotation;
|
||||
DesiredRotation = rotation;
|
||||
x.OverrideValue(rotation.x);
|
||||
y.OverrideValue(rotation.y);
|
||||
z.OverrideValue(rotation.z);
|
||||
w.OverrideValue(rotation.w);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[System.Serializable]
|
||||
public class FMuscle_Eulers
|
||||
{
|
||||
[HideInInspector] public Vector3 DesiredEulerAngles;
|
||||
public Vector3 ProceduralEulerAngles { get; private set; }
|
||||
public Quaternion ProceduralRotation { get { return Quaternion.Euler(ProceduralEulerAngles); } }
|
||||
|
||||
private FMuscle_Angle x;
|
||||
private FMuscle_Angle y;
|
||||
private FMuscle_Angle z;
|
||||
|
||||
public void Initialize(Vector3 initEulerAngles)
|
||||
{
|
||||
x = new FMuscle_Angle();
|
||||
y = new FMuscle_Angle();
|
||||
z = new FMuscle_Angle();
|
||||
|
||||
x.Initialize(initEulerAngles.x);
|
||||
y.Initialize(initEulerAngles.y);
|
||||
z.Initialize(initEulerAngles.z);
|
||||
|
||||
ProceduralEulerAngles = initEulerAngles;
|
||||
}
|
||||
|
||||
public void Initialize(Quaternion initRotation)
|
||||
{
|
||||
Initialize(initRotation.eulerAngles);
|
||||
}
|
||||
|
||||
public bool IsWorking()
|
||||
{
|
||||
return x.IsWorking() || y.IsWorking() || z.IsWorking();
|
||||
}
|
||||
|
||||
public void Push(Vector3 value)
|
||||
{
|
||||
x.Push(value.x);
|
||||
y.Push(value.y);
|
||||
z.Push(value.z);
|
||||
}
|
||||
|
||||
public void Push(float v)
|
||||
{
|
||||
x.Push(v);
|
||||
y.Push(v);
|
||||
z.Push(v);
|
||||
}
|
||||
|
||||
public void Push(Vector3 value, float multiply)
|
||||
{
|
||||
x.Push(value.x * multiply);
|
||||
y.Push(value.y * multiply);
|
||||
z.Push(value.z * multiply);
|
||||
}
|
||||
|
||||
public void Update(float delta, Vector3 desired, float acceleration, float accelerationLimit, float damping, float brakePower)
|
||||
{
|
||||
x.Update(delta, ProceduralEulerAngles.x, desired.x, acceleration, accelerationLimit, damping, brakePower);
|
||||
y.Update(delta, ProceduralEulerAngles.y, desired.y, acceleration, accelerationLimit, damping, brakePower);
|
||||
z.Update(delta, ProceduralEulerAngles.z, desired.z, acceleration, accelerationLimit, damping, brakePower);
|
||||
|
||||
ProceduralEulerAngles = new Vector3(x.OutValue, y.OutValue, z.OutValue);
|
||||
}
|
||||
|
||||
[FPD_Suffix(0f, 10000)] public float Acceleration = 5000f;
|
||||
[FPD_Suffix(0f, 10000)] public float AccelerationLimit = 1000f;
|
||||
[FPD_Suffix(0f, 50f)] public float Damping = 10f;
|
||||
[FPD_Suffix(0f, 1f)] public float BrakePower = 0.2f;
|
||||
public Vector3 Update(float delta, Vector3 desired)
|
||||
{
|
||||
x.Update(delta, ProceduralEulerAngles.x, desired.x, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
y.Update(delta, ProceduralEulerAngles.y, desired.y, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
z.Update(delta, ProceduralEulerAngles.z, desired.z, Acceleration, AccelerationLimit, Damping, BrakePower);
|
||||
|
||||
ProceduralEulerAngles = new Vector3(x.OutValue, y.OutValue, z.OutValue);
|
||||
return ProceduralEulerAngles;
|
||||
}
|
||||
|
||||
public void Update(float delta, Quaternion desired)
|
||||
{
|
||||
Update(delta, desired.eulerAngles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding push force to quaternion
|
||||
/// </summary>
|
||||
public IEnumerator PushImpulseCoroutine(Vector3 power, float duration, bool fadeOutPower = false, float delay = 0f)
|
||||
{
|
||||
if (delay > 0f) yield return new WaitForSeconds(delay);
|
||||
|
||||
float elapsed = 0f;
|
||||
Push(0.001f);
|
||||
|
||||
while (elapsed / duration < 1f)
|
||||
{
|
||||
if (!fadeOutPower) Push(power, Time.deltaTime * 60f); else Push(power, (1f - elapsed / duration) * Time.deltaTime * 60f);
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static void Lerp(ref FMuscle_Eulers source, FMuscle_Eulers a, FMuscle_Eulers b, float t)
|
||||
{
|
||||
if (a == null || b == null || source == null) return;
|
||||
|
||||
source.Acceleration = Mathf.LerpUnclamped(a.Acceleration, b.Acceleration, t);
|
||||
source.AccelerationLimit = Mathf.LerpUnclamped(a.AccelerationLimit, b.AccelerationLimit, t);
|
||||
source.BrakePower = Mathf.LerpUnclamped(a.BrakePower, b.BrakePower, t);
|
||||
source.Damping = Mathf.LerpUnclamped(a.Damping, b.Damping, t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e012de6d06709240a411f67e61220a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,303 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
public class UniRotateBone
|
||||
{
|
||||
public Transform transform { get; protected set; }
|
||||
|
||||
public Vector3 initialLocalPosition { get; protected set; }
|
||||
public Quaternion initialLocalRotation { get; protected set; }
|
||||
public Vector3 initialLocalPositionInRootSpace { get; protected set; }
|
||||
public Quaternion initialLocalRotationInRootSpace { get; protected set; }
|
||||
|
||||
public Vector3 right { get; protected set; }
|
||||
public Vector3 up { get; protected set; }
|
||||
public Vector3 forward { get; protected set; }
|
||||
|
||||
public Vector3 dright { get; protected set; }
|
||||
public Vector3 dup { get; protected set; }
|
||||
public Vector3 dforward { get; protected set; }
|
||||
|
||||
public Vector3 fromParentForward { get; protected set; }
|
||||
public Vector3 fromParentCross { get; protected set; }
|
||||
|
||||
public Vector3 keyframedPosition { get; protected set; }
|
||||
public Quaternion keyframedRotation { get; protected set; }
|
||||
public Quaternion mapping { get; protected set; }
|
||||
public Quaternion dmapping { get; protected set; }
|
||||
public Transform root { get; protected set; }
|
||||
|
||||
|
||||
public UniRotateBone(Transform t, Transform root)
|
||||
{
|
||||
transform = t;
|
||||
initialLocalPosition = transform.localPosition;
|
||||
initialLocalRotation = transform.localRotation;
|
||||
|
||||
if (root)
|
||||
{
|
||||
initialLocalPositionInRootSpace = root.InverseTransformPoint(t.position);
|
||||
initialLocalRotationInRootSpace = FEngineering.QToLocal(root.rotation, t.rotation);
|
||||
}
|
||||
|
||||
forward = transform.InverseTransformDirection(root.forward);
|
||||
up = transform.InverseTransformDirection(root.up);
|
||||
right = transform.InverseTransformDirection(root.right);
|
||||
|
||||
dforward = Quaternion.FromToRotation(forward, Vector3.forward) * Vector3.forward;
|
||||
dup = Quaternion.FromToRotation(up, Vector3.up) * Vector3.up;
|
||||
dright = Quaternion.FromToRotation(right, Vector3.right) * Vector3.right;
|
||||
|
||||
if (t.parent) fromParentForward = GetFromParentForward().normalized;
|
||||
else fromParentForward = forward;
|
||||
|
||||
fromParentCross = -Vector3.Cross(fromParentForward, forward);
|
||||
|
||||
mapping = Quaternion.FromToRotation(right, Vector3.right);
|
||||
mapping *= Quaternion.FromToRotation(up, Vector3.up);
|
||||
|
||||
dmapping = Quaternion.FromToRotation(fromParentForward, Vector3.right);
|
||||
dmapping *= Quaternion.FromToRotation(up, Vector3.up);
|
||||
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
|
||||
public Vector3 GetFromParentForward()
|
||||
{
|
||||
return transform.InverseTransformDirection(transform.position - transform.parent.position);
|
||||
}
|
||||
|
||||
#region Complex look target rotation
|
||||
|
||||
|
||||
public Vector3 forwardReference { get; private set; }
|
||||
public Vector3 upReference { get; private set; }
|
||||
public Vector3 rightCrossReference { get; private set; }
|
||||
private Vector3 dynamicUpReference = Vector3.up;
|
||||
|
||||
public Quaternion GetRootCompensateRotation(Quaternion initPelvisInWorld, Quaternion currInWorld, float armsRootCompensate)
|
||||
{
|
||||
Quaternion pre;
|
||||
|
||||
if (armsRootCompensate > 0f)
|
||||
{
|
||||
// Transforming wrinst rotation to local space of pelvis
|
||||
pre = FEngineering.QToLocal(currInWorld, transform.parent.rotation);
|
||||
pre = FEngineering.QToWorld(initPelvisInWorld, pre); // Transpose from pelvis local space but with init static rotation
|
||||
|
||||
if (armsRootCompensate < 1f)
|
||||
pre = Quaternion.Lerp(transform.parent.rotation, pre, armsRootCompensate);
|
||||
}
|
||||
else
|
||||
pre = transform.parent.rotation;
|
||||
|
||||
return pre;
|
||||
}
|
||||
|
||||
public void RefreshCustomAxis(Vector3 up, Vector3 forward)
|
||||
{
|
||||
if (transform == null) return;
|
||||
forwardReference = Quaternion.Inverse(transform.parent.rotation) * root.rotation * forward;
|
||||
upReference = Quaternion.Inverse(transform.parent.rotation) * root.rotation * up;
|
||||
rightCrossReference = Vector3.Cross(upReference, forwardReference);
|
||||
}
|
||||
|
||||
public void RefreshCustomAxis(Vector3 up, Vector3 forward, Quaternion customParentRot)
|
||||
{
|
||||
forwardReference = Quaternion.Inverse(customParentRot) * root.rotation * forward;
|
||||
upReference = Quaternion.Inverse(customParentRot) * root.rotation * up;
|
||||
rightCrossReference = Vector3.Cross(upReference, forwardReference);
|
||||
}
|
||||
|
||||
/// <summary> Execute RefreshCustomAxis() before this | Do rotation = RotateCustomAxis() * transform.rotation </summary>
|
||||
public Quaternion RotateCustomAxis(float x, float y, UniRotateBone oRef)
|
||||
{
|
||||
// With calculated angles we can get rotation by rotating around desired axes
|
||||
Vector3 lookDirectionParent = Quaternion.AngleAxis(y, oRef.upReference) * Quaternion.AngleAxis(x, rightCrossReference) * oRef.forwardReference;
|
||||
|
||||
// Making look and up direction perpendicular
|
||||
Vector3 upDirGoal = oRef.upReference;
|
||||
Vector3.OrthoNormalize(ref lookDirectionParent, ref upDirGoal);
|
||||
|
||||
// Look and up directions in lead's parent space
|
||||
Vector3 lookDir = lookDirectionParent;
|
||||
dynamicUpReference = upDirGoal;
|
||||
Vector3.OrthoNormalize(ref lookDir, ref dynamicUpReference);
|
||||
|
||||
// Finally getting look rotation
|
||||
Quaternion lookRot = transform.parent.rotation * Quaternion.LookRotation(lookDir, dynamicUpReference);
|
||||
lookRot *= Quaternion.Inverse(transform.parent.rotation * Quaternion.LookRotation(oRef.forwardReference, oRef.upReference));
|
||||
return lookRot;
|
||||
}
|
||||
|
||||
internal Quaternion GetSourcePoseRotation()
|
||||
{
|
||||
return FEngineering.QToWorld(root.rotation, initialLocalRotationInRootSpace);
|
||||
}
|
||||
|
||||
/// <summary> Execute RefreshCustomAxis() before this </summary>
|
||||
public Vector2 GetCustomLookAngles(Vector3 direction, UniRotateBone orientationsReference)
|
||||
{
|
||||
// Target look rotation equivalent for LeadBone's parent
|
||||
Vector3 lookDirectionParent = Quaternion.Inverse(transform.parent.rotation) * (direction).normalized;
|
||||
Vector2 angles = Vector2.zero;
|
||||
|
||||
// Getting angle offset in y axis - horizontal rotation
|
||||
angles.y = AngleAroundAxis(orientationsReference.forwardReference, lookDirectionParent, orientationsReference.upReference);
|
||||
|
||||
Vector3 targetRight = Vector3.Cross(orientationsReference.upReference, lookDirectionParent);
|
||||
Vector3 horizontalPlaneTarget = lookDirectionParent - Vector3.Project(lookDirectionParent, orientationsReference.upReference);
|
||||
|
||||
angles.x = AngleAroundAxis(horizontalPlaneTarget, lookDirectionParent, targetRight);
|
||||
|
||||
return angles;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculate angle between two directions around defined axis
|
||||
/// </summary>
|
||||
public static float AngleAroundAxis(Vector3 firstDirection, Vector3 secondDirection, Vector3 axis)
|
||||
{
|
||||
// Projecting to orthogonal target axis plane
|
||||
firstDirection -= Vector3.Project(firstDirection, axis);
|
||||
secondDirection -= Vector3.Project(secondDirection, axis);
|
||||
|
||||
float angle = Vector3.Angle(firstDirection, secondDirection);
|
||||
|
||||
return angle * (Vector3.Dot(axis, Vector3.Cross(firstDirection, secondDirection)) < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public Quaternion DynamicMapping()
|
||||
{
|
||||
Quaternion dMap = Quaternion.FromToRotation(right, transform.InverseTransformDirection(root.right));
|
||||
dMap *= Quaternion.FromToRotation(up, transform.InverseTransformDirection(root.up));
|
||||
return dMap;
|
||||
}
|
||||
|
||||
public void CaptureKeyframeAnimation()
|
||||
{
|
||||
keyframedPosition = transform.position;
|
||||
keyframedRotation = transform.rotation;
|
||||
}
|
||||
|
||||
public void RotateBy(float x, float y, float z)
|
||||
{
|
||||
Quaternion rot = transform.rotation;
|
||||
if (x != 0f) rot *= Quaternion.AngleAxis(x, right);
|
||||
if (y != 0f) rot *= Quaternion.AngleAxis(y, up);
|
||||
if (z != 0f) rot *= Quaternion.AngleAxis(z, forward);
|
||||
transform.rotation = rot;
|
||||
}
|
||||
|
||||
public void RotateBy(Vector3 angles)
|
||||
{
|
||||
RotateBy(angles.x, angles.y, angles.z);
|
||||
}
|
||||
|
||||
public void RotateBy(Vector3 angles, float blend)
|
||||
{
|
||||
RotateBy(BlendAngle(angles.x, blend), BlendAngle(angles.y, blend), BlendAngle(angles.z, blend));
|
||||
}
|
||||
|
||||
public void RotateByDynamic(Vector3 angles)
|
||||
{
|
||||
RotateByDynamic(angles.x, angles.y, angles.z);
|
||||
}
|
||||
|
||||
public void RotateByDynamic(float x, float y, float z)
|
||||
{
|
||||
Quaternion rot = transform.rotation;
|
||||
if (x != 0f) rot *= Quaternion.AngleAxis(x, transform.InverseTransformDirection(root.right));
|
||||
if (y != 0f) rot *= Quaternion.AngleAxis(y, transform.InverseTransformDirection(root.up));
|
||||
if (z != 0f) rot *= Quaternion.AngleAxis(z, transform.InverseTransformDirection(root.forward));
|
||||
transform.rotation = rot;
|
||||
}
|
||||
|
||||
public Quaternion GetAngleRotation(float x, float y, float z)
|
||||
{
|
||||
Quaternion rot = Quaternion.identity;
|
||||
if (x != 0f) rot *= Quaternion.AngleAxis(x, right);
|
||||
if (y != 0f) rot *= Quaternion.AngleAxis(y, up);
|
||||
if (z != 0f) rot *= Quaternion.AngleAxis(z, forward);
|
||||
return rot;
|
||||
}
|
||||
|
||||
public Quaternion GetAngleRotationDynamic(float x, float y, float z)
|
||||
{
|
||||
Quaternion rot = Quaternion.identity;
|
||||
if (x != 0f) rot *= Quaternion.AngleAxis(x, transform.InverseTransformDirection(root.right));
|
||||
if (y != 0f) rot *= Quaternion.AngleAxis(y, transform.InverseTransformDirection(root.up));
|
||||
if (z != 0f) rot *= Quaternion.AngleAxis(z, transform.InverseTransformDirection(root.forward));
|
||||
return rot;
|
||||
}
|
||||
|
||||
public Quaternion GetAngleRotationDynamic(Vector3 angles)
|
||||
{
|
||||
return GetAngleRotationDynamic(angles.x, angles.y, angles.z);
|
||||
}
|
||||
|
||||
public void RotateByDynamic(Vector3 angles, float blend)
|
||||
{
|
||||
RotateByDynamic(BlendAngle(angles.x, blend), BlendAngle(angles.y, blend), BlendAngle(angles.z, blend));
|
||||
}
|
||||
|
||||
public void RotateByDynamic(float x, float y, float z, float blend)
|
||||
{
|
||||
RotateByDynamic(BlendAngle(x, blend), BlendAngle(y, blend), BlendAngle(z, blend));
|
||||
}
|
||||
|
||||
public void RotateByDynamic(float x, float y, float z, Quaternion orientation)
|
||||
{
|
||||
Quaternion rot = transform.rotation;
|
||||
if (x != 0f) rot *= Quaternion.AngleAxis(x, transform.InverseTransformDirection(orientation * Vector3.right));
|
||||
if (y != 0f) rot *= Quaternion.AngleAxis(y, transform.InverseTransformDirection(orientation * Vector3.up));
|
||||
if (z != 0f) rot *= Quaternion.AngleAxis(z, transform.InverseTransformDirection(orientation * Vector3.forward));
|
||||
transform.rotation = rot;
|
||||
}
|
||||
|
||||
public void RotateXBy(float angle) { transform.rotation *= Quaternion.AngleAxis(angle, right); }
|
||||
public void RotateYBy(float angle) { transform.rotation *= Quaternion.AngleAxis(angle, up); }
|
||||
public void RotateZBy(float angle) { transform.rotation *= Quaternion.AngleAxis(angle, forward); }
|
||||
|
||||
public void PreCalibrate()
|
||||
{
|
||||
transform.localPosition = initialLocalPosition;
|
||||
transform.localRotation = initialLocalRotation;
|
||||
}
|
||||
|
||||
/// <summary> Bone must have parent </summary>
|
||||
public Quaternion RotationTowards(Vector3 toDir)
|
||||
{
|
||||
Quaternion fromTo = Quaternion.FromToRotation
|
||||
(
|
||||
transform.TransformDirection(fromParentForward).normalized, // Forward direction
|
||||
(toDir).normalized // Look direction
|
||||
);
|
||||
|
||||
return fromTo * transform.rotation;
|
||||
}
|
||||
|
||||
/// <summary> Bone must have parent </summary>
|
||||
public Quaternion RotationTowardsDynamic(Vector3 toDir)
|
||||
{
|
||||
Quaternion fromTo = Quaternion.FromToRotation
|
||||
(
|
||||
(transform.position - transform.parent.position).normalized, // Forward direction
|
||||
(toDir).normalized // Look direction
|
||||
);
|
||||
|
||||
return fromTo * transform.rotation;
|
||||
}
|
||||
|
||||
public static float BlendAngle(float angle, float blend) { return Mathf.LerpAngle(0f, angle, blend); }
|
||||
|
||||
public Vector3 Dir(Vector3 forward) { return transform.TransformDirection(forward); }
|
||||
public Vector3 IDir(Vector3 forward) { return transform.InverseTransformDirection(forward); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f94b85e47ce668b44bbd07f3ae8737d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user