Netcode Bootstrap
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AimIKAffectedBoneComponent : IBufferElementData
|
||||
{
|
||||
public Entity boneEntity;
|
||||
public float weight;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AimIKComponent: IComponentData, IEnableableComponent
|
||||
{
|
||||
public Entity target;
|
||||
public float2 angleLimits;
|
||||
public float3 forwardVector;
|
||||
public float weight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3798f7affc8ae8346a6562b94b67c8cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/AimIKComponents.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,120 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaAnimationInjectionSystemGroup))]
|
||||
public partial struct AimIKSystem: ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
partial struct AimIKJob : IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> boneEntityRefLookup;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public RuntimeAnimationData runtimeData;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(AimIKComponent aik, in AnimatorEntityRefComponent aer, in DynamicBuffer<AimIKAffectedBoneComponent> aimedBones)
|
||||
{
|
||||
if (aik.weight < math.EPSILON)
|
||||
return;
|
||||
|
||||
var rigDef = rigDefLookup[aer.animatorEntity];
|
||||
using var animStream = AnimationStream.Create(runtimeData, rigDef);
|
||||
|
||||
var targetEntityRigRelativePose = IKCommon.GetRigRelativeEntityPose
|
||||
(
|
||||
aik.target,
|
||||
aer.animatorEntity,
|
||||
animStream.GetWorldPose(0),
|
||||
runtimeData,
|
||||
localTransformLookup,
|
||||
parentLookup,
|
||||
boneEntityRefLookup,
|
||||
rigDefLookup
|
||||
);
|
||||
|
||||
for (var i = 0; i < aimedBones.Length; ++i)
|
||||
{
|
||||
var aimedBone = aimedBones[i];
|
||||
if (!boneEntityRefLookup.TryGetComponent(aimedBone.boneEntity, out var aimedBoneEntity))
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
Debug.LogWarning($"Aimed entity '{aimedBone.boneEntity}' does not have AnimatorEntityRefComponent.");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
var ikBoneWorldPose = animStream.GetWorldPose(aimedBoneEntity.boneIndexInAnimationRig);
|
||||
var toTargetDir = math.normalize(targetEntityRigRelativePose.pos - ikBoneWorldPose.pos);
|
||||
var originalForward = math.rotate(ikBoneWorldPose.rot, aik.forwardVector);
|
||||
var acos = math.dot(toTargetDir, originalForward);
|
||||
if (math.abs(acos) < 0.99999f)
|
||||
{
|
||||
var crossDir = math.normalize(math.cross(originalForward, toTargetDir));
|
||||
var angle = math.acos(acos);
|
||||
angle = math.clamp(angle, aik.angleLimits.x, aik.angleLimits.y);
|
||||
var correctedRot = quaternion.AxisAngle(crossDir, angle);
|
||||
var compositeWeight = aik.weight * aimedBone.weight;
|
||||
correctedRot = math.slerp(quaternion.identity, correctedRot, compositeWeight);
|
||||
|
||||
ikBoneWorldPose.rot = math.mul(correctedRot, ikBoneWorldPose.rot);
|
||||
animStream.SetWorldPose(aimedBoneEntity.boneIndexInAnimationRig, ikBoneWorldPose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var q = SystemAPI.QueryBuilder()
|
||||
.WithAll<AimIKComponent, AnimatorEntityRefComponent, AimIKAffectedBoneComponent>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(q);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
|
||||
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
|
||||
var aerLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var ikJob = new AimIKJob()
|
||||
{
|
||||
rigDefLookup = rigDefLookup,
|
||||
runtimeData = runtimeData,
|
||||
localTransformLookup = localTransformLookup,
|
||||
parentLookup = parentLookup,
|
||||
boneEntityRefLookup = aerLookup
|
||||
};
|
||||
|
||||
ikJob.ScheduleParallel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b586d291c3ba8540ba45bbd45901a05
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/AimIKSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,29 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct DynamicBoneChainNode: IBufferElementData
|
||||
{
|
||||
public int parentIndex;
|
||||
public float3 position;
|
||||
public float3 prevPosition;
|
||||
public BoneTransform referenceLocalPose;
|
||||
public Entity boneEntity;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct DynamicBoneChainComponent: IComponentData, IEnableableComponent
|
||||
{
|
||||
public float inertia;
|
||||
public float damping;
|
||||
public float elasticity;
|
||||
public float stiffness;
|
||||
public float timeAccumulator;
|
||||
// Previous entity position. Used to simulate inertial motion of whole chain
|
||||
public float3 prevPosition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d66897e34c41a043887768428234143
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/DynamicBoneChainComponents.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaAnimationInjectionSystemGroup), OrderFirst = true)]
|
||||
public partial struct DynamicBoneChainSystem: ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
partial struct DynamicBoneChainVerletSolverJob : IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> boneEntityRefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public RuntimeAnimationData runtimeData;
|
||||
|
||||
public float deltaTime;
|
||||
static readonly float fixedUpdateRate = 1 / 60.0f;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(Entity e, ref DynamicBoneChainComponent dbcc, in AnimatorEntityRefComponent aer, ref DynamicBuffer<DynamicBoneChainNode> dynamicChain)
|
||||
{
|
||||
var rigDef = rigDefLookup[aer.animatorEntity];
|
||||
using var animStream = AnimationStream.Create(runtimeData, rigDef);
|
||||
|
||||
Span<int> boneIndicesInRig = stackalloc int[dynamicChain.Length];
|
||||
Span<float3> initialPositions = stackalloc float3[dynamicChain.Length];
|
||||
var dca = dynamicChain.AsNativeArray();
|
||||
|
||||
InitBoneFrameData(dca, boneIndicesInRig, initialPositions, animStream);
|
||||
var inertia = ComputeEntityInertia(e, ref dbcc, runtimeData);
|
||||
|
||||
dbcc.timeAccumulator += deltaTime;
|
||||
var simulationCount = 0;
|
||||
while (dbcc.timeAccumulator >= fixedUpdateRate)
|
||||
{
|
||||
var modInertia = math.select(0, inertia * dbcc.inertia, simulationCount == 0);
|
||||
Integrate(dbcc, dca, modInertia, boneIndicesInRig, animStream);
|
||||
Elasticity(dbcc, dca, boneIndicesInRig, animStream);
|
||||
ConstrainDistance(dca, boneIndicesInRig, animStream);
|
||||
dbcc.timeAccumulator -= fixedUpdateRate;
|
||||
simulationCount++;
|
||||
}
|
||||
|
||||
if (simulationCount == 0)
|
||||
{
|
||||
ApplyMovementOffset(inertia, dca);
|
||||
Elasticity(dbcc, dca, boneIndicesInRig, animStream);
|
||||
ConstrainDistance(dca, boneIndicesInRig, animStream);
|
||||
}
|
||||
|
||||
MakeChainRotations(dca, boneIndicesInRig, animStream);
|
||||
for (var i = 0; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
animStream.SetWorldPosition(boneIndicesInRig[i], dynamicChain[i].position);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ApplyMovementOffset(float3 inertia, NativeArray<DynamicBoneChainNode> dynamicChain)
|
||||
{
|
||||
for (var i = 1; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
var dc = dynamicChain[i];
|
||||
dc.position += inertia;
|
||||
dc.prevPosition += inertia;
|
||||
dynamicChain[i] = dc;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void InitBoneFrameData(NativeArray<DynamicBoneChainNode> dynamicChain, Span<int> boneIndicesInRig, Span<float3> initialPositions, AnimationStream animStream)
|
||||
{
|
||||
for (var i = 0; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
var dbe = dynamicChain[i];
|
||||
if (!boneEntityRefLookup.TryGetComponent(dbe.boneEntity, out var boneEntityRef))
|
||||
return;
|
||||
|
||||
animStream.SetLocalPose(boneEntityRef.boneIndexInAnimationRig, dbe.referenceLocalPose);
|
||||
if (i == 0)
|
||||
{
|
||||
var rootPose = animStream.GetWorldPose(boneEntityRef.boneIndexInAnimationRig);
|
||||
dbe.position = dbe.prevPosition = rootPose.pos;
|
||||
dynamicChain[0] = dbe;
|
||||
}
|
||||
|
||||
boneIndicesInRig[i] = boneEntityRef.boneIndexInAnimationRig;
|
||||
}
|
||||
|
||||
for (var i = 0; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
initialPositions[i] = animStream.GetWorldPosition(boneIndicesInRig[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float3 ComputeEntityInertia(Entity e, ref DynamicBoneChainComponent dbcc, RuntimeAnimationData runtimeAnimationData)
|
||||
{
|
||||
BoneTransform bt = BoneTransform.Identity();
|
||||
IKCommon.GetEntityWorldTransform
|
||||
(
|
||||
e,
|
||||
ref bt,
|
||||
runtimeAnimationData,
|
||||
localTransformLookup,
|
||||
parentLookup,
|
||||
boneEntityRefLookup,
|
||||
rigDefLookup
|
||||
);
|
||||
var rv = bt.pos - dbcc.prevPosition;
|
||||
dbcc.prevPosition = bt.pos;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Integrate(DynamicBoneChainComponent dbcc, NativeArray<DynamicBoneChainNode> dynamicChain, float3 inertia, Span<int> boneIndicesInRig, AnimationStream animStream)
|
||||
{
|
||||
// Root bone is stationary
|
||||
var dc0 = dynamicChain[0];
|
||||
dc0.prevPosition = dc0.position;
|
||||
dc0.position = animStream.GetWorldPosition(boneIndicesInRig[0]);
|
||||
|
||||
for (var i = 1; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
var dc = dynamicChain[i];
|
||||
var dv = dc.position - dc.prevPosition;
|
||||
dc.prevPosition = dc.position + inertia;
|
||||
dc.position += inertia + dv * (1 - dbcc.damping);
|
||||
dynamicChain[i] = dc;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ConstrainDistance(NativeArray<DynamicBoneChainNode> dynamicChain, Span<int> boneIndicesInRig, AnimationStream animStream)
|
||||
{
|
||||
// Distance constraints
|
||||
for (var i = 1; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
var dc = dynamicChain[i];
|
||||
var bonePose = animStream.GetWorldPose(boneIndicesInRig[i]);
|
||||
var parentBonePose = animStream.GetWorldPose(boneIndicesInRig[dc.parentIndex]);
|
||||
var refDeltaPos = parentBonePose.pos - bonePose.pos;
|
||||
var refDeltaPosLen = math.length(refDeltaPos);
|
||||
|
||||
var curDeltaPos = dynamicChain[dc.parentIndex].position - dc.position;
|
||||
var curDeltaPosLenSq = math.lengthsq(curDeltaPos);
|
||||
|
||||
if (curDeltaPosLenSq > 0)
|
||||
{
|
||||
var curDeltaPosLen = math.sqrt(curDeltaPosLenSq);
|
||||
var slm = math.rcp(curDeltaPosLen);
|
||||
dc.position += curDeltaPos * slm * (curDeltaPosLen - refDeltaPosLen);
|
||||
dynamicChain[i] = dc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Elasticity(DynamicBoneChainComponent dbcc, NativeArray<DynamicBoneChainNode> dynamicChain, Span<int> boneIndicesInRig, AnimationStream animStream)
|
||||
{
|
||||
for (var i = 1; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
var dc = dynamicChain[i];
|
||||
var targetPos = ComputeRigidPos(i, dynamicChain, boneIndicesInRig, animStream);
|
||||
var delta = targetPos - dc.position;
|
||||
dc.position += delta * dbcc.elasticity;
|
||||
dc.position += Stiffness(targetPos, dc.position, dbcc.stiffness);
|
||||
dynamicChain[i] = dc;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float3 Stiffness(float3 rigidPos, float3 simPos, float stiffnessFactor)
|
||||
{
|
||||
if (stiffnessFactor <= 0)
|
||||
return float3.zero;
|
||||
|
||||
var delta = rigidPos - simPos;
|
||||
var deltaLengthSq = math.lengthsq(delta);
|
||||
var deltaLength = math.sqrt(deltaLengthSq);
|
||||
|
||||
if (deltaLengthSq < math.EPSILON)
|
||||
return float3.zero;
|
||||
|
||||
var rcpDeltaLength = math.rcp(deltaLength);
|
||||
var stiffness = (2 - 2 * stiffnessFactor) * deltaLength;
|
||||
var dl = math.max(deltaLength - stiffness, 0);
|
||||
var rv = dl * delta * rcpDeltaLength;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float3 ComputeRigidPos(int boneIndex, NativeArray<DynamicBoneChainNode> dynamicChain, Span<int> boneIndicesInRig, AnimationStream animStream)
|
||||
{
|
||||
var dc1 = dynamicChain[boneIndex];
|
||||
var dc0 = dynamicChain[dc1.parentIndex];
|
||||
var wpp0 = animStream.GetWorldPose(boneIndicesInRig[dc1.parentIndex]);
|
||||
var lpp1 = animStream.GetLocalPosition(boneIndicesInRig[boneIndex]);
|
||||
wpp0.pos = dc0.position;
|
||||
|
||||
var rv = BoneTransform.TransformPoint(wpp0, lpp1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MakeChainRotations(NativeArray<DynamicBoneChainNode> dynamicChain, Span<int> boneIndicesInRig, AnimationStream animStream)
|
||||
{
|
||||
for (var i = 2; i < dynamicChain.Length; ++i)
|
||||
{
|
||||
var dc1 = dynamicChain[i];
|
||||
var dc0 = dynamicChain[dc1.parentIndex];
|
||||
|
||||
var dv = dc1.position - dc0.position;
|
||||
var ndv = math.normalizesafe(dv);
|
||||
var wp0 = animStream.GetWorldPose(boneIndicesInRig[dc1.parentIndex]);
|
||||
var lp0 = animStream.GetLocalPose(boneIndicesInRig[dc1.parentIndex]);
|
||||
var wf0 = math.rotate(wp0.rot, lp0.pos);
|
||||
var nwf0 = math.normalizesafe(wf0);
|
||||
var q = MathUtils.FromToRotationForNormalizedVectors(nwf0, ndv);
|
||||
var newRot = math.mul(q, wp0.rot);
|
||||
animStream.SetWorldRotation(boneIndicesInRig[dc1.parentIndex], newRot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var q = SystemAPI.QueryBuilder()
|
||||
.WithAll<DynamicBoneChainComponent, AnimatorEntityRefComponent, DynamicBoneChainNode>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(q);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var aerLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
|
||||
var ltLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
|
||||
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var ikJob = new DynamicBoneChainVerletSolverJob()
|
||||
{
|
||||
runtimeData = runtimeData,
|
||||
rigDefLookup = rigDefLookup,
|
||||
boneEntityRefLookup = aerLookup,
|
||||
parentLookup = parentLookup,
|
||||
localTransformLookup = ltLookup,
|
||||
deltaTime = SystemAPI.Time.DeltaTime
|
||||
};
|
||||
|
||||
ikJob.ScheduleParallel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f1e853ee9ad07e4fa1e6e32a4b717c4
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/DynamicBoneChainSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,13 @@
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct FABRIKComponent: IComponentData, IEnableableComponent
|
||||
{
|
||||
public Entity tip, target;
|
||||
public int numIterations;
|
||||
public float weight, threshold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c091fd7a225a7d43bde886f3ef8cc87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/FABRIKComponents.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,59 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaAnimationInjectionSystemGroup))]
|
||||
public partial struct FABRIKSystem: ISystem
|
||||
{
|
||||
struct FABRIKData
|
||||
{
|
||||
public NativeArray<float3> chainWorldPositions;
|
||||
public NativeArray<float3> chainInitialDirections;
|
||||
public NativeArray<float> chainLengths;
|
||||
public NativeArray<int> chainBoneIndices;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var q = SystemAPI.QueryBuilder()
|
||||
.WithAll<FABRIKComponent, AnimatorEntityRefComponent>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(q);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
|
||||
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
|
||||
var animatorEntityRefLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
|
||||
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var ikJob = new FABRIKJob()
|
||||
{
|
||||
parentLookup = parentLookup,
|
||||
localTransformLookup = localTransformLookup,
|
||||
rigDefLookup = rigDefLookup,
|
||||
animatorEntityRefLookup = animatorEntityRefLookup,
|
||||
runtimeData = runtimeData
|
||||
};
|
||||
|
||||
ikJob.ScheduleParallel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b79c61d80e67ed479e8273ed043b123
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/FABRIKSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,208 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public partial struct FABRIKSystem
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
partial struct FABRIKJob : IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> animatorEntityRefLookup;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public RuntimeAnimationData runtimeData;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(FABRIKComponent ikc, in AnimatorEntityRefComponent aer)
|
||||
{
|
||||
if (ikc.weight <= math.EPSILON)
|
||||
return;
|
||||
|
||||
var rigDef = rigDefLookup[aer.animatorEntity];
|
||||
using var animStream = AnimationStream.Create(runtimeData, rigDef);
|
||||
|
||||
var targetEntityRigRootRelativePose = IKCommon.GetRigRelativeEntityPose
|
||||
(
|
||||
ikc.target,
|
||||
aer.animatorEntity,
|
||||
animStream.GetWorldPose(0),
|
||||
runtimeData,
|
||||
localTransformLookup,
|
||||
parentLookup,
|
||||
animatorEntityRefLookup,
|
||||
rigDefLookup
|
||||
);
|
||||
var targetEntityRigRelativePosition = targetEntityRigRootRelativePose.pos;
|
||||
|
||||
var ikData = PrepareIKData(ikc, rigDef, aer);
|
||||
|
||||
IKChainToWorld(ikData, animStream);
|
||||
if (Solve(ikData, ikc.numIterations, ikc.threshold, targetEntityRigRelativePosition))
|
||||
IKDataToBonePoses(ikData, animStream, ikc.weight);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FABRIKData PrepareIKData(FABRIKComponent ikComponent, RigDefinitionComponent rb, AnimatorEntityRefComponent rootBoneAER)
|
||||
{
|
||||
var rv = new FABRIKData();
|
||||
|
||||
var chainEndBoneIndex = animatorEntityRefLookup[ikComponent.tip].boneIndexInAnimationRig;
|
||||
var chainRootBoneIndex = rootBoneAER.boneIndexInAnimationRig;
|
||||
var curBoneIndex = chainEndBoneIndex;
|
||||
|
||||
var chainBoneIndices = new NativeList<int>(Allocator.Temp);
|
||||
while (chainRootBoneIndex != curBoneIndex && curBoneIndex >= 0)
|
||||
{
|
||||
chainBoneIndices.Add(curBoneIndex);
|
||||
curBoneIndex = rb.rigBlob.Value.bones[curBoneIndex].parentBoneIndex;
|
||||
}
|
||||
chainBoneIndices.Add(chainRootBoneIndex);
|
||||
var numBonesInChain = chainBoneIndices.Length;
|
||||
|
||||
rv.chainLengths = new NativeArray<float>(numBonesInChain, Allocator.Temp);
|
||||
rv.chainBoneIndices = chainBoneIndices.AsArray();
|
||||
rv.chainWorldPositions = new NativeArray<float3>(numBonesInChain, Allocator.Temp);
|
||||
rv.chainInitialDirections = new NativeArray<float3>(numBonesInChain, Allocator.Temp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IKChainToWorld(FABRIKData ikData, AnimationStream animStream)
|
||||
{
|
||||
ikData.chainLengths[^1] = 0;
|
||||
|
||||
// Fill world positions
|
||||
for (var i = 0; i < ikData.chainBoneIndices.Length; ++i)
|
||||
{
|
||||
var boneIndex = ikData.chainBoneIndices[i];
|
||||
var boneWorldPos = animStream.GetWorldPosition(boneIndex);
|
||||
ikData.chainWorldPositions[i] = boneWorldPos;
|
||||
}
|
||||
|
||||
// Fill chain lengths and directions
|
||||
for (var i = 0; i < ikData.chainBoneIndices.Length - 1; ++i)
|
||||
{
|
||||
var curBonePos = ikData.chainWorldPositions[i];
|
||||
var nextBonePos = ikData.chainWorldPositions[i + 1];
|
||||
var lv = nextBonePos - curBonePos;
|
||||
var l = math.length(lv);
|
||||
ikData.chainInitialDirections[i] = lv / l;
|
||||
ikData.chainLengths[i] = l;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IKDataToBonePoses(FABRIKData ikData, AnimationStream animationStream, float weight)
|
||||
{
|
||||
var initialIndex = ikData.chainWorldPositions.Length - 1;
|
||||
//var initialIndex = 0;
|
||||
var prevBoneRot = quaternion.identity;
|
||||
for (var i = initialIndex; i > 0; --i)
|
||||
{
|
||||
var pos = ikData.chainWorldPositions[i];
|
||||
var boneIndex = ikData.chainBoneIndices[i];
|
||||
|
||||
var bonePoseOrig = animationStream.GetWorldPose(boneIndex);
|
||||
var bonePose = bonePoseOrig;
|
||||
var boneDir = ikData.chainInitialDirections[i - 1];
|
||||
var nextBonePos = ikData.chainWorldPositions[i - 1];
|
||||
var boneVec = math.normalize(pos - nextBonePos);
|
||||
var rot = MathUtils.FromToRotationForNormalizedVectors(boneDir, boneVec);
|
||||
bonePose.rot = math.mul(math.inverse(prevBoneRot), bonePose.rot);
|
||||
bonePose.rot = math.mul(rot, bonePose.rot);
|
||||
prevBoneRot = rot;
|
||||
bonePose.rot = math.slerp(bonePoseOrig.rot, bonePose.rot, weight);
|
||||
var resultTransform = BoneTransform.Lerp(bonePoseOrig, bonePose, weight);
|
||||
animationStream.SetWorldPose(boneIndex, resultTransform);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ForwardPass(FABRIKData ikData, float3 origin)
|
||||
{
|
||||
ikData.chainWorldPositions[^1] = origin;
|
||||
|
||||
for (var i = ikData.chainWorldPositions.Length - 2; i >= 0; --i)
|
||||
{
|
||||
var p0 = ikData.chainWorldPositions[i];
|
||||
var p1 = ikData.chainWorldPositions[i + 1];
|
||||
var dir = math.normalize(p0 - p1);
|
||||
var offset = dir * ikData.chainLengths[i];
|
||||
ikData.chainWorldPositions[i] = ikData.chainWorldPositions[i + 1] + offset;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BackwardPass(FABRIKData ikData, float3 goalInWorldSpace)
|
||||
{
|
||||
ikData.chainWorldPositions[0] = goalInWorldSpace;
|
||||
|
||||
for (var i = 1; i < ikData.chainWorldPositions.Length; ++i)
|
||||
{
|
||||
var p0 = ikData.chainWorldPositions[i - 1];
|
||||
var p1 = ikData.chainWorldPositions[i];
|
||||
var dir = math.normalize(p1 - p0);
|
||||
var offset = dir * ikData.chainLengths[i - 1];
|
||||
ikData.chainWorldPositions[i] = ikData.chainWorldPositions[i - 1] + offset;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ValidateIKData(FABRIKData ikData)
|
||||
{
|
||||
for (var i = 0; i < ikData.chainWorldPositions.Length; ++i)
|
||||
{
|
||||
if (math.any(math.isnan(ikData.chainWorldPositions[i])))
|
||||
Debug.Log("NAN!");
|
||||
|
||||
if (math.any(math.isnan(ikData.chainInitialDirections[i])))
|
||||
Debug.Log("NAN!");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Solve(FABRIKData ikData, int numIterations, float threshold, float3 goalPos)
|
||||
{
|
||||
var chainOrigin = ikData.chainWorldPositions[^1];
|
||||
for (var i = 0; i < numIterations; ++i)
|
||||
{
|
||||
var tipPos = ikData.chainWorldPositions[0];
|
||||
if (math.lengthsq(goalPos - tipPos) <= threshold)
|
||||
return i > math.EPSILON;
|
||||
|
||||
BackwardPass(ikData, goalPos);
|
||||
ForwardPass(ikData, chainOrigin);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 103c9a5e7f8477145a93dec6fd184175
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/FABRIKSystemJobs.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,68 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Transforms;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public static class IKCommon
|
||||
{
|
||||
public static void GetEntityWorldTransform
|
||||
(
|
||||
Entity e,
|
||||
ref BoneTransform t,
|
||||
in RuntimeAnimationData runtimeAnimationData,
|
||||
ComponentLookup<LocalTransform> ltl,
|
||||
ComponentLookup<Parent> pl,
|
||||
ComponentLookup<AnimatorEntityRefComponent> aerc,
|
||||
ComponentLookup<RigDefinitionComponent> rd
|
||||
)
|
||||
{
|
||||
if (!ltl.TryGetComponent(e, out var lt)) return;
|
||||
|
||||
// If current entity is a part of the rig (bone entity) use its animated pose
|
||||
if (aerc.TryGetComponent(e, out var aer))
|
||||
{
|
||||
var boneWorldPoses = RuntimeAnimationData.GetAnimationDataForRigRO(runtimeAnimationData.worldSpaceBonesBuffer, rd[aer.animatorEntity]);
|
||||
if (boneWorldPoses.Length > aer.boneIndexInAnimationRig)
|
||||
{
|
||||
var bt = boneWorldPoses[aer.boneIndexInAnimationRig];
|
||||
t = BoneTransform.Multiply(bt, t);
|
||||
}
|
||||
// We have got rig relative position so must continue with parent entities of animated rig
|
||||
e = aer.animatorEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = BoneTransform.Multiply(new BoneTransform(lt), t);
|
||||
}
|
||||
|
||||
if (pl.TryGetComponent(e, out var p))
|
||||
{
|
||||
GetEntityWorldTransform(p.Value, ref t, runtimeAnimationData, ltl, pl, aerc, rd);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BoneTransform GetRigRelativeEntityPose
|
||||
(
|
||||
Entity target,
|
||||
Entity animatorEntity,
|
||||
BoneTransform rigRootWorldPose,
|
||||
in RuntimeAnimationData runtimeAnimationData,
|
||||
ComponentLookup<LocalTransform> ltl,
|
||||
ComponentLookup<Parent> pl,
|
||||
ComponentLookup<AnimatorEntityRefComponent> aerc,
|
||||
ComponentLookup<RigDefinitionComponent> rd
|
||||
)
|
||||
{
|
||||
var targetEntityWorldPose = BoneTransform.Identity();
|
||||
GetEntityWorldTransform(target, ref targetEntityWorldPose, runtimeAnimationData, ltl, pl, aerc, rd);
|
||||
var animatedEntityWorldPose = BoneTransform.Inverse(rigRootWorldPose);
|
||||
GetEntityWorldTransform(animatorEntity, ref animatedEntityWorldPose, runtimeAnimationData, ltl, pl, aerc, rd);
|
||||
var rv = BoneTransform.Multiply(BoneTransform.Inverse(animatedEntityWorldPose), targetEntityWorldPose);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8fef388c445806448874a5058408c748
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/IKCommon.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,11 @@
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
[DisableAutoCreation]
|
||||
[UpdateAfter(typeof(AnimationProcessSystem))]
|
||||
public partial class RukhankaAnimationInjectionSystemGroup: ComponentSystemGroup { }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ecb24b3c3252594593580b8628863b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/IKSystemGroup.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,13 @@
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct OverrideTransformIKComponent: IComponentData, IEnableableComponent
|
||||
{
|
||||
public Entity target;
|
||||
public float positionWeight;
|
||||
public float rotationWeight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22e9a073053bf24408ebc5456c6efd7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/OverrideTransformIK.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,96 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaAnimationInjectionSystemGroup))]
|
||||
[UpdateAfter(typeof(FABRIKSystem))]
|
||||
public partial struct OverrideTransformIKSystem: ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
partial struct OverrideTransformIKJob : IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> animatorEntityRefLookup;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public RuntimeAnimationData runtimeData;
|
||||
|
||||
void Execute(OverrideTransformIKComponent ikc, in AnimatorEntityRefComponent aer)
|
||||
{
|
||||
var rigDef = rigDefLookup[aer.animatorEntity];
|
||||
using var animStream = AnimationStream.Create(runtimeData, rigDef);
|
||||
|
||||
var targetEntityRigRootRelativePose = IKCommon.GetRigRelativeEntityPose
|
||||
(
|
||||
ikc.target,
|
||||
aer.animatorEntity,
|
||||
animStream.GetWorldPose(0),
|
||||
runtimeData,
|
||||
localTransformLookup,
|
||||
parentLookup,
|
||||
animatorEntityRefLookup,
|
||||
rigDefLookup
|
||||
);
|
||||
|
||||
var bonePose = animStream.GetWorldPose(aer.boneIndexInAnimationRig);
|
||||
|
||||
targetEntityRigRootRelativePose.pos = math.lerp(bonePose.pos, targetEntityRigRootRelativePose.pos, ikc.positionWeight);
|
||||
targetEntityRigRootRelativePose.rot = math.slerp(bonePose.rot, targetEntityRigRootRelativePose.rot, ikc.rotationWeight);
|
||||
targetEntityRigRootRelativePose.scale = 1;
|
||||
|
||||
animStream.SetWorldPose(aer.boneIndexInAnimationRig, targetEntityRigRootRelativePose);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var q = SystemAPI.QueryBuilder()
|
||||
.WithAll<OverrideTransformIKComponent, AnimatorEntityRefComponent>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(q);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
|
||||
var animatorEntityRefLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
|
||||
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var ikJob = new OverrideTransformIKJob()
|
||||
{
|
||||
rigDefLookup = rigDefLookup,
|
||||
runtimeData = runtimeData,
|
||||
localTransformLookup = localTransformLookup,
|
||||
parentLookup = parentLookup,
|
||||
animatorEntityRefLookup = animatorEntityRefLookup
|
||||
};
|
||||
|
||||
ikJob.ScheduleParallel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bf390f7f6397b04b97ae25ec7191690
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/OverrideTransformIKSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,12 @@
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct TwoBoneIKComponent: IComponentData, IEnableableComponent
|
||||
{
|
||||
public Entity mid, tip, target, midBentHint;
|
||||
public float weight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b57af7854a95c2b448d661235574e355
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/TwoBoneIKComponents.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,188 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaAnimationInjectionSystemGroup))]
|
||||
[UpdateAfter(typeof(AimIKSystem))]
|
||||
[UpdateAfter(typeof(FABRIKSystem))]
|
||||
public partial struct TwoBoneIKSystem: ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
partial struct TwoBoneIKJob : IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> animatorEntityRefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public RuntimeAnimationData runtimeData;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(TwoBoneIKComponent ikc, in AnimatorEntityRefComponent aer)
|
||||
{
|
||||
if (ikc.weight == 0)
|
||||
return;
|
||||
|
||||
var rigDef = rigDefLookup[aer.animatorEntity];
|
||||
using var animStream = AnimationStream.Create(runtimeData, rigDef);
|
||||
|
||||
var targetEntityRigRootRelativePose = IKCommon.GetRigRelativeEntityPose
|
||||
(
|
||||
ikc.target,
|
||||
aer.animatorEntity,
|
||||
animStream.GetWorldPose(0),
|
||||
runtimeData,
|
||||
localTransformLookup,
|
||||
parentLookup,
|
||||
animatorEntityRefLookup,
|
||||
rigDefLookup
|
||||
);
|
||||
|
||||
var midEntityRef = animatorEntityRefLookup[ikc.mid];
|
||||
var tipEntityRef = animatorEntityRefLookup[ikc.tip];
|
||||
|
||||
var rootWorldPose = animStream.GetWorldPose(aer.boneIndexInAnimationRig);
|
||||
var midWorldPose = animStream.GetWorldPose(midEntityRef.boneIndexInAnimationRig);
|
||||
var tipWorldPose = animStream.GetWorldPose(tipEntityRef.boneIndexInAnimationRig);
|
||||
var targetWorldPos = math.lerp(tipWorldPose.pos, targetEntityRigRootRelativePose.pos, ikc.weight);
|
||||
var initialTipRotation = tipWorldPose.rot;
|
||||
|
||||
var rootToMidVec = midWorldPose.pos - rootWorldPose.pos;
|
||||
var rootToMidVecLen = math.length(rootToMidVec);
|
||||
var midToTipVec = tipWorldPose.pos - midWorldPose.pos;
|
||||
var midToTipVecLen = math.length(midToTipVec);
|
||||
var rootToTipVec = tipWorldPose.pos - rootWorldPose.pos;
|
||||
var rootToTipVecLen = math.length(rootToTipVec);
|
||||
var rootToTargetVec = targetWorldPos - rootWorldPose.pos;
|
||||
var rootToTargetVecLen = math.length(rootToTargetVec);
|
||||
|
||||
var curBendAngle = GetAngleFromCosineLaw(rootToMidVecLen, midToTipVecLen, rootToTipVecLen);
|
||||
var targetBendAngle = GetAngleFromCosineLaw(rootToMidVecLen, midToTipVecLen, rootToTargetVecLen);
|
||||
|
||||
var bendAxis = math.cross(rootToMidVec, midToTipVec);
|
||||
|
||||
var bendAxisLenSqr = math.lengthsq(bendAxis);
|
||||
if (bendAxisLenSqr < math.EPSILON)
|
||||
{
|
||||
bendAxis = math.cross(rootToTargetVec, midToTipVec);
|
||||
bendAxisLenSqr = math.lengthsq(bendAxis);
|
||||
if (bendAxisLenSqr <= math.EPSILON)
|
||||
bendAxis = math.up();
|
||||
}
|
||||
|
||||
bendAxis = math.normalize(bendAxis);
|
||||
var deltaAngle = curBendAngle - targetBendAngle;
|
||||
var midRotDelta = quaternion.AxisAngle(bendAxis, deltaAngle);
|
||||
var midRot = math.mul(midRotDelta, midWorldPose.rot);
|
||||
midRot = math.normalize(midRot);
|
||||
animStream.SetWorldRotation(midEntityRef.boneIndexInAnimationRig, midRot);
|
||||
|
||||
tipWorldPose = animStream.GetWorldPose(tipEntityRef.boneIndexInAnimationRig);
|
||||
var updatedRootToTipVec = tipWorldPose.pos - rootWorldPose.pos;
|
||||
var rootRotDelta = MathUtils.FromToRotation(updatedRootToTipVec, rootToTargetVec);
|
||||
var rootRot = math.mul(rootRotDelta, rootWorldPose.rot);
|
||||
animStream.SetWorldRotation(aer.boneIndexInAnimationRig, rootRot);
|
||||
|
||||
float rootToTipLenSqr = math.lengthsq(rootToTipVec);
|
||||
if (ikc.midBentHint != Entity.Null && rootToTipLenSqr > 0)
|
||||
{
|
||||
var hintRigRelativePose = IKCommon.GetRigRelativeEntityPose
|
||||
(
|
||||
ikc.midBentHint,
|
||||
aer.animatorEntity,
|
||||
animStream.GetWorldPose(0),
|
||||
runtimeData,
|
||||
localTransformLookup,
|
||||
parentLookup,
|
||||
animatorEntityRefLookup,
|
||||
rigDefLookup
|
||||
);
|
||||
|
||||
var tipPose = animStream.GetWorldPose(tipEntityRef.boneIndexInAnimationRig);
|
||||
var midPose = animStream.GetWorldPose(midEntityRef.boneIndexInAnimationRig);
|
||||
rootToMidVec = midPose.pos - rootWorldPose.pos;
|
||||
rootToTipVec = tipPose.pos - rootWorldPose.pos;
|
||||
|
||||
var rootToTipVecNormalized = math.normalize(rootToTipVec);
|
||||
var rootToHintVec = hintRigRelativePose.pos - rootWorldPose.pos;
|
||||
var p0 = rootToMidVec - rootToTipVecNormalized * math.dot(rootToMidVec, rootToTipVecNormalized);
|
||||
var p1 = rootToHintVec - rootToTipVecNormalized * math.dot(rootToHintVec, rootToTipVecNormalized);
|
||||
|
||||
float jointMaxLen = rootToMidVecLen + midToTipVecLen;
|
||||
var p0LenSqr = math.lengthsq(p0);
|
||||
var p1LenSqr = math.lengthsq(p1);
|
||||
var maxProjLen = jointMaxLen * jointMaxLen;
|
||||
if (p0LenSqr > maxProjLen * 0.001f && p1LenSqr > 0)
|
||||
{
|
||||
var hintRotation = MathUtils.FromToRotation(p0, p1);
|
||||
rootWorldPose = animStream.GetWorldPose(aer.boneIndexInAnimationRig);
|
||||
animStream.SetWorldRotation(aer.boneIndexInAnimationRig, math.mul(hintRotation, rootWorldPose.rot));
|
||||
}
|
||||
}
|
||||
|
||||
var finalTipRot = math.slerp(initialTipRotation, targetEntityRigRootRelativePose.rot, ikc.weight);
|
||||
animStream.SetWorldRotation(tipEntityRef.boneIndexInAnimationRig, finalTipRot);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float GetAngleFromCosineLaw(float aLen, float bLen, float cLen)
|
||||
{
|
||||
var cosC = (aLen * aLen + bLen * bLen - cLen * cLen) / (aLen * bLen) * 0.5f;
|
||||
cosC = math.clamp(cosC, -1.0f, 1.0f);
|
||||
var rv = math.acos(cosC);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var q = SystemAPI.QueryBuilder()
|
||||
.WithAll<TwoBoneIKComponent, AnimatorEntityRefComponent>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(q);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
|
||||
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
|
||||
var animatorEntityRefLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var ikJob = new TwoBoneIKJob()
|
||||
{
|
||||
rigDefLookup = rigDefLookup,
|
||||
runtimeData = runtimeData,
|
||||
localTransformLookup = localTransformLookup,
|
||||
animatorEntityRefLookup = animatorEntityRefLookup,
|
||||
parentLookup = parentLookup
|
||||
};
|
||||
|
||||
ikJob.ScheduleParallel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53563f8e07d29ff4394b6e031fcd443b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/IK/TwoBoneIKSystem.cs
|
||||
uploadId: 897522
|
||||
Reference in New Issue
Block a user