导入角色动画,和增加角色控制
This commit is contained in:
3
Assets/KINEMATION/KAnimationCore/Runtime/Attributes.meta
Normal file
3
Assets/KINEMATION/KAnimationCore/Runtime/Attributes.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9315a7a54d5b407cbdeba4a62a7a6d63
|
||||
timeCreated: 1698339177
|
||||
@@ -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 { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea0d7667c78b9aa49bd1f93eff9f3757
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/KINEMATION/KAnimationCore/Runtime/Core.meta
Normal file
3
Assets/KINEMATION/KAnimationCore/Runtime/Core.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a8a77a412af4ca69c2e184eab017b7f
|
||||
timeCreated: 1698339164
|
||||
438
Assets/KINEMATION/KAnimationCore/Runtime/Core/KAnimationMath.cs
Normal file
438
Assets/KINEMATION/KAnimationCore/Runtime/Core/KAnimationMath.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4980d4c84030441c922128e9a378b5fb
|
||||
timeCreated: 1698338913
|
||||
71
Assets/KINEMATION/KAnimationCore/Runtime/Core/KChainIK.cs
Normal file
71
Assets/KINEMATION/KAnimationCore/Runtime/Core/KChainIK.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1308cae72dcf420ab9a43892668bf485
|
||||
timeCreated: 1716450134
|
||||
154
Assets/KINEMATION/KAnimationCore/Runtime/Core/KCurves.cs
Normal file
154
Assets/KINEMATION/KAnimationCore/Runtime/Core/KCurves.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4594292dc1514661a692348534d3285c
|
||||
timeCreated: 1698343937
|
||||
104
Assets/KINEMATION/KAnimationCore/Runtime/Core/KMath.cs
Normal file
104
Assets/KINEMATION/KAnimationCore/Runtime/Core/KMath.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd8cdd8e10864049b6b1119a7c3cc296
|
||||
timeCreated: 1704002312
|
||||
87
Assets/KINEMATION/KAnimationCore/Runtime/Core/KSpringMath.cs
Normal file
87
Assets/KINEMATION/KAnimationCore/Runtime/Core/KSpringMath.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b715a4ee42b646169c5ce859e6d34964
|
||||
timeCreated: 1698339846
|
||||
153
Assets/KINEMATION/KAnimationCore/Runtime/Core/KTransform.cs
Normal file
153
Assets/KINEMATION/KAnimationCore/Runtime/Core/KTransform.cs
Normal 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8ef53e57347421a8fcc4468bd018afd
|
||||
timeCreated: 1698341198
|
||||
121
Assets/KINEMATION/KAnimationCore/Runtime/Core/KTwoBoneIK.cs
Normal file
121
Assets/KINEMATION/KAnimationCore/Runtime/Core/KTwoBoneIK.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d99a3775c3e469dbe146569b35cd7a6
|
||||
timeCreated: 1698338938
|
||||
53
Assets/KINEMATION/KAnimationCore/Runtime/Core/SetFloat.cs
Normal file
53
Assets/KINEMATION/KAnimationCore/Runtime/Core/SetFloat.cs
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4437088df934c1a9375bf492c23d011
|
||||
timeCreated: 1709881756
|
||||
3
Assets/KINEMATION/KAnimationCore/Runtime/Input.meta
Normal file
3
Assets/KINEMATION/KAnimationCore/Runtime/Input.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d01e94a2fa34eba9f2ebd278987b1dc
|
||||
timeCreated: 1707394003
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca9ff9d900444c4785ba2de65c0e0263
|
||||
timeCreated: 1707563078
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37f35ec97fc94416a139ca21ae59beda
|
||||
timeCreated: 1707463958
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "KAnimationCore.Runtime"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8548a9d25a091541a1fcde53694c91a
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/KINEMATION/KAnimationCore/Runtime/Misc.meta
Normal file
3
Assets/KINEMATION/KAnimationCore/Runtime/Misc.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 373787767ed74d69a69333922d6c13a8
|
||||
timeCreated: 1744450603
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace KINEMATION.KAnimationCore.Runtime.Misc
|
||||
{
|
||||
public interface IAssetDragAndDrop
|
||||
{
|
||||
public void SetAsset(ScriptableObject asset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6651ef9c95614dd687f3bab919072187
|
||||
timeCreated: 1744450620
|
||||
3
Assets/KINEMATION/KAnimationCore/Runtime/Rig.meta
Normal file
3
Assets/KINEMATION/KAnimationCore/Runtime/Rig.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51e3869638134e4db26201aca4fc9096
|
||||
timeCreated: 1704099380
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace KINEMATION.KAnimationCore.Runtime.Rig
|
||||
{
|
||||
public interface IRigObserver
|
||||
{
|
||||
public void OnRigUpdated();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00acdb04eae148a0b993a37cb2b95007
|
||||
timeCreated: 1708431186
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace KINEMATION.KAnimationCore.Runtime.Rig
|
||||
{
|
||||
public interface IRigProvider
|
||||
{
|
||||
public KRigElement[] GetHierarchy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c27f9465994402da2a00b6e82c4a580
|
||||
timeCreated: 1744271712
|
||||
8
Assets/KINEMATION/KAnimationCore/Runtime/Rig/IRigUser.cs
Normal file
8
Assets/KINEMATION/KAnimationCore/Runtime/Rig/IRigUser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 906523edc5b146fb8837b1804a606af5
|
||||
timeCreated: 1705907873
|
||||
32
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KPose.cs
Normal file
32
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KPose.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 054feb4bdc8a463ea7b6aba306d8cd64
|
||||
timeCreated: 1704783390
|
||||
138
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRig.cs
Normal file
138
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRig.cs
Normal 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
|
||||
}
|
||||
}
|
||||
11
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRig.cs.meta
Normal file
11
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRig.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c9c04639ae548bdb0a8e927492d029d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
176
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRigComponent.cs
Normal file
176
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRigComponent.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e03e7ed42ce9470c899fc6cc550571e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRigElement.cs
Normal file
22
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRigElement.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba9819a027d34b42a29b91a6cdaa5bbf
|
||||
timeCreated: 1704271222
|
||||
123
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRigElementChain.cs
Normal file
123
Assets/KINEMATION/KAnimationCore/Runtime/Rig/KRigElementChain.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33155135ddde45ec8689a8c186f23deb
|
||||
timeCreated: 1704271199
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f3351c9a16a41d9b4e52958975b758b
|
||||
timeCreated: 1707634755
|
||||
Reference in New Issue
Block a user