导入角色动画,和增加角色控制

This commit is contained in:
2025-12-11 19:30:20 +08:00
parent a60a92e7ba
commit 7775fa30bb
1452 changed files with 592217 additions and 42573 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9315a7a54d5b407cbdeba4a62a7a6d63
timeCreated: 1698339177

View File

@@ -0,0 +1,77 @@
// Designed by KINEMATION, 2024.
using System;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Attributes
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class CurveSelectorAttribute : PropertyAttribute
{
public bool useAnimator;
public bool usePlayables;
public bool useInput;
public CurveSelectorAttribute(bool useAnimator = true, bool usePlayables = true, bool useInput = true)
{
this.useAnimator = useAnimator;
this.usePlayables = usePlayables;
this.useInput = useInput;
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class InputProperty : PropertyAttribute { }
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class RigAssetSelectorAttribute : PropertyAttribute
{
public string assetName;
public RigAssetSelectorAttribute(string rigName = "")
{
assetName = rigName;
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class ElementChainSelectorAttribute : RigAssetSelectorAttribute
{
public ElementChainSelectorAttribute(string rigName = "")
{
assetName = rigName;
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class ReadOnlyAttribute : PropertyAttribute { }
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class UnfoldAttribute : PropertyAttribute { }
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class TabAttribute : PropertyAttribute
{
public string tabName;
public TabAttribute(string tabName)
{
this.tabName = tabName;
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class CustomElementChainDrawerAttribute : PropertyAttribute
{
public bool drawLabel;
public bool drawTextField;
public CustomElementChainDrawerAttribute(bool drawLabel, bool drawTextField)
{
this.drawLabel = drawLabel;
this.drawTextField = drawTextField;
}
}
public class KAttributes { }
}

View File

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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4a8a77a412af4ca69c2e184eab017b7f
timeCreated: 1698339164

View File

@@ -0,0 +1,438 @@
// Designed by KINEMATION, 2023
using KINEMATION.KAnimationCore.Runtime.Rig;
using UnityEngine;
using UnityEngine.Animations;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
public class KAnimationMath
{
public static Quaternion RotateInSpace(Quaternion space, Quaternion target, Quaternion rotation, float alpha)
{
return Quaternion.Slerp(target, space * rotation * (Quaternion.Inverse(space) * target), alpha);
}
public static Quaternion RotateInSpace(KTransform space, KTransform target, Quaternion offset, float alpha)
{
return RotateInSpace(space.rotation, target.rotation, offset, alpha);
}
public static void RotateInSpace(Transform space, Transform target, Quaternion offset, float alpha)
{
target.rotation = RotateInSpace(space.rotation, target.rotation, offset, alpha);
}
public static Vector3 MoveInSpace(KTransform space, KTransform target, Vector3 offset, float alpha)
{
return target.position + (space.TransformPoint(offset, false) - space.position) * alpha;
}
public static void MoveInSpace(Transform space, Transform target, Vector3 offset, float alpha)
{
target.position += (space.TransformPoint(offset) - space.position) * alpha;
}
public static Vector3 MoveInSpace(Transform space, Vector3 target, Vector3 offset, float alpha)
{
return target + (space.TransformPoint(offset) - space.position) * alpha;
}
public static KTransform GetTransform(AnimationStream stream, TransformStreamHandle handle,
bool isWorld = true)
{
if (!stream.isValid || !handle.IsValid(stream))
{
return KTransform.Identity;
}
KTransform output = new KTransform()
{
position = isWorld ? handle.GetPosition(stream) : handle.GetLocalPosition(stream),
rotation = isWorld ? handle.GetRotation(stream) : handle.GetLocalRotation(stream),
};
return output;
}
public static KTransform GetTransform(AnimationStream stream, TransformSceneHandle handle,
bool isWorld = true)
{
KTransform output = new KTransform()
{
position = isWorld ? handle.GetPosition(stream) : handle.GetLocalPosition(stream),
rotation = isWorld ? handle.GetRotation(stream) : handle.GetLocalRotation(stream),
};
return output;
}
public static void MoveInSpace(AnimationStream stream, TransformSceneHandle space,
TransformStreamHandle target, Vector3 offset, float weight)
{
KTransform spaceT = GetTransform(stream, space);
KTransform targetT = GetTransform(stream, target);
var result = MoveInSpace(spaceT, targetT, offset, weight);
target.SetPosition(stream, result);
}
public static void MoveInSpace(AnimationStream stream, TransformStreamHandle space,
TransformStreamHandle target, Vector3 offset, float weight)
{
KTransform spaceT = GetTransform(stream, space);
KTransform targetT = GetTransform(stream, target);
var result = MoveInSpace(spaceT, targetT, offset, weight);
target.SetPosition(stream, result);
}
public static void RotateInSpace(AnimationStream stream, TransformStreamHandle space,
TransformStreamHandle target, Quaternion offset, float weight)
{
KTransform spaceT = GetTransform(stream, space);
KTransform targetT = GetTransform(stream, target);
var result = RotateInSpace(spaceT, targetT, offset, weight);
target.SetRotation(stream, result);
}
public static void RotateInSpace(AnimationStream stream, TransformSceneHandle space,
TransformStreamHandle target, Quaternion offset, float weight)
{
KTransform spaceT = GetTransform(stream, space);
KTransform targetT = GetTransform(stream, target);
var result = RotateInSpace(spaceT, targetT, offset, weight);
target.SetRotation(stream, result);
}
public static void ModifyPosition(AnimationStream stream, TransformSceneHandle root, TransformStreamHandle bone,
Vector3 position, ESpaceType space, EModifyMode mode, float weight)
{
if (mode == EModifyMode.Ignore) return;
KTransform rootTransform = GetTransform(stream, root);
if (mode == EModifyMode.Add)
{
if (space == ESpaceType.BoneSpace)
{
MoveInSpace(stream, bone, bone, position, weight);
return;
}
if (space == ESpaceType.ParentBoneSpace)
{
var local = GetTransform(stream, bone, false);
bone.SetLocalPosition(stream, Vector3.Lerp(local.position,
local.position + position, weight));
return;
}
if (space == ESpaceType.ComponentSpace)
{
MoveInSpace(stream, root, bone, position, weight);
return;
}
KTransform world = GetTransform(stream, bone);
bone.SetPosition(stream,
Vector3.Lerp(world.position, world.position + position, weight));
return;
}
if (space is ESpaceType.BoneSpace or ESpaceType.ParentBoneSpace)
{
bone.SetLocalPosition(stream,
Vector3.Lerp(bone.GetLocalPosition(stream), position, weight));
return;
}
if (space == ESpaceType.ComponentSpace)
{
position = rootTransform.TransformPoint(position, false);
bone.SetPosition(stream, Vector3.Lerp(bone.GetPosition(stream), position, weight));
return;
}
bone.SetPosition(stream, Vector3.Lerp(bone.GetPosition(stream), position, weight));
}
public static void ModifyRotation(AnimationStream stream, TransformSceneHandle root, TransformStreamHandle bone,
Quaternion rotation, ESpaceType space, EModifyMode mode, float weight)
{
if (mode == EModifyMode.Ignore) return;
KTransform rootTransform = GetTransform(stream, root);
if (mode == EModifyMode.Add)
{
if (space == ESpaceType.BoneSpace)
{
RotateInSpace(stream, bone, bone, rotation, weight);
return;
}
if (space == ESpaceType.ParentBoneSpace)
{
var local = GetTransform(stream, bone, false);
bone.SetLocalRotation(stream, Quaternion.Slerp(local.rotation,
local.rotation * rotation, weight));
return;
}
if (space == ESpaceType.ComponentSpace)
{
RotateInSpace(stream, root, bone, rotation, weight);
return;
}
KTransform world = GetTransform(stream, bone);
bone.SetRotation(stream,
Quaternion.Slerp(world.rotation, world.rotation * rotation, weight));
return;
}
if (space is ESpaceType.BoneSpace or ESpaceType.ParentBoneSpace)
{
bone.SetLocalRotation(stream,
Quaternion.Slerp(bone.GetLocalRotation(stream), rotation, weight));
return;
}
if (space == ESpaceType.ComponentSpace)
{
rotation = rootTransform.rotation * rotation;
bone.SetRotation(stream, Quaternion.Slerp(bone.GetRotation(stream), rotation, weight));
return;
}
bone.SetRotation(stream, Quaternion.Slerp(bone.GetRotation(stream), rotation,
weight));
}
public static void ModifyTransform(AnimationStream stream, TransformSceneHandle root,
TransformStreamHandle target, KPose pose, float weight)
{
KTransform rootTransform = GetTransform(stream, root);
if (pose.modifyMode == EModifyMode.Add)
{
if (pose.space == ESpaceType.BoneSpace)
{
MoveInSpace(stream, target, target, pose.pose.position, weight);
RotateInSpace(stream, target, target, pose.pose.rotation, weight);
return;
}
if (pose.space == ESpaceType.ParentBoneSpace)
{
var local = GetTransform(stream, target, false);
target.SetLocalPosition(stream, Vector3.Lerp(local.position,
local.position + pose.pose.position, weight));
target.SetLocalRotation(stream, Quaternion.Slerp(local.rotation,
local.rotation * pose.pose.rotation, weight));
return;
}
if (pose.space == ESpaceType.ComponentSpace)
{
MoveInSpace(stream, root, target, pose.pose.position, weight);
RotateInSpace(stream, root, target, pose.pose.rotation, weight);
return;
}
KTransform world = GetTransform(stream, target);
target.SetPosition(stream,
Vector3.Lerp(world.position, world.position + pose.pose.position, weight));
target.SetRotation(stream,
Quaternion.Slerp(world.rotation, world.rotation * pose.pose.rotation, weight));
return;
}
if (pose.space is ESpaceType.BoneSpace or ESpaceType.ParentBoneSpace)
{
target.SetLocalPosition(stream,
Vector3.Lerp(target.GetLocalPosition(stream), pose.pose.position, weight));
target.SetLocalRotation(stream,
Quaternion.Slerp(target.GetLocalRotation(stream), pose.pose.rotation, weight));
return;
}
if (pose.space == ESpaceType.ComponentSpace)
{
var worldTransform = rootTransform.GetWorldTransform(pose.pose, false);
worldTransform = KTransform.Lerp(GetTransform(stream, target), worldTransform, weight);
target.SetPosition(stream, worldTransform.position);
target.SetRotation(stream, worldTransform.rotation);
return;
}
target.SetPosition(stream,
Vector3.Lerp(target.GetPosition(stream), pose.pose.position, weight));
target.SetRotation(stream, Quaternion.Slerp(target.GetRotation(stream), pose.pose.rotation,
weight));
}
public static void ModifyTransform(AnimationStream stream, TransformStreamHandle root,
TransformStreamHandle target, KPose pose, float weight)
{
KTransform rootTransform = GetTransform(stream, root);
if (pose.modifyMode == EModifyMode.Add)
{
if (pose.space == ESpaceType.BoneSpace)
{
MoveInSpace(stream, target, target, pose.pose.position, weight);
RotateInSpace(stream, target, target, pose.pose.rotation, weight);
return;
}
if (pose.space == ESpaceType.ParentBoneSpace)
{
var local = GetTransform(stream, target, false);
target.SetLocalPosition(stream, Vector3.Lerp(local.position,
local.position + pose.pose.position, weight));
target.SetLocalRotation(stream, Quaternion.Slerp(local.rotation,
local.rotation * pose.pose.rotation, weight));
return;
}
if (pose.space == ESpaceType.ComponentSpace)
{
MoveInSpace(stream, root, target, pose.pose.position, weight);
RotateInSpace(stream, root, target, pose.pose.rotation, weight);
return;
}
KTransform world = GetTransform(stream, target);
target.SetPosition(stream,
Vector3.Lerp(world.position, world.position + pose.pose.position, weight));
target.SetRotation(stream,
Quaternion.Slerp(world.rotation, world.rotation * pose.pose.rotation, weight));
return;
}
if (pose.space is ESpaceType.BoneSpace or ESpaceType.ParentBoneSpace)
{
target.SetLocalPosition(stream,
Vector3.Lerp(target.GetLocalPosition(stream), pose.pose.position, weight));
target.SetLocalRotation(stream,
Quaternion.Slerp(target.GetLocalRotation(stream), pose.pose.rotation, weight));
return;
}
if (pose.space == ESpaceType.ComponentSpace)
{
var worldTransform = rootTransform.GetWorldTransform(pose.pose, false);
target.SetPosition(stream, worldTransform.position);
target.SetRotation(stream, worldTransform.rotation);
return;
}
target.SetPosition(stream,
Vector3.Lerp(target.GetPosition(stream), pose.pose.position, weight));
target.SetRotation(stream, Quaternion.Slerp(target.GetRotation(stream), pose.pose.rotation,
weight));
}
// Copies a bone pose in world space.
public static void CopyBone(AnimationStream stream, TransformStreamHandle from, TransformStreamHandle to,
float weight = 1f)
{
to.SetPosition(stream, Vector3.Lerp(to.GetPosition(stream), from.GetPosition(stream), weight));
to.SetRotation(stream, Quaternion.Slerp(to.GetRotation(stream), from.GetRotation(stream), weight));
}
// Copies a bone pose in world space.
public static void CopyBone(AnimationStream stream, TransformSceneHandle from, TransformStreamHandle to,
float weight = 1f)
{
to.SetPosition(stream, Vector3.Lerp(to.GetPosition(stream), from.GetPosition(stream), weight));
to.SetRotation(stream, Quaternion.Slerp(to.GetRotation(stream), from.GetRotation(stream), weight));
}
public static bool IsWeightFull(float weight)
{
return Mathf.Approximately(weight, 1f);
}
public static bool IsWeightRelevant(float weight)
{
return !Mathf.Approximately(weight, 0f);
}
public static void ModifyTransform(Transform component, Transform target, in KPose pose, float alpha = 1f)
{
if (pose.modifyMode == EModifyMode.Add)
{
AddTransform(component, target, in pose, alpha);
return;
}
ReplaceTransform(component, target, in pose, alpha);
}
private static void AddTransform(Transform component, Transform target, in KPose pose, float alpha = 1f)
{
if (pose.space == ESpaceType.BoneSpace)
{
MoveInSpace(target, target, pose.pose.position, alpha);
RotateInSpace(target, target, pose.pose.rotation, alpha);
return;
}
if (pose.space == ESpaceType.ParentBoneSpace)
{
Transform parent = target.parent;
MoveInSpace(parent, target, pose.pose.position, alpha);
RotateInSpace(parent, target, pose.pose.rotation, alpha);
return;
}
if (pose.space == ESpaceType.ComponentSpace)
{
MoveInSpace(component, target, pose.pose.position, alpha);
RotateInSpace(component, target, pose.pose.rotation, alpha);
return;
}
Vector3 position = target.position;
Quaternion rotation = target.rotation;
target.position = Vector3.Lerp(position, position + pose.pose.position, alpha);
target.rotation = Quaternion.Slerp(rotation, rotation * pose.pose.rotation, alpha);
}
private static void ReplaceTransform(Transform component, Transform target, in KPose pose, float alpha = 1f)
{
if (pose.space == ESpaceType.BoneSpace || pose.space == ESpaceType.ParentBoneSpace)
{
target.localPosition = Vector3.Lerp(target.localPosition, pose.pose.position, alpha);
target.localRotation = Quaternion.Slerp(target.localRotation, pose.pose.rotation, alpha);
return;
}
if (pose.space == ESpaceType.ComponentSpace)
{
target.position = Vector3.Lerp(target.position, component.TransformPoint(pose.pose.position), alpha);
target.rotation = Quaternion.Slerp(target.rotation, component.rotation * pose.pose.rotation, alpha);
return;
}
target.position = Vector3.Lerp(target.position, pose.pose.position, alpha);
target.rotation = Quaternion.Slerp(target.rotation, pose.pose.rotation, alpha);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4980d4c84030441c922128e9a378b5fb
timeCreated: 1698338913

View File

@@ -0,0 +1,71 @@
// Designed by KINEMATION, 2024.
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
public struct ChainIKData
{
public Vector3[] positions;
public float[] lengths;
public Vector3 target;
public float tolerance;
public float maxReach;
public int maxIterations;
}
public class KChainIK
{
public static bool SolveFABRIK(ref ChainIKData ikData)
{
// If the target is unreachable
var rootToTargetDir = ikData.target - ikData.positions[0];
if (rootToTargetDir.sqrMagnitude > KMath.Square(ikData.maxReach))
{
// Line up chain towards target
var dir = rootToTargetDir.normalized;
for (int i = 1; i < ikData.positions.Length; ++i)
{
ikData.positions[i] = ikData.positions[i - 1] + dir * ikData.lengths[i - 1];
}
return true;
}
int tipIndex = ikData.positions.Length - 1;
float sqrTolerance = KMath.Square(ikData.tolerance);
if (KMath.SqrDistance(ikData.positions[tipIndex], ikData.target) > sqrTolerance)
{
var rootPos = ikData.positions[0];
int iteration = 0;
do
{
// Forward reaching phase
// Set tip to target and propagate displacement to rest of chain
ikData.positions[tipIndex] = ikData.target;
for (int i = tipIndex - 1; i > -1; --i)
{
ikData.positions[i] = ikData.positions[i + 1] +
((ikData.positions[i] - ikData.positions[i + 1]).normalized *
ikData.lengths[i]);
}
// Backward reaching phase
// Set root back at it's original position and propagate displacement to rest of chain
ikData.positions[0] = rootPos;
for (int i = 1; i < ikData.positions.Length; ++i)
ikData.positions[i] = ikData.positions[i - 1] +
((ikData.positions[i] - ikData.positions[i - 1]).normalized * ikData.lengths[i - 1]);
} while ((KMath.SqrDistance(ikData.positions[tipIndex], ikData.target) > sqrTolerance) &&
(++iteration < ikData.maxIterations));
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1308cae72dcf420ab9a43892668bf485
timeCreated: 1716450134

View File

@@ -0,0 +1,154 @@
// Designed by KINEMATION, 2024.
using System;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
[Serializable]
public struct VectorCurve
{
public AnimationCurve x;
public AnimationCurve y;
public AnimationCurve z;
public static VectorCurve Linear(float timeStart, float timeEnd, float valueStart, float valueEnd)
{
VectorCurve result = new VectorCurve()
{
x = AnimationCurve.Linear(timeStart, timeEnd, valueStart, valueEnd),
y = AnimationCurve.Linear(timeStart, timeEnd, valueStart, valueEnd),
z = AnimationCurve.Linear(timeStart, timeEnd, valueStart, valueEnd)
};
return result;
}
public static VectorCurve Constant(float timeStart, float timeEnd, float value)
{
VectorCurve result = new VectorCurve()
{
x = AnimationCurve.Constant(timeStart, timeEnd, value),
y = AnimationCurve.Constant(timeStart, timeEnd, value),
z = AnimationCurve.Constant(timeStart, timeEnd, value)
};
return result;
}
public float GetCurveLength()
{
float maxTime = -1f;
float curveTime = KCurves.GetCurveLength(x);
maxTime = curveTime > maxTime ? curveTime : maxTime;
curveTime = KCurves.GetCurveLength(y);
maxTime = curveTime > maxTime ? curveTime : maxTime;
curveTime = KCurves.GetCurveLength(z);
maxTime = curveTime > maxTime ? curveTime : maxTime;
return maxTime;
}
public Vector3 GetValue(float time)
{
return new Vector3(x.Evaluate(time), y.Evaluate(time), z.Evaluate(time));
}
public Vector3 GetLastValue()
{
float length = GetCurveLength();
return GetValue(length);
}
public bool IsValid()
{
return x != null && y != null && z != null;
}
public VectorCurve(Keyframe[] keyFrame)
{
x = new AnimationCurve(keyFrame);
y = new AnimationCurve(keyFrame);
z = new AnimationCurve(keyFrame);
}
}
[Serializable]
public enum EEaseFunc
{
Linear,
Sine,
Cubic,
Custom
}
[Serializable]
public struct EaseMode
{
public EEaseFunc easeFunc;
public AnimationCurve curve;
public EaseMode(EEaseFunc func)
{
easeFunc = func;
curve = AnimationCurve.Linear(0f, 0f, 1f, 0f);
}
}
public class KCurves
{
public static float GetCurveLength(AnimationCurve curve)
{
float length = 0f;
if (curve != null)
{
length = curve[curve.length - 1].time;
}
return length;
}
public static float EaseSine(float a, float b, float alpha)
{
return Mathf.Lerp(a, b, -(Mathf.Cos(Mathf.PI * alpha) - 1) / 2);
}
public static float EaseCubic(float a, float b, float alpha)
{
alpha = alpha < 0.5 ? 4 * alpha * alpha * alpha : 1 - Mathf.Pow(-2 * alpha + 2, 3) / 2;
return Mathf.Lerp(a, b, alpha);
}
public static float EaseCurve(float a, float b, float alpha, AnimationCurve curve)
{
alpha = curve?.Evaluate(alpha) ?? alpha;
return Mathf.Lerp(a, b, alpha);
}
public static float Ease(float a, float b, float alpha, EaseMode ease)
{
alpha = Mathf.Clamp01(alpha);
if (ease.easeFunc == EEaseFunc.Sine)
{
return EaseSine(a, b, alpha);
}
if (ease.easeFunc == EEaseFunc.Cubic)
{
return EaseCubic(a, b, alpha);
}
if (ease.easeFunc == EEaseFunc.Custom)
{
return EaseCurve(a, b, alpha, ease.curve);
}
return Mathf.Lerp(a, b, alpha);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4594292dc1514661a692348534d3285c
timeCreated: 1698343937

View File

@@ -0,0 +1,104 @@
// Designed by KINEMATION, 2024.
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
public class KMath
{
public const float FloatMin = 1e-10f;
public const float SqrEpsilon = 1e-8f;
public static float Square(float value)
{
return value * value;
}
public static float SqrDistance(Vector3 a, Vector3 b)
{
return (b - a).sqrMagnitude;
}
public static float NormalizeEulerAngle(float angle)
{
while (angle < -180f) angle += 360f;
while (angle >= 180f) angle -= 360f;
return angle;
}
public static float TriangleAngle(float aLen, float aLen1, float aLen2)
{
float c = Mathf.Clamp((aLen1 * aLen1 + aLen2 * aLen2 - aLen * aLen) / (aLen1 * aLen2) / 2.0f, -1.0f, 1.0f);
return Mathf.Acos(c);
}
public static Quaternion FromToRotation(Vector3 from, Vector3 to)
{
float theta = Vector3.Dot(from.normalized, to.normalized);
if (theta >= 1f) return Quaternion.identity;
if (theta <= -1f)
{
Vector3 axis = Vector3.Cross(from, Vector3.right);
if (axis.sqrMagnitude == 0f) axis = Vector3.Cross(from, Vector3.up);
return Quaternion.AngleAxis(180f, axis);
}
return Quaternion.AngleAxis(Mathf.Acos(theta) * Mathf.Rad2Deg, Vector3.Cross(from, to).normalized);
}
public static Quaternion NormalizeSafe(Quaternion q)
{
float dot = Quaternion.Dot(q, q);
if (dot > FloatMin)
{
float rsqrt = 1.0f / Mathf.Sqrt(dot);
return new Quaternion(q.x * rsqrt, q.y * rsqrt, q.z * rsqrt, q.w * rsqrt);
}
return Quaternion.identity;
}
public static float InvLerp(float value, float a, float b)
{
float alpha = 0f;
if (!Mathf.Approximately(a, b))
{
alpha = (value - a) / (b - a);
}
return Mathf.Clamp01(alpha);
}
public static float ExpDecayAlpha(float speed, float deltaTime)
{
return 1 - Mathf.Exp(-speed * deltaTime);
}
public static float FloatInterp(float a, float b, float speed, float deltaTime)
{
return speed > 0f ? Mathf.Lerp(a, b, ExpDecayAlpha(speed, deltaTime)) : b;
}
public static Quaternion SmoothSlerp(Quaternion a, Quaternion b, float speed, float deltaTime)
{
return speed > 0f ? Quaternion.Slerp(a, b, ExpDecayAlpha(speed, deltaTime)) : b;
}
public static Vector2 ComputeLookAtInput(Transform root, Transform from, Transform to)
{
Vector2 result = Vector2.zero;
Quaternion rot = Quaternion.LookRotation(to.position - from.position);
rot = Quaternion.Inverse(root.rotation) * rot;
Vector3 euler = rot.eulerAngles;
result.x = NormalizeEulerAngle(euler.x);
result.y = NormalizeEulerAngle(euler.y);
return result;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bd8cdd8e10864049b6b1119a7c3cc296
timeCreated: 1704002312

View File

@@ -0,0 +1,87 @@
// Designed by KINEMATION, 2023
using System;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
public struct FloatSpringState
{
public float velocity;
public float error;
public void Reset()
{
error = velocity = 0f;
}
}
public struct VectorSpringState
{
public FloatSpringState x;
public FloatSpringState y;
public FloatSpringState z;
public void Reset()
{
x.Reset();
y.Reset();
z.Reset();
}
}
[Serializable]
public struct VectorSpring
{
public Vector3 damping;
public Vector3 stiffness;
public Vector3 speed;
public Vector3 scale;
public static VectorSpring identity = new VectorSpring()
{
damping = Vector3.zero,
stiffness = Vector3.zero,
speed = Vector3.zero,
scale = Vector3.zero
};
}
public class KSpringMath
{
public static float FloatSpringInterp(float current, float target, float speed, float criticalDamping,
float stiffness, float scale, ref FloatSpringState state, float deltaTime)
{
float interpSpeed = Mathf.Min(deltaTime * speed, 1f);
if (!Mathf.Approximately(interpSpeed, 0f))
{
float damping = 2 * Mathf.Sqrt(stiffness) * criticalDamping;
float error = target * scale - current;
float errorDeriv = error - state.error;
state.velocity += error * stiffness * interpSpeed + errorDeriv * damping;
state.error = error;
float value = current + state.velocity * interpSpeed;
return value;
}
return current;
}
public static Vector3 VectorSpringInterp(Vector3 current, in Vector3 target, in VectorSpring spring,
ref VectorSpringState state, float deltaTime)
{
current.x = FloatSpringInterp(current.x, target.x, spring.speed.x,
spring.damping.x, spring.stiffness.x, spring.scale.x, ref state.x, deltaTime);
current.y = FloatSpringInterp(current.y, target.y, spring.speed.y,
spring.damping.y, spring.stiffness.y, spring.scale.y, ref state.y, deltaTime);
current.z = FloatSpringInterp(current.z, target.z, spring.speed.z,
spring.damping.z, spring.stiffness.z, spring.scale.z, ref state.z, deltaTime);
return current;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b715a4ee42b646169c5ce859e6d34964
timeCreated: 1698339846

View File

@@ -0,0 +1,153 @@
// Designed by KINEMATION, 2024
using System;
using UnityEngine;
using Quaternion = UnityEngine.Quaternion;
using Vector3 = UnityEngine.Vector3;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
// Struct alternative for Transform.
[Serializable]
public struct KTransform
{
public static KTransform Identity = new(Vector3.zero, Quaternion.identity, Vector3.one);
public Vector3 position;
public Quaternion rotation;
public Vector3 scale;
public KTransform(Vector3 newPos, Quaternion newRot, Vector3 newScale)
{
position = newPos;
rotation = newRot;
scale = newScale;
}
public KTransform(Vector3 newPos, Quaternion newRot)
{
position = newPos;
rotation = newRot;
scale = Vector3.one;
}
public KTransform(Transform t, bool worldSpace = true)
{
if (worldSpace)
{
position = t.position;
rotation = t.rotation;
}
else
{
position = t.localPosition;
rotation = t.localRotation;
}
scale = t.localScale;
}
// Linearly interpolates translation and scale. Spherically interpolates rotation.
public static KTransform Lerp(KTransform a, KTransform b, float alpha)
{
Vector3 outPos = Vector3.Lerp(a.position, b.position, alpha);
Quaternion outRot = Quaternion.Slerp(a.rotation, b.rotation, alpha);
Vector3 outScale = Vector3.Lerp(a.scale, a.scale, alpha);
return new KTransform(outPos, outRot, outScale);
}
public static KTransform EaseLerp(KTransform a, KTransform b, float alpha, EaseMode easeMode)
{
return Lerp(a, b, KCurves.Ease(0f, 1f, alpha, easeMode));
}
// Frame-rate independent interpolation.
public static KTransform ExpDecay(KTransform a, KTransform b, float speed, float deltaTime)
{
return Lerp(a, b, KMath.ExpDecayAlpha(speed, deltaTime));
}
public bool Equals(KTransform other, bool useScale)
{
bool result = position.Equals(other.position) && rotation.Equals(other.rotation);
if (useScale)
{
result = result && scale.Equals(other.scale);
}
return result;
}
// Returns a point relative to this transform.
public Vector3 InverseTransformPoint(Vector3 worldPosition, bool useScale)
{
Vector3 result = Quaternion.Inverse(rotation) * (worldPosition - position);
if (useScale)
{
result = Vector3.Scale(scale, result);
}
return result;
}
// Returns a vector relative to this transform.
public Vector3 InverseTransformVector(Vector3 worldDirection, bool useScale)
{
Vector3 result = Quaternion.Inverse(rotation) * worldDirection;
if (useScale)
{
result = Vector3.Scale(scale, result);
}
return result;
}
// Converts a local position from this transform to world.
public Vector3 TransformPoint(Vector3 localPosition, bool useScale)
{
if (useScale)
{
localPosition = Vector3.Scale(scale, localPosition);
}
return position + rotation * localPosition;
}
// Converts a local vector from this transform to world.
public Vector3 TransformVector(Vector3 localDirection, bool useScale)
{
if (useScale)
{
localDirection = Vector3.Scale(scale, localDirection);
}
return rotation * localDirection;
}
// Returns a transform relative to this transform.
public KTransform GetRelativeTransform(KTransform worldTransform, bool useScale)
{
return new KTransform()
{
position = InverseTransformPoint(worldTransform.position, useScale),
rotation = Quaternion.Inverse(rotation) * worldTransform.rotation,
scale = Vector3.Scale(scale, worldTransform.scale)
};
}
// Converts a local transform to world.
public KTransform GetWorldTransform(KTransform localTransform, bool useScale)
{
return new KTransform()
{
position = TransformPoint(localTransform.position, useScale),
rotation = rotation * localTransform.rotation,
scale = Vector3.Scale(scale, localTransform.scale)
};
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8ef53e57347421a8fcc4468bd018afd
timeCreated: 1698341198

View File

@@ -0,0 +1,121 @@
// Designed by KINEMATION, 2023
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
public struct KTwoBoneIkData
{
public KTransform root;
public KTransform mid;
public KTransform tip;
public KTransform target;
public KTransform hint;
public float posWeight;
public float rotWeight;
public float hintWeight;
public bool hasValidHint;
}
public class KTwoBoneIK
{
public static void Solve(ref KTwoBoneIkData ikData)
{
Vector3 aPosition = ikData.root.position;
Vector3 bPosition = ikData.mid.position;
Vector3 cPosition = ikData.tip.position;
Vector3 tPosition = Vector3.Lerp(cPosition, ikData.target.position, ikData.posWeight);
Quaternion tRotation = Quaternion.Slerp(ikData.tip.rotation, ikData.target.rotation, ikData.rotWeight);
bool hasHint = ikData.hasValidHint && ikData.hintWeight > 0f;
Vector3 ab = bPosition - aPosition;
Vector3 bc = cPosition - bPosition;
Vector3 ac = cPosition - aPosition;
Vector3 at = tPosition - aPosition;
float abLen = ab.magnitude;
float bcLen = bc.magnitude;
float acLen = ac.magnitude;
float atLen = at.magnitude;
float oldAbcAngle = KMath.TriangleAngle(acLen, abLen, bcLen);
float newAbcAngle = KMath.TriangleAngle(atLen, abLen, bcLen);
// Bend normal strategy is to take whatever has been provided in the animation
// stream to minimize configuration changes, however if this is collinear
// try computing a bend normal given the desired target position.
// If this also fails, try resolving axis using hint if provided.
Vector3 axis = Vector3.Cross(ab, bc);
if (axis.sqrMagnitude < KMath.SqrEpsilon)
{
axis = hasHint ? Vector3.Cross(ikData.hint.position - aPosition, bc) : Vector3.zero;
if (axis.sqrMagnitude < KMath.SqrEpsilon)
axis = Vector3.Cross(at, bc);
if (axis.sqrMagnitude < KMath.SqrEpsilon)
axis = Vector3.up;
}
axis = Vector3.Normalize(axis);
float a = 0.5f * (oldAbcAngle - newAbcAngle);
float sin = Mathf.Sin(a);
float cos = Mathf.Cos(a);
Quaternion deltaR = new Quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos);
KTransform localTip = ikData.mid.GetRelativeTransform(ikData.tip, false);
ikData.mid.rotation = deltaR * ikData.mid.rotation;
// Update child transform.
ikData.tip = ikData.mid.GetWorldTransform(localTip, false);
cPosition = ikData.tip.position;
ac = cPosition - aPosition;
KTransform localMid = ikData.root.GetRelativeTransform(ikData.mid, false);
localTip = ikData.mid.GetRelativeTransform(ikData.tip, false);
ikData.root.rotation = KMath.FromToRotation(ac, at) * ikData.root.rotation;
// Update child transforms.
ikData.mid = ikData.root.GetWorldTransform(localMid, false);
ikData.tip = ikData.mid.GetWorldTransform(localTip, false);
if (hasHint)
{
float acSqrMag = ac.sqrMagnitude;
if (acSqrMag > 0f)
{
bPosition = ikData.mid.position;
cPosition = ikData.tip.position;
ab = bPosition - aPosition;
ac = cPosition - aPosition;
Vector3 acNorm = ac / Mathf.Sqrt(acSqrMag);
Vector3 ah = ikData.hint.position - aPosition;
Vector3 abProj = ab - acNorm * Vector3.Dot(ab, acNorm);
Vector3 ahProj = ah - acNorm * Vector3.Dot(ah, acNorm);
float maxReach = abLen + bcLen;
if (abProj.sqrMagnitude > (maxReach * maxReach * 0.001f) && ahProj.sqrMagnitude > 0f)
{
Quaternion hintR = KMath.FromToRotation(abProj, ahProj);
hintR.x *= ikData.hintWeight;
hintR.y *= ikData.hintWeight;
hintR.z *= ikData.hintWeight;
hintR = KMath.NormalizeSafe(hintR);
ikData.root.rotation = hintR * ikData.root.rotation;
ikData.mid = ikData.root.GetWorldTransform(localMid, false);
ikData.tip = ikData.mid.GetWorldTransform(localTip, false);
}
}
}
ikData.tip.rotation = tRotation;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8d99a3775c3e469dbe146569b35cd7a6
timeCreated: 1698338938

View File

@@ -0,0 +1,53 @@
// Designed by KINEMATION, 2024.
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Core
{
public class SetFloat : StateMachineBehaviour
{
[SerializeField] private string paramName;
[SerializeField] private float paramTargetValue;
[SerializeField] private EaseMode easeMode = new EaseMode(EEaseFunc.Linear);
private int _paramId;
private float _paramStartValue;
private bool _isInitialized;
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if (!_isInitialized)
{
_paramId = Animator.StringToHash(paramName);
_isInitialized = true;
}
_paramStartValue = animator.GetFloat(_paramId);
}
public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
int nextHash = animator.GetNextAnimatorStateInfo(layerIndex).fullPathHash;
if (nextHash != stateInfo.fullPathHash && nextHash != 0)
{
return;
}
float alpha = 0f;
if (animator.IsInTransition(layerIndex))
{
alpha = animator.GetAnimatorTransitionInfo(layerIndex).normalizedTime;
}
else
{
alpha = 1f;
}
animator.SetFloat(_paramId, KCurves.Ease(_paramStartValue, paramTargetValue, alpha, easeMode));
}
public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d4437088df934c1a9375bf492c23d011
timeCreated: 1709881756

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9d01e94a2fa34eba9f2ebd278987b1dc
timeCreated: 1707394003

View File

@@ -0,0 +1,20 @@
using System;
namespace KINEMATION.KAnimationCore.Runtime.Input
{
[Obsolete("use `UserInputController` instead.")]
public interface IUserInputController
{
public void Initialize();
public int GetPropertyIndex(string propertyName);
public void SetValue(string propertyName, object value);
public T GetValue<T>(string propertyName);
public void SetValue(int propertyIndex, object value);
public T GetValue<T>(int propertyIndex);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca9ff9d900444c4785ba2de65c0e0263
timeCreated: 1707563078

View File

@@ -0,0 +1,34 @@
using System;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Input
{
[Serializable]
public struct BoolProperty
{
public string name;
public bool defaultValue;
}
[Serializable]
public struct IntProperty
{
public string name;
public int defaultValue;
}
[Serializable]
public struct FloatProperty
{
public string name;
public float defaultValue;
public float interpolationSpeed;
}
[Serializable]
public struct VectorProperty
{
public string name;
public Vector4 defaultValue;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 37f35ec97fc94416a139ca21ae59beda
timeCreated: 1707463958

View File

@@ -0,0 +1,16 @@
// Designed by KINEMATION, 2024.
using System.Collections.Generic;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Input
{
[CreateAssetMenu(fileName = "NewInputConfig", menuName = "KINEMATION/Input Config")]
public class UserInputConfig : ScriptableObject
{
public List<IntProperty> intProperties = new List<IntProperty>();
public List<FloatProperty> floatProperties = new List<FloatProperty>();
public List<BoolProperty> boolProperties = new List<BoolProperty>();
public List<VectorProperty> vectorProperties = new List<VectorProperty>();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e8993601b7c484b862c9c567134ed2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a497183ade831dc4aa44bf44b5ce27b8, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,194 @@
// Designed by KINEMATION, 2024.
using KINEMATION.KAnimationCore.Runtime.Core;
using System.Collections.Generic;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Input
{
public class UserInputController : MonoBehaviour
{
[SerializeField] public UserInputConfig inputConfig;
protected List<object> _inputProperties;
protected Dictionary<string, int> _inputPropertyMap;
protected (int, float, float)[] _floatsToInterpolate;
public UserInputConfig GetConfig()
{
return inputConfig;
}
protected virtual void Update()
{
if (_floatsToInterpolate == null) return;
foreach (var tuple in _floatsToInterpolate)
{
float value = (float) _inputProperties[tuple.Item1];
if (Mathf.Approximately(value, tuple.Item3))
{
value = tuple.Item3;
}
else
{
float alpha = KMath.ExpDecayAlpha(Time.deltaTime, tuple.Item2);
value = Mathf.LerpUnclamped(value, tuple.Item3, alpha);
}
_inputProperties[tuple.Item1] = value;
}
}
public virtual void Initialize()
{
#if UNITY_EDITOR
_propertyNames = new List<(string, object)>();
#endif
_inputProperties = new List<object>();
_inputPropertyMap = new Dictionary<string, int>();
List<(int, float, float)> floatsToInterpolate = new List<(int, float, float)>();
int index = 0;
foreach (var property in inputConfig.boolProperties)
{
_inputProperties.Add(property.defaultValue);
_inputPropertyMap.TryAdd(property.name, index);
index++;
#if UNITY_EDITOR
_propertyNames.Add((property.name, null));
#endif
}
foreach (var property in inputConfig.intProperties)
{
_inputProperties.Add(property.defaultValue);
_inputPropertyMap.TryAdd(property.name, index);
index++;
#if UNITY_EDITOR
_propertyNames.Add((property.name, null));
#endif
}
foreach (var property in inputConfig.floatProperties)
{
_inputProperties.Add(property.defaultValue);
_inputPropertyMap.TryAdd(property.name, index);
if (!Mathf.Approximately(property.interpolationSpeed, 0f))
{
floatsToInterpolate.Add((index, property.interpolationSpeed, property.defaultValue));
}
index++;
#if UNITY_EDITOR
_propertyNames.Add((property.name, null));
#endif
}
if (floatsToInterpolate.Count > 0)
{
_floatsToInterpolate = floatsToInterpolate.ToArray();
}
foreach (var property in inputConfig.vectorProperties)
{
_inputProperties.Add(property.defaultValue);
_inputPropertyMap.TryAdd(property.name, index);
index++;
#if UNITY_EDITOR
_propertyNames.Add((property.name, null));
#endif
}
}
public int GetPropertyIndex(string propertyName)
{
if (_inputPropertyMap.TryGetValue(propertyName, out int index))
{
return index;
}
return -1;
}
public virtual void SetValue(string propertyName, object value)
{
SetValue(GetPropertyIndex(propertyName), value);
}
public virtual T GetValue<T>(string propertyName)
{
return GetValue<T>(GetPropertyIndex(propertyName));
}
public virtual void SetValue(int propertyIndex, object value)
{
if (propertyIndex < 0 || propertyIndex > _inputProperties.Count - 1)
{
return;
}
if (_floatsToInterpolate != null)
{
int floatToInterpolateIndex = -1;
for (int i = 0; i < _floatsToInterpolate.Length; i++)
{
if (_floatsToInterpolate[i].Item1 == propertyIndex)
{
floatToInterpolateIndex = i;
}
}
if (floatToInterpolateIndex != -1)
{
var tuple = _floatsToInterpolate[floatToInterpolateIndex];
tuple.Item3 = (float) value;
_floatsToInterpolate[floatToInterpolateIndex] = tuple;
return;
}
}
_inputProperties[propertyIndex] = value;
}
public virtual T GetValue<T>(int propertyIndex)
{
if (propertyIndex < 0 || propertyIndex > _inputProperties.Count - 1)
{
return default(T);
}
return (T) _inputProperties[propertyIndex];
}
#if UNITY_EDITOR
protected List<(string, object)> _propertyNames;
public virtual (string, object)[] GetPropertyBindings()
{
if (_propertyNames == null) return null;
int count = _propertyNames.Count;
for (int i = 0; i < count; i++)
{
var item = _propertyNames[i];
item.Item2 = _inputProperties[i];
_propertyNames[i] = item;
}
return _propertyNames.ToArray();
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26b2896c936d4be59e98d9bf358c5a96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 6fd4d99d3a1edc7408fe599214be6059, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
{
"name": "KAnimationCore.Runtime"
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d8548a9d25a091541a1fcde53694c91a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 373787767ed74d69a69333922d6c13a8
timeCreated: 1744450603

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Misc
{
public interface IAssetDragAndDrop
{
public void SetAsset(ScriptableObject asset);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6651ef9c95614dd687f3bab919072187
timeCreated: 1744450620

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 51e3869638134e4db26201aca4fc9096
timeCreated: 1704099380

View File

@@ -0,0 +1,7 @@
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
public interface IRigObserver
{
public void OnRigUpdated();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00acdb04eae148a0b993a37cb2b95007
timeCreated: 1708431186

View File

@@ -0,0 +1,7 @@
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
public interface IRigProvider
{
public KRigElement[] GetHierarchy();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8c27f9465994402da2a00b6e82c4a580
timeCreated: 1744271712

View File

@@ -0,0 +1,8 @@
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
public interface IRigUser
{
// Must return a reference to the used Rig Asset.
public KRig GetRigAsset();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 906523edc5b146fb8837b1804a606af5
timeCreated: 1705907873

View File

@@ -0,0 +1,32 @@
using System;
using KINEMATION.KAnimationCore.Runtime.Core;
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
// Represents the space we will modify bone transform in.
public enum ESpaceType
{
BoneSpace,
ParentBoneSpace,
ComponentSpace,
WorldSpace
}
// Whether the operation is additive or absolute.
public enum EModifyMode
{
Add,
Replace,
Ignore
}
// Represents the pose for the specific rig element.
[Serializable]
public struct KPose
{
public KRigElement element;
public KTransform pose;
public ESpaceType space;
public EModifyMode modifyMode;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 054feb4bdc8a463ea7b6aba306d8cd64
timeCreated: 1704783390

View File

@@ -0,0 +1,138 @@
// Designed by KINEMATION, 2024.
using KINEMATION.KAnimationCore.Runtime.Input;
using System.Collections.Generic;
using KINEMATION.KAnimationCore.Runtime.Attributes;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
public abstract class KRigBase : ScriptableObject, IRigProvider
{
public RuntimeAnimatorController targetAnimator;
public List<KRigElement> rigHierarchy = new List<KRigElement>();
[CustomElementChainDrawer(false, true)]
public List<KRigElementChain> rigElementChains = new List<KRigElementChain>();
public KRigElement[] GetHierarchy()
{
return rigHierarchy.ToArray();
}
}
// Character skeleton asset.
public class KRig : KRigBase
{
public UserInputConfig inputConfig;
public List<string> rigCurves = new List<string>();
public KRigElementChain GetElementChainByName(string chainName)
{
var chain = rigElementChains.Find(item => item.chainName.Equals(chainName));
return chain;
}
public KTransformChain GetPopulatedChain(string chainName, KRigComponent rigComponent)
{
KTransformChain result = new KTransformChain();
var targetChain = GetElementChainByName(chainName);
if (targetChain == null)
{
Debug.LogError($"Rig `{name}`: `{chainName}` chain not found!");
return null;
}
foreach (var element in targetChain.elementChain)
{
result.transformChain.Add(rigComponent.GetRigTransform(element));
}
return result;
}
#if UNITY_EDITOR
public List<int> rigDepths = new List<int>();
private List<Object> _rigObservers = new List<Object>();
private void OnEnable()
{
// Force update rig depths for compatibility reasons.
int count = rigHierarchy.Count;
for (int i = 0; i < count; i++)
{
var element = rigHierarchy[i];
element.depth = rigDepths[i];
rigHierarchy[i] = element;
}
}
public void ImportRig(KRigComponent rigComponent)
{
rigHierarchy.Clear();
rigDepths.Clear();
rigComponent.RefreshHierarchy();
var hierarchy = rigComponent.GetRigTransforms();
var depths = rigComponent.GetHierarchyDepths();
for (int i = 0; i < hierarchy.Length; i++)
{
rigHierarchy.Add(new KRigElement(i, hierarchy[i].transform.name));
rigDepths.Add(depths[i]);
}
NotifyObservers();
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssetIfDirty(this);
}
public KRigElement GetElementByName(string targetName)
{
return rigHierarchy.Find(item => item.name.Equals(targetName));
}
public void RegisterRigObserver(Object newRigObserver)
{
// Only register Rig Observers.
IRigObserver observer = (IRigObserver) newRigObserver;
if (observer == null) return;
if (_rigObservers.Contains(newRigObserver))
{
return;
}
_rigObservers.Add(newRigObserver);
EditorUtility.SetDirty(this);
}
public void UnRegisterObserver(Object rigObserver)
{
_rigObservers.Remove(rigObserver);
EditorUtility.SetDirty(this);
}
public void NotifyObservers()
{
List<Object> validObservers = new List<Object>();
foreach (var observer in _rigObservers)
{
if (observer is IRigObserver obj)
{
obj.OnRigUpdated();
validObservers.Add(observer);
}
}
_rigObservers = validObservers;
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,176 @@
// Designed by KINEMATION, 2024.
using System.Collections.Generic;
using KINEMATION.KAnimationCore.Runtime.Core;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
public class KRigComponent : MonoBehaviour
{
[SerializeField] private List<Transform> hierarchy = new List<Transform>();
private List<KVirtualElement> _virtualElements;
private Dictionary<string, int> _hierarchyMap;
private List<KTransform> _cachedHierarchyPose;
#if UNITY_EDITOR
[SerializeField] private List<int> hierarchyDepths = new List<int>();
public bool CompareRig(KRig compareTo)
{
if (compareTo == null || hierarchy == null || compareTo.rigHierarchy.Count != hierarchy.Count)
{
return false;
}
int count = hierarchy.Count;
for (int i = 0; i < count; i++)
{
if (!compareTo.rigHierarchy[i].name.Equals(hierarchy[i].name)) return false;
}
return true;
}
public int[] GetHierarchyDepths()
{
return hierarchyDepths.ToArray();
}
public void RefreshHierarchy()
{
hierarchy.Clear();
hierarchyDepths.Clear();
TraverseHierarchyByLayer(transform, 0);
}
public Transform[] GetHierarchy()
{
if (hierarchy == null)
{
return null;
}
return hierarchy.ToArray();
}
public bool Contains(string entry)
{
if (hierarchy == null) return false;
HashSet<string> set = new HashSet<string>();
foreach (var element in hierarchy)
{
set.Add(element.name);
}
return set.Contains(entry);
}
private void TraverseHierarchyByLayer(Transform currentTransform, int depth)
{
hierarchy.Add(currentTransform);
hierarchyDepths.Add(depth);
foreach (Transform child in currentTransform)
{
TraverseHierarchyByLayer(child, depth + 1);
}
}
#endif
public void Initialize()
{
// Register Virtual Elements.
_virtualElements = new List<KVirtualElement>();
KVirtualElement[] virtualElements = GetComponentsInChildren<KVirtualElement>();
foreach (var virtualElement in virtualElements)
{
_virtualElements.Add(virtualElement);
}
// Map the hierarchy indexes to the element names.
_hierarchyMap = new Dictionary<string, int>();
int count = hierarchy.Count;
for (int i = 0; i < count; i++)
{
_hierarchyMap.TryAdd(hierarchy[i].name, i);
}
_cachedHierarchyPose = new List<KTransform>();
}
public void AnimateVirtualElements()
{
foreach (var virtualElement in _virtualElements)
{
virtualElement.Animate();
}
}
public Transform[] GetRigTransforms()
{
return hierarchy.ToArray();
}
public Transform GetRigTransform(KRigElement rigElement)
{
int index = rigElement.index;
// Invalid index, try to use the element name instead.
if (index < 0 || index > hierarchy.Count - 1)
{
index = _hierarchyMap[rigElement.name];
}
// Total failure, return null.
if (index < 0 || index > hierarchy.Count - 1)
{
return null;
}
return hierarchy[index].transform;
}
public Transform GetRigTransform(string elementName)
{
if (_hierarchyMap.TryGetValue(elementName, out var element))
{
return hierarchy[element];
}
return null;
}
public Transform GetRigTransform(int elementIndex)
{
if (elementIndex < 0 || elementIndex > hierarchy.Count - 1)
{
return null;
}
return hierarchy[elementIndex].transform;
}
public void CacheHierarchyPose()
{
_cachedHierarchyPose.Clear();
foreach (var element in hierarchy) _cachedHierarchyPose.Add(new KTransform(element,
false));
}
public void ApplyHierarchyCachedPose()
{
int count = hierarchy.Count;
for (int i = 0; i < count; i++)
{
var cachedPose = _cachedHierarchyPose[i];
hierarchy[i].localPosition = cachedPose.position;
hierarchy[i].localRotation = cachedPose.rotation;
}
}
}
}

View File

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

View File

@@ -0,0 +1,22 @@
// Designed by KINEMATION, 2024.
using System;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
[Serializable]
public struct KRigElement
{
public string name;
[HideInInspector] public int index;
public int depth;
public KRigElement(int index = -1, string name = "None", int depth = -1)
{
this.index = index;
this.name = name;
this.depth = depth;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ba9819a027d34b42a29b91a6cdaa5bbf
timeCreated: 1704271222

View File

@@ -0,0 +1,123 @@
// Designed by KINEMATION, 2024.
using KINEMATION.KAnimationCore.Runtime.Core;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
[Serializable]
public class KRigElementChain
{
public int Count => elementChain.Count;
public string chainName;
[HideInInspector] public List<KRigElement> elementChain = new List<KRigElement>();
public KRigElementChain GetCopy()
{
KRigElementChain copy = new KRigElementChain();
copy.chainName = chainName;
foreach (var element in elementChain) copy.elementChain.Add(element);
return copy;
}
}
// A simplified version of the KRigElementChain, which contains transforms only.
public class KTransformChain
{
public List<Transform> transformChain = new List<Transform>();
public List<KTransform> cachedTransforms = new List<KTransform>();
public ESpaceType spaceType;
public void CacheTransforms(ESpaceType targetSpace, Transform root = null)
{
cachedTransforms.Clear();
spaceType = targetSpace;
foreach (var element in transformChain)
{
KTransform cache = new KTransform();
if (targetSpace == ESpaceType.WorldSpace)
{
cache.position = element.position;
cache.rotation = element.rotation;
}
else if (targetSpace == ESpaceType.ComponentSpace)
{
if (root == null)
{
root = element.root;
}
cache.position = root.InverseTransformPoint(element.position);
cache.rotation = Quaternion.Inverse(root.rotation) * element.rotation;
}
else
{
cache.position = element.localPosition;
cache.rotation = element.localRotation;
}
cachedTransforms.Add(cache);
}
}
public void BlendTransforms(float weight)
{
int count = transformChain.Count;
for (int i = 0; i < count; i++)
{
Transform element = transformChain[i];
KTransform cache = cachedTransforms[i];
KPose pose = new KPose()
{
modifyMode = EModifyMode.Replace,
pose = cache,
space = spaceType
};
KAnimationMath.ModifyTransform(element.root, element, pose, weight);
}
}
public float GetLength(Transform root = null)
{
float chainLength = 0f;
int count = transformChain.Count;
if (count > 0 && root == null) root = transformChain[0];
for (int i = 0; i < count; i++)
{
Transform targetBone = transformChain[i];
if (count == 1)
{
Vector3 targetMS = root.InverseTransformPoint(targetBone.position);
chainLength = targetMS.magnitude;
}
if (i > 0)
{
chainLength += (targetBone.position - transformChain[i - 1].position).magnitude;
}
}
return chainLength;
}
public bool IsValid()
{
if (transformChain == null || cachedTransforms == null) return false;
if (transformChain.Count == 0) return false;
return true;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 33155135ddde45ec8689a8c186f23deb
timeCreated: 1704271199

View File

@@ -0,0 +1,15 @@
using UnityEngine;
namespace KINEMATION.KAnimationCore.Runtime.Rig
{
public class KVirtualElement : MonoBehaviour
{
public Transform targetBone;
public void Animate()
{
transform.position = targetBone.position;
transform.rotation = targetBone.rotation;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9f3351c9a16a41d9b4e52958975b758b
timeCreated: 1707634755