Netcode Bootstrap
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct ControllerBlob: GenericAssetBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public string Name() => name.ToString();
|
||||
public float bakingTime;
|
||||
public float BakingTime() => bakingTime;
|
||||
#endif
|
||||
public Hash128 hash;
|
||||
public Hash128 Hash() => hash;
|
||||
|
||||
public BlobArray<LayerBlob> layers;
|
||||
public BlobArray<ParameterBlob> parameters;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum ControllerParameterType
|
||||
{
|
||||
Int,
|
||||
Float,
|
||||
Bool,
|
||||
Trigger
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ParameterValue
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public float floatValue;
|
||||
[FieldOffset(0)]
|
||||
public int intValue;
|
||||
[FieldOffset(0)]
|
||||
public bool boolValue;
|
||||
|
||||
public static implicit operator ParameterValue(float f) => new ParameterValue() { floatValue = f };
|
||||
public static implicit operator ParameterValue(int i) => new ParameterValue() { intValue = i };
|
||||
public static implicit operator ParameterValue(bool b) => new ParameterValue() { boolValue = b };
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ParameterBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public ParameterValue defaultValue;
|
||||
public ControllerParameterType type;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum AnimationBlendingMode
|
||||
{
|
||||
Override = 0,
|
||||
Additive = 1
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum AnimatorConditionMode
|
||||
{
|
||||
If = 1,
|
||||
IfNot = 2,
|
||||
Greater = 3,
|
||||
Less = 4,
|
||||
Equals = 6,
|
||||
NotEqual = 7
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct LayerBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public int defaultStateIndex;
|
||||
public float initialWeight;
|
||||
public int syncedLayerIndex;
|
||||
// syncedTiming acts as a bool (v != 0) for layers that have syncedLayerIndex >= 0. Base layer state speed also
|
||||
// need to be adjusted, so syncedTiming acts as index to synced layer for weight lerp computation
|
||||
public int syncedTiming;
|
||||
public AnimationBlendingMode blendingMode;
|
||||
public BlobArray<StateBlob> states;
|
||||
public Hash128 avatarMaskBlobHash;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct TransitionBlob
|
||||
{
|
||||
// Just a copy of UnityEditor.Animations.TransitionInterruptionSource
|
||||
// I cannot use original enum because it is in editor assembly
|
||||
public enum InterruptionSource
|
||||
{
|
||||
None,
|
||||
Source,
|
||||
Destination,
|
||||
SourceThenDestination,
|
||||
DestinationThenSource,
|
||||
}
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public BlobArray<ConditionBlob> conditions;
|
||||
public int targetStateId;
|
||||
public float duration;
|
||||
public float exitTime;
|
||||
public float offset;
|
||||
public bool hasExitTime;
|
||||
public bool hasFixedDuration;
|
||||
public InterruptionSource interruptionSource;
|
||||
public bool orderedInterruption;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ConditionBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public int paramIdx;
|
||||
public ParameterValue threshold;
|
||||
public AnimatorConditionMode conditionMode;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct MotionBlob
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
None,
|
||||
AnimationClip,
|
||||
BlendTree1D,
|
||||
BlendTree2DSimpleDirectional,
|
||||
BlendTree2DFreeformDirectional,
|
||||
BlendTree2DFreeformCartesian,
|
||||
BlendTreeDirect
|
||||
}
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public Type type;
|
||||
public int animationIndex;
|
||||
public BlendTreeBlob blendTree;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ChildMotionBlob
|
||||
{
|
||||
public MotionBlob motion;
|
||||
public float threshold;
|
||||
public float timeScale;
|
||||
public bool mirror;
|
||||
public float2 position2D;
|
||||
public int directBlendParameterIndex;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct BlendTreeBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public int blendParameterIndex;
|
||||
public int blendParameterYIndex;
|
||||
public bool normalizeBlendValues;
|
||||
public BlobArray<ChildMotionBlob> motions;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct StateBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public BlobString tag;
|
||||
#endif
|
||||
public uint hash;
|
||||
public uint tagHash;
|
||||
public float speed;
|
||||
public int speedMultiplierParameterIndex;
|
||||
public int timeParameterIndex;
|
||||
public float cycleOffset;
|
||||
public int cycleOffsetParameterIndex;
|
||||
public BlobArray<TransitionBlob> transitions;
|
||||
public MotionBlob motion;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ControllerAnimationsBlob
|
||||
{
|
||||
public BlobArray<Hash128> animations;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb7e72e901a18aa4ea9c4806acac4167
|
||||
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/AnimatorController/AnimatorControllerBlob.cs
|
||||
uploadId: 897522
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
using Rukhanka;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
using Unity.NetCode;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using static Rukhanka.AnimatorControllerSystemJobs;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: RegisterGenericSystemType(typeof(AnimatorControllerSystem<PredictedAnimatorControllerQuery>))]
|
||||
[assembly: RegisterGenericSystemType(typeof(AnimatorControllerSystem<AnimatorControllerQuery>))]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
public partial struct AnimatorControllerSystem<T>: ISystem where T: AnimatorControllerQueryCreator, new()
|
||||
{
|
||||
EntityQuery animatorControllerQuery;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var queryCreator = new T();
|
||||
animatorControllerQuery = queryCreator.CreateQuery(ref ss);
|
||||
|
||||
ss.RequireForUpdate(animatorControllerQuery);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var dt = SystemAPI.Time.DeltaTime;
|
||||
|
||||
var controllerLayersBufferHandle = SystemAPI.GetBufferTypeHandle<AnimatorControllerLayerComponent>();
|
||||
var controllerParametersBufferHandle = SystemAPI.GetBufferTypeHandle<AnimatorControllerParameterComponent>();
|
||||
var animatorOverrideAnimationsLookup = SystemAPI.GetComponentLookup<AnimatorOverrideAnimations>(true);
|
||||
var entityTypeHandle = SystemAPI.GetEntityTypeHandle();
|
||||
var controllerEventsBufferLookup = SystemAPI.GetBufferLookup<AnimatorControllerEventComponent>();
|
||||
var animDBSingleton = SystemAPI.GetSingleton<BlobDatabaseSingleton>();
|
||||
|
||||
var internalDataSingletonQuery = SystemAPI.QueryBuilder().WithAllRW<InternalAnimatorDataSingleton>().Build();
|
||||
var internalAnimatorData = FillAnimationsFromControllerSystem.GetInternalDataSingleton(internalDataSingletonQuery, ref ss);
|
||||
|
||||
var stateMachineProcessJob = new StateMachineProcessJob()
|
||||
{
|
||||
controllerLayersBufferHandle = controllerLayersBufferHandle,
|
||||
controllerParametersBufferHandle = controllerParametersBufferHandle,
|
||||
dt = dt,
|
||||
entityTypeHandle = entityTypeHandle,
|
||||
controllerEventsBufferLookup = controllerEventsBufferLookup,
|
||||
animationDatabase = animDBSingleton.animations,
|
||||
animatorOverrideAnimationLookup = animatorOverrideAnimationsLookup,
|
||||
animatorOverrideAnimationsMap = internalAnimatorData.animatorOverrideAnimationsMap.AsParallelWriter()
|
||||
|
||||
};
|
||||
|
||||
ss.Dependency = stateMachineProcessJob.ScheduleParallel(animatorControllerQuery, ss.Dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public interface AnimatorControllerQueryCreator
|
||||
{
|
||||
EntityQuery CreateQuery(ref SystemState ss);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct PredictedAnimatorControllerQuery: AnimatorControllerQueryCreator
|
||||
{
|
||||
public EntityQuery CreateQuery(ref SystemState ss)
|
||||
{
|
||||
var eqBuilder0 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAllRW<AnimatorControllerLayerComponent>()
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
.WithAll<Simulate, PredictedGhost>()
|
||||
#endif
|
||||
;
|
||||
var animatorControllerQuery = ss.GetEntityQuery(eqBuilder0);
|
||||
return animatorControllerQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerQuery: AnimatorControllerQueryCreator
|
||||
{
|
||||
public EntityQuery CreateQuery(ref SystemState ss)
|
||||
{
|
||||
var eqBuilder0 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAllRW<AnimatorControllerLayerComponent>()
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
.WithNone<GhostInstance>()
|
||||
#endif
|
||||
;
|
||||
var animatorControllerQuery = ss.GetEntityQuery(eqBuilder0);
|
||||
return animatorControllerQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecdf4d0de0c58ed4489dca4b8c9ab490
|
||||
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/AnimatorController/AnimatorControllerSystem.cs
|
||||
uploadId: 897522
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct AnimatorControllerLayerComponent: IBufferElementData, IEnableableComponent
|
||||
{
|
||||
public BlobAssetReference<ControllerBlob> controller;
|
||||
public BlobAssetReference<ControllerAnimationsBlob> animations;
|
||||
public int layerIndex;
|
||||
public float weight;
|
||||
public float speed;
|
||||
public RuntimeAnimatorData rtd;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerParameterComponent: IBufferElementData
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedString64Bytes name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public ControllerParameterType type;
|
||||
public ParameterValue value;
|
||||
|
||||
public float FloatValue
|
||||
{
|
||||
get => value.floatValue;
|
||||
set => this.value.floatValue = value;
|
||||
}
|
||||
|
||||
public int IntValue
|
||||
{
|
||||
get => value.intValue;
|
||||
set => this.value.intValue= value;
|
||||
}
|
||||
|
||||
public bool BoolValue
|
||||
{
|
||||
get => value.boolValue;
|
||||
set => this.value.boolValue = value;
|
||||
}
|
||||
|
||||
public void SetTrigger()
|
||||
{
|
||||
value.boolValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerParameterIndexTableComponent: IComponentData
|
||||
{
|
||||
public BlobAssetReference<PerfectHashTableBlob> value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerEventComponent : IBufferElementData, IEnableableComponent
|
||||
{
|
||||
public enum EventType
|
||||
{
|
||||
StateEnter,
|
||||
StateExit,
|
||||
StateUpdate
|
||||
}
|
||||
|
||||
public EventType eventType;
|
||||
public int layerId;
|
||||
public int stateId;
|
||||
public float timeInState;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedString32Bytes stateName;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorOverrideAnimations: IComponentData, IEnableableComponent
|
||||
{
|
||||
public BlobAssetReference<ControllerAnimationsBlob> value;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1026c97737e562f4c816544af6ac420f
|
||||
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/AnimatorController/AnimatorControllerSystemComponents.cs
|
||||
uploadId: 897522
|
||||
+740
@@ -0,0 +1,740 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Tests")]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AnimatorControllerSystemJobs
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
public struct StateMachineProcessJob: IJobChunk
|
||||
{
|
||||
public float dt;
|
||||
public BufferTypeHandle<AnimatorControllerLayerComponent> controllerLayersBufferHandle;
|
||||
public BufferTypeHandle<AnimatorControllerParameterComponent> controllerParametersBufferHandle;
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
[NativeDisableParallelForRestriction]
|
||||
public BufferLookup<AnimatorControllerEventComponent> controllerEventsBufferLookup;
|
||||
[ReadOnly]
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> animationDatabase;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorOverrideAnimations> animatorOverrideAnimationLookup;
|
||||
|
||||
public NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>>.ParallelWriter animatorOverrideAnimationsMap;
|
||||
|
||||
BlobAssetReference<ControllerAnimationsBlob> controllerAnimationsBlob;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var layerBuffers = chunk.GetBufferAccessor(ref controllerLayersBufferHandle);
|
||||
var parameterBuffers = chunk.GetBufferAccessor(ref controllerParametersBufferHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var layers = layerBuffers[i];
|
||||
var parameters = parameterBuffers.Length > 0 ? parameterBuffers[i].AsNativeArray() : default;
|
||||
var e = entities[i];
|
||||
|
||||
DynamicBuffer<AnimatorControllerEventComponent> controllerEventsBuffer = default;
|
||||
if (controllerEventsBufferLookup.HasBuffer(e) && controllerEventsBufferLookup.IsBufferEnabled(e))
|
||||
controllerEventsBuffer = controllerEventsBufferLookup[e];
|
||||
|
||||
ExecuteSingle(layers, parameters, ref controllerEventsBuffer, e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void ExecuteSingle
|
||||
(
|
||||
in DynamicBuffer<AnimatorControllerLayerComponent> aclc,
|
||||
in NativeArray<AnimatorControllerParameterComponent> acpc,
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
Entity entity
|
||||
)
|
||||
{
|
||||
if (events.IsCreated)
|
||||
events.Clear();
|
||||
|
||||
var numIntsForBitMemory = BitFieldN.CalculateUIntsCountForGivenBitCount(acpc.Length);
|
||||
var triggersToResetMem = stackalloc uint[numIntsForBitMemory];
|
||||
var triggersToReset = new BitFieldN(triggersToResetMem, numIntsForBitMemory);
|
||||
|
||||
var startIndex = 0;
|
||||
for (int i = 0; i < aclc.Length; ++i)
|
||||
{
|
||||
ref var acc = ref aclc.ElementAt(i);
|
||||
|
||||
// Save controller animations blob asset reference in class variable, because passing it inside almost all functions will bloat signatures significantly
|
||||
controllerAnimationsBlob = FillAnimationsFromControllerSystem.GetControllerAnimationsBlob
|
||||
(entity, animatorOverrideAnimationLookup, acc.animations, animatorOverrideAnimationsMap);
|
||||
|
||||
ProcessLayer(ref acc.controller.Value, acc.layerIndex, acpc, aclc, ref events, triggersToReset);
|
||||
if (events.IsCreated)
|
||||
{
|
||||
EmitStateUpdateEvents(ref events, acc, startIndex);
|
||||
startIndex = events.Length;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset affected triggers
|
||||
ResetTriggers(acpc, triggersToReset);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ResetTriggers(NativeArray<AnimatorControllerParameterComponent> acpc, BitFieldN triggersToReset)
|
||||
{
|
||||
if (!triggersToReset.TestAny())
|
||||
return;
|
||||
|
||||
for (var i = 0; i < acpc.Length; ++i)
|
||||
{
|
||||
var p = acpc[i];
|
||||
if (p.type == ControllerParameterType.Trigger && triggersToReset.IsSet(i))
|
||||
{
|
||||
p.BoolValue = false;
|
||||
acpc[i] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RuntimeAnimatorData.StateData InitControllerStateData(ref RuntimeAnimatorData rtd, int stateID)
|
||||
{
|
||||
var rv = rtd.MakeDefaultState();
|
||||
rv.id = stateID;
|
||||
rv.normalizedDuration = 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TryExitTransition(ref AnimatorControllerLayerComponent acc, ref DynamicBuffer<AnimatorControllerEventComponent> events)
|
||||
{
|
||||
if (acc.rtd.activeTransition.id < 0)
|
||||
return;
|
||||
|
||||
if (CheckTransitionExitConditions(acc.rtd.activeTransition))
|
||||
{
|
||||
// Add state exit event
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateExit, acc, acc.rtd.srcState.id, acc.rtd.srcState.normalizedDuration);
|
||||
|
||||
acc.rtd.srcState = acc.rtd.dstState;
|
||||
acc.rtd.ClearStateSnapshots();
|
||||
acc.rtd.dstState = acc.rtd.MakeDefaultState();
|
||||
acc.rtd.activeTransition = acc.rtd.MakeDefaultTransition();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetValidTransitionForCurrentFrame
|
||||
(
|
||||
ref BlobArray<TransitionBlob> transitions,
|
||||
float normalizedStateDuration,
|
||||
float srcStateDurationFrameDelta,
|
||||
NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
var isTransitionFits = false;
|
||||
int i = 0;
|
||||
for (; i < transitions.Length && !isTransitionFits; ++i)
|
||||
{
|
||||
ref var t = ref transitions[i];
|
||||
isTransitionFits =
|
||||
CheckTransitionEnterExitTimeCondition(ref t, normalizedStateDuration, srcStateDurationFrameDelta) &&
|
||||
CheckTransitionEnterConditions(ref t, runtimeParams, triggersToReset);
|
||||
}
|
||||
return isTransitionFits ? i - 1 : -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TryEnterTransition
|
||||
(
|
||||
in DynamicBuffer<AnimatorControllerLayerComponent> aclc,
|
||||
ref ControllerBlob controllerBlob,
|
||||
int layerIndex,
|
||||
NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float srcStateDurationFrameDelta,
|
||||
float curStateDuration,
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
ref var acc = ref aclc.ElementAt(layerIndex);
|
||||
ref var layer = ref controllerBlob.layers[layerIndex];
|
||||
ref var currentState = ref layer.states[acc.rtd.srcState.id];
|
||||
|
||||
if (acc.rtd.activeTransition.id >= 0)
|
||||
{
|
||||
TryTransitionInterruption(ref layer, ref acc.rtd, srcStateDurationFrameDelta, runtimeParams, triggersToReset);
|
||||
return;
|
||||
}
|
||||
|
||||
var newTransitionIndex = GetValidTransitionForCurrentFrame(ref currentState.transitions, acc.rtd.srcState.normalizedDuration, srcStateDurationFrameDelta, runtimeParams, triggersToReset);
|
||||
if (newTransitionIndex < 0)
|
||||
return;
|
||||
|
||||
ref var t = ref currentState.transitions[newTransitionIndex];
|
||||
var timeShouldBeInTransition = GetTimeInSecondsShouldBeInTransition(ref t, acc.rtd.srcState.normalizedDuration, curStateDuration, srcStateDurationFrameDelta);
|
||||
acc.rtd.activeTransition.id = newTransitionIndex;
|
||||
acc.rtd.activeTransition.length = GetTransitionLength(ref t);
|
||||
acc.rtd.activeTransition.normalizedDuration = timeShouldBeInTransition / CalculateTransitionDuration(acc.rtd.activeTransition, curStateDuration);
|
||||
var dstStateDur = CalculateStateDuration(layerIndex, t.targetStateId, ref controllerBlob, aclc.AsNativeArray(), runtimeParams);
|
||||
acc.rtd.dstState = InitControllerStateData(ref acc.rtd, t.targetStateId);
|
||||
acc.rtd.dstState.normalizedDuration += timeShouldBeInTransition / dstStateDur + t.offset;
|
||||
|
||||
// Add state enter event
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateEnter, acc, acc.rtd.dstState.id, acc.rtd.dstState.normalizedDuration);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EmitEvent
|
||||
(
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
AnimatorControllerEventComponent.EventType eventType,
|
||||
AnimatorControllerLayerComponent aclc,
|
||||
int stateId,
|
||||
float stateDuration
|
||||
)
|
||||
{
|
||||
if (!events.IsCreated)
|
||||
return;
|
||||
|
||||
var evt = new AnimatorControllerEventComponent()
|
||||
{
|
||||
eventType = eventType,
|
||||
stateId = stateId,
|
||||
layerId = aclc.layerIndex,
|
||||
timeInState = stateDuration,
|
||||
};
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
aclc.controller.Value.layers[aclc.layerIndex].states[stateId].name.CopyToWithTruncate(ref evt.stateName);
|
||||
#endif
|
||||
|
||||
events.Add(evt);
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EmitStateUpdateEvents(ref DynamicBuffer<AnimatorControllerEventComponent> events, in AnimatorControllerLayerComponent acc, int startIndex)
|
||||
{
|
||||
if (!events.IsCreated)
|
||||
return;
|
||||
|
||||
var srcStateEnterExit = false;
|
||||
var dstStateEnterExit = false;
|
||||
for (var i = startIndex; i < events.Length; ++i)
|
||||
{
|
||||
var e = events[i];
|
||||
if (acc.rtd.srcState.id >= 0 && e.stateId == acc.rtd.srcState.id)
|
||||
srcStateEnterExit = true;
|
||||
if (acc.rtd.dstState.id >= 0 && e.stateId == acc.rtd.dstState.id)
|
||||
dstStateEnterExit = true;
|
||||
}
|
||||
|
||||
if (acc.rtd.srcState.id >= 0 && !srcStateEnterExit)
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateUpdate, acc, acc.rtd.srcState.id, acc.rtd.srcState.normalizedDuration);
|
||||
if (acc.rtd.dstState.id >= 0 && !dstStateEnterExit)
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateUpdate, acc, acc.rtd.dstState.id, acc.rtd.dstState.normalizedDuration);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProcessLayer
|
||||
(
|
||||
ref ControllerBlob c,
|
||||
int layerIndex,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
DynamicBuffer<AnimatorControllerLayerComponent> aclc,
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
ref var layer = ref c.layers[layerIndex];
|
||||
ref var acc = ref aclc.ElementAt(layerIndex);
|
||||
|
||||
var currentStateID = acc.rtd.srcState.id;
|
||||
if (currentStateID < 0)
|
||||
currentStateID = layer.defaultStateIndex;
|
||||
|
||||
// Adjust delta time according to the layer animator speed
|
||||
var layerDeltaTime = dt * acc.speed;
|
||||
|
||||
var curStateDuration = CalculateStateDuration(layerIndex, currentStateID, ref c, aclc.AsNativeArray(), runtimeParams);
|
||||
|
||||
if (Hint.Unlikely(acc.rtd.srcState.id < 0))
|
||||
{
|
||||
acc.rtd.srcState = InitControllerStateData(ref acc.rtd, layer.defaultStateIndex);
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateEnter, acc, acc.rtd.srcState.id, acc.rtd.srcState.normalizedDuration);
|
||||
}
|
||||
|
||||
var srcStateDurationFrameDelta = CalculateStateFrameDeltaSafe(layerDeltaTime, curStateDuration);
|
||||
acc.rtd.srcState.normalizedDuration += srcStateDurationFrameDelta;
|
||||
|
||||
if (acc.rtd.dstState.id >= 0)
|
||||
{
|
||||
var dstStateDuration = CalculateStateDuration(layerIndex, acc.rtd.dstState.id, ref c, aclc.AsNativeArray(), runtimeParams);
|
||||
acc.rtd.dstState.normalizedDuration += CalculateStateFrameDeltaSafe(layerDeltaTime, dstStateDuration);
|
||||
}
|
||||
|
||||
if (acc.rtd.activeTransition.id >= 0)
|
||||
{
|
||||
var transitionDuration = CalculateTransitionDuration(acc.rtd.activeTransition, curStateDuration);
|
||||
acc.rtd.activeTransition.normalizedDuration += layerDeltaTime / transitionDuration;
|
||||
}
|
||||
|
||||
TryExitTransition(ref acc, ref events);
|
||||
TryEnterTransition(aclc, ref c, layerIndex, runtimeParams, srcStateDurationFrameDelta, curStateDuration, ref events, triggersToReset);
|
||||
// Check transition exit conditions one more time in case of Enter->Exit sequence appeared in single frame
|
||||
TryExitTransition(ref acc, ref events);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateStateFrameDeltaSafe(float dt, float stateDuration)
|
||||
{
|
||||
var rv = dt / stateDuration;
|
||||
rv = math.select(0, rv, math.isfinite(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateMotionDuration
|
||||
(
|
||||
ref MotionBlob mb,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float weight
|
||||
)
|
||||
{
|
||||
if (weight == 0) return 0;
|
||||
|
||||
switch (mb.type)
|
||||
{
|
||||
case MotionBlob.Type.None:
|
||||
return 1;
|
||||
case MotionBlob.Type.AnimationClip:
|
||||
var animationHash = controllerAnimationsBlob.Value.animations[mb.animationIndex];
|
||||
var animBlob = BlobDatabaseSingleton.GetBlobAsset(animationHash, animationDatabase);
|
||||
if (animBlob != BlobAssetReference<AnimationClipBlob>.Null)
|
||||
return animBlob.Value.length * weight;
|
||||
return 1;
|
||||
}
|
||||
|
||||
var childMotions = ScriptedAnimator.GetChildMotionsList(ref mb, runtimeParams);
|
||||
var rv = CalculateBlendTreeMotionDuration(childMotions, ref mb.blendTree.motions, runtimeParams, weight);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateBlendTreeMotionDuration
|
||||
(
|
||||
NativeList<ScriptedAnimator.MotionIndexAndWeight> miwArr,
|
||||
ref BlobArray<ChildMotionBlob> motions,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float weight
|
||||
)
|
||||
{
|
||||
if (!miwArr.IsCreated || miwArr.IsEmpty)
|
||||
return 1;
|
||||
|
||||
var weightSum = 0.0f;
|
||||
for (int i = 0; i < miwArr.Length; ++i)
|
||||
weightSum += miwArr[i].weight;
|
||||
|
||||
// If total weight less then 1, normalize weights
|
||||
if (Hint.Unlikely(weightSum < 1))
|
||||
{
|
||||
for (int i = 0; i < miwArr.Length; ++i)
|
||||
{
|
||||
var miw = miwArr[i];
|
||||
miw.weight = miw.weight / weightSum;
|
||||
miwArr[i] = miw;
|
||||
}
|
||||
}
|
||||
|
||||
var rv = 0.0f;
|
||||
for (int i = 0; i < miwArr.Length; ++i)
|
||||
{
|
||||
var miw = miwArr[i];
|
||||
ref var m = ref motions[miw.motionIndex];
|
||||
rv += CalculateMotionDuration(ref m.motion, runtimeParams, weight * miw.weight) / m.timeScale;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Negative value means that length is a normalized value from source state length
|
||||
float GetTransitionLength(ref TransitionBlob tb)
|
||||
{
|
||||
return math.select(-tb.duration, tb.duration, tb.hasFixedDuration);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateTransitionDuration(in RuntimeAnimatorData.TransitionData trd, float curStateDuration)
|
||||
{
|
||||
var rv = math.abs(trd.length);
|
||||
if (trd.length < 0)
|
||||
{
|
||||
rv *= curStateDuration;
|
||||
}
|
||||
return math.max(rv, 0.0001f);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateStateDuration
|
||||
(
|
||||
int layerIndex,
|
||||
int stateId,
|
||||
ref ControllerBlob controllerBlob,
|
||||
in NativeArray<AnimatorControllerLayerComponent> aclc,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams
|
||||
)
|
||||
{
|
||||
ref var layer = ref controllerBlob.layers[layerIndex];
|
||||
ref var sb = ref layer.states[stateId];
|
||||
var motionDuration = CalculateMotionDuration(ref sb.motion, runtimeParams, 1);
|
||||
|
||||
// In case of layer sync option is enabled, adjust state duration with respect to "timing" property
|
||||
if (Hint.Unlikely(layer.syncedLayerIndex >= 0))
|
||||
{
|
||||
// Override controller must be exact copy of current
|
||||
ref var baseState = ref controllerBlob.layers[layer.syncedLayerIndex].states[stateId];
|
||||
var baseMotionDuration = CalculateMotionDuration(ref baseState.motion, runtimeParams, 1);
|
||||
var weightedMotionDuration = math.lerp(baseMotionDuration, motionDuration, aclc[layerIndex].weight);
|
||||
motionDuration = math.select(baseMotionDuration, weightedMotionDuration, layer.syncedTiming > 0);
|
||||
}
|
||||
else if (Hint.Unlikely(layer.syncedTiming >= 0))
|
||||
{
|
||||
ref var syncedState = ref controllerBlob.layers[layer.syncedTiming].states[stateId];
|
||||
var syncedStateDuration = CalculateMotionDuration(ref syncedState.motion, runtimeParams, 1);
|
||||
motionDuration = math.lerp(motionDuration, syncedStateDuration, aclc[layer.syncedTiming].weight);
|
||||
}
|
||||
|
||||
var speedMultiplier = 1.0f;
|
||||
if (sb.speedMultiplierParameterIndex >= 0)
|
||||
{
|
||||
speedMultiplier = runtimeParams[sb.speedMultiplierParameterIndex].FloatValue;
|
||||
}
|
||||
var rv = motionDuration / (sb.speed * speedMultiplier);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal static float GetLoopAwareTransitionExitTime(float exitTime, float normalizedDuration, float speedSign)
|
||||
{
|
||||
var rv = exitTime;
|
||||
if (exitTime <= 1.0f)
|
||||
{
|
||||
// Unity animator logic and documentation mismatch. Documentation says that exit time loop condition should be when transition exitTime less then 1, but in practice it will loop when exitTime is less or equal(!) to 1.
|
||||
exitTime = math.min(exitTime, 0.9999f);
|
||||
var snd = normalizedDuration * speedSign;
|
||||
|
||||
var f = math.frac(snd);
|
||||
rv += (int)snd;
|
||||
if (f > exitTime)
|
||||
rv += 1;
|
||||
}
|
||||
return rv * speedSign;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float GetTimeInSecondsShouldBeInTransition(ref TransitionBlob tb, float normalizedStateDuration, float curStateDuration, float frameDT)
|
||||
{
|
||||
if (!tb.hasExitTime) return 0;
|
||||
|
||||
// This should be always less then curStateRTD.normalizedDuration
|
||||
var loopAwareExitTime = GetLoopAwareTransitionExitTime(tb.exitTime, normalizedStateDuration - frameDT, math.sign(frameDT));
|
||||
var loopDelta = normalizedStateDuration - loopAwareExitTime;
|
||||
var rv = loopDelta * curStateDuration;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckTransitionEnterExitTimeCondition(ref TransitionBlob tb, float normalizedStateDuration, float srcStateDurationFrameDelta)
|
||||
{
|
||||
var noNormalConditions = tb.conditions.Length == 0;
|
||||
if (!tb.hasExitTime) return !noNormalConditions;
|
||||
|
||||
var l0 = normalizedStateDuration - srcStateDurationFrameDelta;
|
||||
var l1 = normalizedStateDuration;
|
||||
var speedSign = math.select(-1, 1, l0 < l1);
|
||||
|
||||
var loopAwareExitTime = GetLoopAwareTransitionExitTime(tb.exitTime, l0, speedSign);
|
||||
|
||||
if (speedSign < 0)
|
||||
(l0, l1) = (l1, l0);
|
||||
|
||||
var rv = loopAwareExitTime > l0 && loopAwareExitTime <= l1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckIntCondition(in AnimatorControllerParameterComponent param, ref ConditionBlob c)
|
||||
{
|
||||
var rv = true;
|
||||
switch (c.conditionMode)
|
||||
{
|
||||
case AnimatorConditionMode.Equals:
|
||||
if (param.IntValue != c.threshold.intValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.Greater:
|
||||
if (param.IntValue <= c.threshold.intValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.Less:
|
||||
if (param.IntValue >= c.threshold.intValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.NotEqual:
|
||||
if (param.IntValue == c.threshold.intValue) rv = false;
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Unsupported condition type for int parameter value!");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckFloatCondition(in AnimatorControllerParameterComponent param, ref ConditionBlob c)
|
||||
{
|
||||
var rv = true;
|
||||
switch (c.conditionMode)
|
||||
{
|
||||
case AnimatorConditionMode.Greater:
|
||||
if (param.FloatValue <= c.threshold.floatValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.Less:
|
||||
if (param.FloatValue >= c.threshold.floatValue) rv = false;
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Unsupported condition type for int parameter value!");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckBoolCondition(in AnimatorControllerParameterComponent param, ref ConditionBlob c)
|
||||
{
|
||||
var rv = true;
|
||||
switch (c.conditionMode)
|
||||
{
|
||||
case AnimatorConditionMode.If:
|
||||
rv = param.BoolValue;
|
||||
break;
|
||||
case AnimatorConditionMode.IfNot:
|
||||
rv = !param.BoolValue;
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Unsupported condition type for int parameter value!");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MarkTriggersToReset(ref TransitionBlob tb, BitFieldN triggersToReset)
|
||||
{
|
||||
for (int i = 0; i < tb.conditions.Length; ++i)
|
||||
{
|
||||
ref var c = ref tb.conditions[i];
|
||||
// Mark all transition parameters as "need to be reset". We will check actual parameter type later, after all layers processing
|
||||
triggersToReset.Set(c.paramIdx, true);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckTransitionEnterConditions(ref TransitionBlob tb, NativeArray<AnimatorControllerParameterComponent> runtimeParams, BitFieldN triggersToReset)
|
||||
{
|
||||
if (tb.conditions.Length == 0)
|
||||
return true;
|
||||
|
||||
var rv = true;
|
||||
var hasTriggers = false;
|
||||
for (int i = 0; i < tb.conditions.Length && rv; ++i)
|
||||
{
|
||||
ref var c = ref tb.conditions[i];
|
||||
var param = runtimeParams[c.paramIdx];
|
||||
|
||||
switch (param.type)
|
||||
{
|
||||
case ControllerParameterType.Float:
|
||||
rv = CheckFloatCondition(param, ref c);
|
||||
break;
|
||||
case ControllerParameterType.Int:
|
||||
rv = CheckIntCondition(param, ref c);
|
||||
break;
|
||||
case ControllerParameterType.Bool:
|
||||
rv = CheckBoolCondition(param, ref c);
|
||||
break;
|
||||
case ControllerParameterType.Trigger:
|
||||
rv = CheckBoolCondition(param, ref c);
|
||||
hasTriggers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasTriggers && rv)
|
||||
MarkTriggersToReset(ref tb, triggersToReset);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckTransitionExitConditions(RuntimeAnimatorData.TransitionData transitionRuntimeData)
|
||||
{
|
||||
return transitionRuntimeData.normalizedDuration >= 1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void TryTransitionInterruption
|
||||
(
|
||||
ref LayerBlob layerBlob,
|
||||
ref RuntimeAnimatorData rtd,
|
||||
float srcStateDurationFrameDelta,
|
||||
NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
ref var srcStateTransitions = ref layerBlob.states[rtd.srcState.id].transitions;
|
||||
var transitionIndex = rtd.activeTransition.id;
|
||||
// Transition index could be greater than number of actual transitions when 'ScriptedAnimator.CrossFade' is used
|
||||
if (transitionIndex >= srcStateTransitions.Length)
|
||||
return;
|
||||
|
||||
// We are in transition right now. Perform transition interruption logic
|
||||
ref var activeTransition = ref srcStateTransitions[transitionIndex];
|
||||
// Can't interrupt, return
|
||||
if (Hint.Likely(activeTransition.interruptionSource == TransitionBlob.InterruptionSource.None))
|
||||
return;
|
||||
|
||||
// Make copy of triggers to reset flags because I don't know if there are valid interruption transitions yet
|
||||
var triggersToResetMem = stackalloc uint[triggersToReset.SizeInInts()];
|
||||
var triggersClone = triggersToReset.Clone(triggersToResetMem);
|
||||
|
||||
var transitionIndexCandidateSrc = -1;
|
||||
var transitionIndexCandidateDst = -1;
|
||||
var canBeInterrupted = false;
|
||||
|
||||
if
|
||||
(
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.Source ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.SourceThenDestination ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.DestinationThenSource
|
||||
)
|
||||
{
|
||||
transitionIndexCandidateSrc = GetValidTransitionForCurrentFrame(ref srcStateTransitions, rtd.srcState.normalizedDuration, srcStateDurationFrameDelta, runtimeParams, triggersClone);
|
||||
canBeInterrupted |=
|
||||
transitionIndexCandidateSrc >= 0 &&
|
||||
(activeTransition.orderedInterruption ? transitionIndexCandidateSrc < transitionIndex : transitionIndexCandidateSrc != transitionIndex);
|
||||
}
|
||||
|
||||
ref var dstStateTransitions = ref layerBlob.states[rtd.dstState.id].transitions;
|
||||
if
|
||||
(
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.Destination ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.SourceThenDestination ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.DestinationThenSource
|
||||
)
|
||||
{
|
||||
transitionIndexCandidateDst = GetValidTransitionForCurrentFrame(ref dstStateTransitions, rtd.dstState.normalizedDuration, srcStateDurationFrameDelta, runtimeParams, triggersClone);
|
||||
canBeInterrupted |= transitionIndexCandidateDst >= 0;
|
||||
}
|
||||
|
||||
if (!canBeInterrupted)
|
||||
return;
|
||||
|
||||
// There is valid interruption
|
||||
// Select interrupting transition
|
||||
var interruptingTransitionID = -1;
|
||||
TransitionBlob* transitionBlobsPtr = null;
|
||||
switch (activeTransition.interruptionSource)
|
||||
{
|
||||
case TransitionBlob.InterruptionSource.Source:
|
||||
interruptingTransitionID = transitionIndexCandidateSrc;
|
||||
transitionBlobsPtr = (TransitionBlob*)srcStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
case TransitionBlob.InterruptionSource.Destination:
|
||||
interruptingTransitionID = transitionIndexCandidateDst;
|
||||
transitionBlobsPtr = (TransitionBlob*)dstStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
case TransitionBlob.InterruptionSource.SourceThenDestination:
|
||||
interruptingTransitionID = math.select(transitionIndexCandidateDst, transitionIndexCandidateSrc, transitionIndexCandidateSrc >= 0);
|
||||
transitionBlobsPtr = transitionIndexCandidateSrc >= 0 ? (TransitionBlob*)srcStateTransitions.GetUnsafePtr() : (TransitionBlob*)dstStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
case TransitionBlob.InterruptionSource.DestinationThenSource:
|
||||
interruptingTransitionID = math.select(transitionIndexCandidateSrc, transitionIndexCandidateDst, transitionIndexCandidateDst >= 0);
|
||||
transitionBlobsPtr = transitionIndexCandidateDst >= 0 ? (TransitionBlob*)dstStateTransitions.GetUnsafePtr() : (TransitionBlob*)srcStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
default:
|
||||
// This should not happen
|
||||
BurstAssert.IsTrue(false, "Transition interruption invalid code path");
|
||||
return;
|
||||
}
|
||||
ref var newTransition = ref transitionBlobsPtr[interruptingTransitionID];
|
||||
|
||||
// Copy triggers back to original bitset
|
||||
triggersToReset.CopyFrom(triggersClone);
|
||||
|
||||
// If state snapshot array is empty, then push both destination state and source state
|
||||
// If there are some state snapshots in array, the these snapshots are our "source state" already, so we need to add only destination state
|
||||
if (rtd.srcStateSnapshots.IsEmpty)
|
||||
rtd.PushStateSnapshot(rtd.srcState.id, 1, rtd.srcState.normalizedDuration, rtd.srcState.motionId);
|
||||
rtd.PushStateSnapshot(rtd.dstState.id, rtd.activeTransition.normalizedDuration, rtd.dstState.normalizedDuration, rtd.dstState.motionId);
|
||||
|
||||
rtd.srcState.normalizedDuration = 0;
|
||||
// If interrupting transition belongs to next state, configure srcState accordingly
|
||||
if (transitionBlobsPtr == dstStateTransitions.GetUnsafePtr())
|
||||
rtd.srcState.id = rtd.dstState.id;
|
||||
|
||||
rtd.activeTransition.id = interruptingTransitionID;
|
||||
rtd.activeTransition.length = GetTransitionLength(ref newTransition);
|
||||
rtd.activeTransition.normalizedDuration = 0;
|
||||
|
||||
rtd.dstState = InitControllerStateData(ref rtd, newTransition.targetStateId);
|
||||
rtd.dstState.normalizedDuration = newTransition.offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66f7113131a9adf41bd19f34c3f9434c
|
||||
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/AnimatorController/AnimatorControllerSystem_Job.cs
|
||||
uploadId: 897522
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AnimatorParametersAspect
|
||||
{
|
||||
public AnimatorControllerParameterIndexTableComponent indexTable;
|
||||
public DynamicBuffer<AnimatorControllerParameterComponent> parametersArr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimatorParametersAspect(DynamicBuffer<AnimatorControllerParameterComponent> parametersArr, AnimatorControllerParameterIndexTableComponent indexTable)
|
||||
{
|
||||
this.parametersArr = parametersArr;
|
||||
this.indexTable = indexTable;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public float GetFloatParameter(FastAnimatorParameter fp) => GetParameterValue(fp).floatValue;
|
||||
public int GetIntParameter(FastAnimatorParameter fp) => GetParameterValue(fp).intValue;
|
||||
public bool GetBoolParameter(FastAnimatorParameter fp) => GetParameterValue(fp).boolValue;
|
||||
public float GetFloatParameter(uint h) => GetParameterValue(h).floatValue;
|
||||
public int GetIntParameter(uint h) => GetParameterValue(h).intValue;
|
||||
public bool GetBoolParameter(uint h) => GetParameterValue(h).boolValue;
|
||||
public float GetFloatParameter(FixedStringName n) => GetParameterValue(n).floatValue;
|
||||
public int GetIntParameter(FixedStringName n) => GetParameterValue(n).intValue;
|
||||
public bool GetBoolParameter(FixedStringName n) => GetParameterValue(n).boolValue;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValue(FastAnimatorParameter fp)
|
||||
{
|
||||
ParameterValue rv;
|
||||
if (indexTable.value.IsCreated)
|
||||
fp.GetRuntimeParameterData(indexTable.value, parametersArr, out rv);
|
||||
else
|
||||
fp.GetRuntimeParameterData(parametersArr, out rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValue(uint parameterHash)
|
||||
{
|
||||
var fp = new FastAnimatorParameter()
|
||||
{
|
||||
hash = parameterHash,
|
||||
};
|
||||
return GetParameterValue(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValue(FixedStringName parameterName)
|
||||
{
|
||||
var fp = new FastAnimatorParameter(parameterName);
|
||||
return GetParameterValue(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValueByIndex(int paramIndex)
|
||||
{
|
||||
return parametersArr.ElementAt(paramIndex).value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValue(FastAnimatorParameter fp, ParameterValue value)
|
||||
{
|
||||
if (indexTable.value.IsCreated)
|
||||
fp.SetRuntimeParameterData(indexTable.value, parametersArr, value);
|
||||
else
|
||||
fp.SetRuntimeParameterData(parametersArr, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetTrigger(FastAnimatorParameter fp)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { boolValue = true });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void ResetTrigger(FastAnimatorParameter fp)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { boolValue = false });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetIntParameter(FastAnimatorParameter fp, int v)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { intValue = v });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetFloatParameter(FastAnimatorParameter fp, float v)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { floatValue = v });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetBoolParameter(FastAnimatorParameter fp, bool v)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { boolValue = v });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValueByIndex(int paramIndex, ParameterValue value)
|
||||
{
|
||||
parametersArr.ElementAt(paramIndex).value = value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValue(uint parameterHash, ParameterValue value)
|
||||
{
|
||||
var fp = new FastAnimatorParameter()
|
||||
{
|
||||
hash = parameterHash,
|
||||
};
|
||||
SetParameterValue(fp, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValue(FixedStringName parameterName, ParameterValue value)
|
||||
{
|
||||
var fp = new FastAnimatorParameter(parameterName);
|
||||
SetParameterValue(fp, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int GetParameterIndex(FastAnimatorParameter fp)
|
||||
{
|
||||
var index = indexTable.value.IsCreated ?
|
||||
fp.GetRuntimeParameterIndex(indexTable.value, parametersArr) :
|
||||
fp.GetRuntimeParameterIndex(parametersArr);
|
||||
|
||||
return index;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool HasParameter(FastAnimatorParameter fp)
|
||||
{
|
||||
return GetParameterIndex(fp) != -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool HasParameter(uint parameterHash)
|
||||
{
|
||||
var fp = new FastAnimatorParameter()
|
||||
{
|
||||
hash = parameterHash,
|
||||
};
|
||||
return HasParameter(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool HasParameter(FixedStringName parameterName)
|
||||
{
|
||||
var fp = new FastAnimatorParameter(parameterName);
|
||||
return HasParameter(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int ParametersCount() => parametersArr.Length;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20aa9d2bf9a03ba45be985d8bc55cdd7
|
||||
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/AnimatorController/AnimatorParameterAspect.cs
|
||||
uploadId: 897522
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public readonly struct AnimatorStateQueryAspect
|
||||
{
|
||||
readonly DynamicBuffer<AnimatorControllerLayerComponent> layersArr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimatorStateQueryAspect(DynamicBuffer<AnimatorControllerLayerComponent> layers)
|
||||
{
|
||||
layersArr = layers;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct RuntimeStateInfo
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedStringName name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public float normalizedTime;
|
||||
}
|
||||
|
||||
public struct RuntimeTransitionInfo
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedStringName name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public float normalizedTime;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RuntimeStateInfo GetLayerCurrentStateInfo(int layerIndex)
|
||||
{
|
||||
if (layersArr.Length <= layerIndex)
|
||||
return default;
|
||||
|
||||
var layerRuntimeData = layersArr[layerIndex];
|
||||
ref var layerBlob = ref layerRuntimeData.controller.Value.layers[layerIndex];
|
||||
var curStateID = layerRuntimeData.rtd.srcState.id;
|
||||
|
||||
if (curStateID < 0 || curStateID >= layerBlob.states.Length)
|
||||
return default;
|
||||
|
||||
var rv = new RuntimeStateInfo()
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
name = layerBlob.states[curStateID].name.ToFixedString(),
|
||||
#endif
|
||||
hash = layerBlob.states[curStateID].hash,
|
||||
normalizedTime = layerRuntimeData.rtd.srcState.normalizedDuration,
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RuntimeTransitionInfo GetLayerCurrentTransitionInfo(int layerIndex)
|
||||
{
|
||||
if (layersArr.Length <= layerIndex)
|
||||
return default;
|
||||
|
||||
var layerRuntimeData = layersArr[layerIndex];
|
||||
ref var layerBlob = ref layerRuntimeData.controller.Value.layers[layerIndex];
|
||||
var curTransitionID = layerRuntimeData.rtd.activeTransition.id;
|
||||
var curStateID = layerRuntimeData.rtd.srcState.id;
|
||||
|
||||
if (curTransitionID < 0 || curStateID < 0 || curStateID >= layerBlob.states.Length)
|
||||
return default;
|
||||
|
||||
var rv = new RuntimeTransitionInfo()
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
name = layerBlob.states[curStateID].transitions[curTransitionID].name.ToFixedString(),
|
||||
#endif
|
||||
hash = layerBlob.states[curStateID].transitions[curTransitionID].hash,
|
||||
normalizedTime = layerRuntimeData.rtd.activeTransition.normalizedDuration
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool IsInTransition(int layerIndex)
|
||||
{
|
||||
if (layersArr.Length <= layerIndex)
|
||||
return default;
|
||||
|
||||
var layerRuntimeData = layersArr[layerIndex];
|
||||
var rv = layerRuntimeData.rtd.activeTransition.id >= 0;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4d517a04d48e334796e296fb7c2d66e
|
||||
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/AnimatorController/AnimatorStateQueryAspect.cs
|
||||
uploadId: 897522
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Rukhanka.Toolbox;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct FastAnimatorParameter
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedStringName paramName;
|
||||
#endif
|
||||
public uint hash;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public FastAnimatorParameter(FixedStringName name)
|
||||
{
|
||||
hash = name.CalculateHash32();
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
paramName = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public FastAnimatorParameter(uint hash)
|
||||
{
|
||||
this.hash = hash;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
paramName = default;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GetRuntimeParameterDataInternal(int paramIdx, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, out ParameterValue outData)
|
||||
{
|
||||
bool isValid = paramIdx >= 0;
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
outData = runtimeParameters[paramIdx].value;
|
||||
}
|
||||
else
|
||||
{
|
||||
outData = default;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
Debug.LogError($"Could find animator parameter with name {paramName} in hash table! Returning default value!");
|
||||
#endif
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool GetRuntimeParameterData(BlobAssetReference<PerfectHashTableBlob> pt, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, out ParameterValue outData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(pt, runtimeParameters);
|
||||
return GetRuntimeParameterDataInternal(paramIdx, runtimeParameters, out outData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool GetRuntimeParameterData(DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, out ParameterValue outData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(runtimeParameters);
|
||||
return GetRuntimeParameterDataInternal(paramIdx, runtimeParameters, out outData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SetRuntimeParameterDataInternal(int paramIdx, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, in ParameterValue paramData)
|
||||
{
|
||||
bool isValid = paramIdx >= 0;
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
var p = runtimeParameters[paramIdx];
|
||||
p.value = paramData;
|
||||
runtimeParameters[paramIdx] = p;
|
||||
}
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Could find animator parameter with name {paramName} in hash table! Setting value is failed!");
|
||||
}
|
||||
#endif
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool SetRuntimeParameterData(BlobAssetReference<PerfectHashTableBlob> pt, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, in ParameterValue paramData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(pt, runtimeParameters);
|
||||
return SetRuntimeParameterDataInternal(paramIdx, runtimeParameters, paramData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool SetRuntimeParameterData(DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, in ParameterValue paramData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(runtimeParameters);
|
||||
return SetRuntimeParameterDataInternal(paramIdx, runtimeParameters, paramData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool SetTrigger(BlobAssetReference<PerfectHashTableBlob> pt, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters) => SetRuntimeParameterData(pt, runtimeParameters, new ParameterValue() { boolValue = true });
|
||||
public bool SetTrigger(DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters) => SetRuntimeParameterData(runtimeParameters, new ParameterValue() { boolValue = true });
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Linear search variant
|
||||
public static int GetRuntimeParameterIndex(uint hash, in ReadOnlySpan<AnimatorControllerParameterComponent> parameters)
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; ++i)
|
||||
{
|
||||
var p = parameters[i];
|
||||
if (p.hash == hash)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Perfect hash table variant
|
||||
public static int GetRuntimeParameterIndex(uint hash, in BlobAssetReference<PerfectHashTableBlob> pt, in ReadOnlySpan<AnimatorControllerParameterComponent> parameters)
|
||||
{
|
||||
var paramIdx = pt.Value.Query(hash);
|
||||
if (paramIdx < 0)
|
||||
return -1;
|
||||
|
||||
return paramIdx;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe int GetRuntimeParameterIndex(in BlobAssetReference<PerfectHashTableBlob> pt, in DynamicBuffer<AnimatorControllerParameterComponent> acpc)
|
||||
{
|
||||
var span = new ReadOnlySpan<AnimatorControllerParameterComponent>(acpc.GetUnsafeReadOnlyPtr(), acpc.Length);
|
||||
return GetRuntimeParameterIndex(hash, pt, span);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe int GetRuntimeParameterIndex(in DynamicBuffer<AnimatorControllerParameterComponent> acpc)
|
||||
{
|
||||
var span = new ReadOnlySpan<AnimatorControllerParameterComponent>(acpc.GetUnsafeReadOnlyPtr(), acpc.Length);
|
||||
return GetRuntimeParameterIndex(hash, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa18070f79c037d47b59593d7d6cc720
|
||||
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/AnimatorController/FastAnimatorParameter.cs
|
||||
uploadId: 897522
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
using Rukhanka.WaybackMachine;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
[BurstCompile]
|
||||
[UpdateAfter(typeof(AnimatorControllerSystem<AnimatorControllerQuery>))]
|
||||
public partial struct FillAnimationsFromControllerSystem: ISystem
|
||||
{
|
||||
EntityQuery fillAnimationsBufferQuery;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
fillAnimationsBufferQuery = SystemAPI.QueryBuilder()
|
||||
.WithAll<AnimatorControllerLayerComponent, AnimationToProcessComponent>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(fillAnimationsBufferQuery);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnDestroy(ref SystemState ss)
|
||||
{
|
||||
if (SystemAPI.TryGetSingletonRW<InternalAnimatorDataSingleton>(out var internalDataSingleton))
|
||||
internalDataSingleton.ValueRW.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var entityTypeHandle = SystemAPI.GetEntityTypeHandle();
|
||||
var controllerLayersBufferHandleRO = SystemAPI.GetBufferTypeHandle<AnimatorControllerLayerComponent>(true);
|
||||
var controllerParametersBufferHandleRO = SystemAPI.GetBufferTypeHandle<AnimatorControllerParameterComponent>(true);
|
||||
var animatorOverrideAnimationsLookup = SystemAPI.GetComponentLookup<AnimatorOverrideAnimations>(true);
|
||||
var animationToProcessBufferHandle = SystemAPI.GetBufferTypeHandle<AnimationToProcessComponent>();
|
||||
var animDBSingleton = SystemAPI.GetSingleton<BlobDatabaseSingleton>();
|
||||
|
||||
var internalDataSingletonQuery = SystemAPI.QueryBuilder().WithAllRW<InternalAnimatorDataSingleton>().Build();
|
||||
var internalAnimatorData = GetInternalDataSingleton(internalDataSingletonQuery, ref ss);
|
||||
|
||||
var fillAnimationsBufferJob = new FillAnimationsBufferJob()
|
||||
{
|
||||
controllerLayersBufferHandle = controllerLayersBufferHandleRO,
|
||||
controllerParametersBufferHandle = controllerParametersBufferHandleRO,
|
||||
animationToProcessBufferHandle = animationToProcessBufferHandle,
|
||||
animatorOverrideAnimationLookup = animatorOverrideAnimationsLookup,
|
||||
entityTypeHandle = entityTypeHandle,
|
||||
animationDatabase = animDBSingleton.animations,
|
||||
avatarMaskDatabase = animDBSingleton.avatarMasks,
|
||||
animatorOverrideAnimationsMap = internalAnimatorData.animatorOverrideAnimationsMap.AsParallelWriter()
|
||||
};
|
||||
|
||||
ss.Dependency = fillAnimationsBufferJob.ScheduleParallel(fillAnimationsBufferQuery, ss.Dependency);
|
||||
ss.Dependency = CopyEventsForWaybackMachineDuringRecording(ref ss, ss.Dependency);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal static InternalAnimatorDataSingleton GetInternalDataSingleton(EntityQuery singletonQuery, ref SystemState ss)
|
||||
{
|
||||
if (singletonQuery.TryGetSingletonRW<InternalAnimatorDataSingleton>(out var internalAnimatorData))
|
||||
return internalAnimatorData.ValueRW;
|
||||
|
||||
var iads = InternalAnimatorDataSingleton.MakeDefault();
|
||||
ss.EntityManager.CreateSingleton(iads, "Rukhanka.InternalAnimatorDataSingleton");
|
||||
return iads;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal static unsafe BlobAssetReference<ControllerAnimationsBlob> GetControllerAnimationsBlob
|
||||
(
|
||||
Entity e,
|
||||
ComponentLookup<AnimatorOverrideAnimations> animatorOverrideAnimationLookup,
|
||||
BlobAssetReference<ControllerAnimationsBlob> cab,
|
||||
NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>>.ParallelWriter animatorOverrideAnimationsMap
|
||||
)
|
||||
{
|
||||
if (animatorOverrideAnimationLookup.TryGetComponent(e, out var animationOverrides) && animatorOverrideAnimationLookup.IsComponentEnabled(e))
|
||||
{
|
||||
// Try cache first
|
||||
var combinedHash = animationOverrides.value.GetHashCode() ^ cab.GetHashCode();
|
||||
if (UnsafeParallelHashMapBase<int, BlobAssetReference<ControllerAnimationsBlob>>
|
||||
.TryGetFirstValueAtomic(animatorOverrideAnimationsMap.m_Writer.m_Buffer, combinedHash, out var rv, out _))
|
||||
return rv;
|
||||
|
||||
// Merge controller animations and override animations
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var mergedBlobAsset = ref bb.ConstructRoot<ControllerAnimationsBlob>();
|
||||
var animsArr = bb.Allocate(ref mergedBlobAsset.animations, cab.Value.animations.Length);
|
||||
for (var i = 0; i < animsArr.Length; ++i)
|
||||
{
|
||||
var overrideAnim = animationOverrides.value.Value.animations[i];
|
||||
animsArr[i] = overrideAnim.IsValid ? overrideAnim : cab.Value.animations[i];
|
||||
}
|
||||
rv = bb.CreateBlobAssetReference<ControllerAnimationsBlob>(Allocator.Persistent);
|
||||
animatorOverrideAnimationsMap.TryAdd(combinedHash, rv);
|
||||
return rv;
|
||||
}
|
||||
return cab;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle CopyEventsForWaybackMachineDuringRecording(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
if (!SystemAPI.TryGetSingletonRW<RecordComponent>(out var rcd))
|
||||
return dependsOn;
|
||||
|
||||
var copyEventsToWaybackMachineJob = new CopyAnimatorEventsToWaybackMachineRecordingJob()
|
||||
{
|
||||
outEvents = rcd.ValueRW.wbData.Value.emittedAnimatorEvents
|
||||
};
|
||||
var jh = copyEventsToWaybackMachineJob.Schedule(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8142c7ce247b2d4449bacfa4ece227cc
|
||||
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/AnimatorController/FillAnimationsFromControllerSystem.cs
|
||||
uploadId: 897522
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using static Rukhanka.AnimatorControllerSystemJobs;
|
||||
using Rukhanka.WaybackMachine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public partial struct FillAnimationsFromControllerSystem
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
struct FillAnimationsBufferJob: IJobChunk
|
||||
{
|
||||
[ReadOnly]
|
||||
public BufferTypeHandle<AnimatorControllerLayerComponent> controllerLayersBufferHandle;
|
||||
[ReadOnly]
|
||||
public BufferTypeHandle<AnimatorControllerParameterComponent> controllerParametersBufferHandle;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorOverrideAnimations> animatorOverrideAnimationLookup;
|
||||
[ReadOnly]
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
[ReadOnly]
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> animationDatabase;
|
||||
[ReadOnly]
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>> avatarMaskDatabase;
|
||||
|
||||
public BufferTypeHandle<AnimationToProcessComponent> animationToProcessBufferHandle;
|
||||
public NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>>.ParallelWriter animatorOverrideAnimationsMap;
|
||||
|
||||
BlobAssetReference<ControllerAnimationsBlob> controllerAnimationsBlob;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var layerBuffers = chunk.GetBufferAccessor(ref controllerLayersBufferHandle);
|
||||
var parameterBuffers = chunk.GetBufferAccessor(ref controllerParametersBufferHandle);
|
||||
var animationsToProcessBuffers = chunk.GetBufferAccessor(ref animationToProcessBufferHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var layers = layerBuffers[i].AsNativeArray();
|
||||
var parameters = parameterBuffers.Length > 0 ? parameterBuffers[i].AsNativeArray() : default;
|
||||
var e = entities[i];
|
||||
|
||||
var animsBuf = animationsToProcessBuffers[i];
|
||||
|
||||
AddAnimationsForEntity(ref animsBuf, layers, parameters, e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AnimationsPostSetup(Span<AnimationToProcessComponent> animations, ref LayerBlob lb, int layerIndex, float weightMultiplier, float layerWeight)
|
||||
{
|
||||
// Set blending mode and adjust animations weight according to layer weight
|
||||
for (int k = 0; k < animations.Length; ++k)
|
||||
{
|
||||
var a = animations[k];
|
||||
a.blendMode = lb.blendingMode;
|
||||
a.layerWeight = layerWeight;
|
||||
a.layerIndex = layerIndex;
|
||||
a.weight *= weightMultiplier;
|
||||
a.avatarMask = BlobDatabaseSingleton.GetBlobAsset(lb.avatarMaskBlobHash, avatarMaskDatabase);
|
||||
animations[k] = a;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void AddAnimationsForEntity
|
||||
(
|
||||
ref DynamicBuffer<AnimationToProcessComponent> animations,
|
||||
in NativeArray<AnimatorControllerLayerComponent> aclc,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
Entity entity
|
||||
)
|
||||
{
|
||||
if (entity == Entity.Null)
|
||||
return;
|
||||
|
||||
animations.Clear();
|
||||
|
||||
// Need to skip zero weight layers
|
||||
for (int i = 0; i < aclc.Length; ++i)
|
||||
{
|
||||
var animationCurIndex = animations.Length;
|
||||
|
||||
var l = aclc[i];
|
||||
controllerAnimationsBlob = GetControllerAnimationsBlob(entity, animatorOverrideAnimationLookup, l.animations, animatorOverrideAnimationsMap);
|
||||
|
||||
var cb = l.controller;
|
||||
ref var lb = ref cb.Value.layers[i];
|
||||
if (l.weight == 0 || l.rtd.srcState.id < 0)
|
||||
continue;
|
||||
|
||||
ref var srcState0Blob = ref lb.states[l.rtd.srcState.id];
|
||||
|
||||
var srcStateWeight = 1.0f;
|
||||
var dstStateWeight = 0.0f;
|
||||
|
||||
if (l.rtd.activeTransition.id >= 0)
|
||||
{
|
||||
dstStateWeight = l.rtd.activeTransition.normalizedDuration;
|
||||
srcStateWeight = (1 - dstStateWeight);
|
||||
}
|
||||
|
||||
var srcStateTime = GetDurationTime(ref srcState0Blob, runtimeParams, l.rtd.srcState.normalizedDuration);
|
||||
|
||||
var dstStateAnimCount = 0;
|
||||
if (l.rtd.dstState.id >= 0)
|
||||
{
|
||||
ref var dstStateBlob = ref lb.states[l.rtd.dstState.id];
|
||||
var dstStateTime = GetDurationTime(ref dstStateBlob, runtimeParams, l.rtd.dstState.normalizedDuration);
|
||||
dstStateAnimCount = AddMotionForEntity(ref animations, ref dstStateBlob.motion, runtimeParams, 1, dstStateTime, l.rtd.dstState.motionId);
|
||||
}
|
||||
|
||||
var srcStateAnimCount = 0;
|
||||
|
||||
// No state snapshots - no transition interruption process
|
||||
// Default state motion processing
|
||||
if (Hint.Likely(l.rtd.srcStateSnapshots.Length == 0))
|
||||
{
|
||||
ref var srcStateBlob = ref lb.states[l.rtd.srcState.id];
|
||||
srcStateAnimCount += AddMotionForEntity(ref animations, ref srcStateBlob.motion, runtimeParams, 1, srcStateTime, l.rtd.srcState.motionId);
|
||||
}
|
||||
|
||||
// Transition interruption motions from state snapshots
|
||||
for (var k = l.rtd.srcStateSnapshots.length - 1; k >= 0; --k)
|
||||
{
|
||||
var stateData = l.rtd.srcStateSnapshots[k];
|
||||
ref var srcStateBlob = ref lb.states[stateData.id];
|
||||
srcStateAnimCount += AddMotionForEntity(ref animations, ref srcStateBlob.motion, runtimeParams, stateData.weight, stateData.normalizedTime, stateData.motionId);
|
||||
}
|
||||
|
||||
var animStartPtr = (AnimationToProcessComponent*)animations.GetUnsafePtr() + animationCurIndex;
|
||||
var dstAnimsSpan = new Span<AnimationToProcessComponent>(animStartPtr, dstStateAnimCount);
|
||||
var srcAnimsSpan = new Span<AnimationToProcessComponent>(animStartPtr + dstStateAnimCount, srcStateAnimCount);
|
||||
|
||||
var dstLayerMultiplier = math.select(dstStateWeight, 1, srcStateAnimCount > 0);
|
||||
var srcLayerMultiplier = math.select(srcStateWeight, 1, dstStateAnimCount > 0);
|
||||
dstStateWeight = math.select(1, dstStateWeight, srcStateAnimCount > 0);
|
||||
srcStateWeight = math.select(1, srcStateWeight, dstStateAnimCount > 0);
|
||||
|
||||
AnimationsPostSetup(dstAnimsSpan, ref lb, i, dstStateWeight, dstLayerMultiplier * l.weight);
|
||||
AnimationsPostSetup(srcAnimsSpan, ref lb, i, srcStateWeight, srcLayerMultiplier * l.weight);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AddAnimationForEntity
|
||||
(
|
||||
ref DynamicBuffer<AnimationToProcessComponent> outAnims,
|
||||
ref MotionBlob mb,
|
||||
float weight,
|
||||
float normalizedStateTime,
|
||||
uint motionId
|
||||
)
|
||||
{
|
||||
var atp = new AnimationToProcessComponent();
|
||||
|
||||
var animationHash = controllerAnimationsBlob.Value.animations[mb.animationIndex];
|
||||
atp.animation = BlobDatabaseSingleton.GetBlobAsset(animationHash, animationDatabase);
|
||||
atp.weight = weight;
|
||||
atp.time = normalizedStateTime;
|
||||
atp.motionId = mb.hash + motionId;
|
||||
outAnims.Add(atp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AddMotionsFromBlendtree
|
||||
(
|
||||
in NativeList<ScriptedAnimator.MotionIndexAndWeight> miws,
|
||||
ref DynamicBuffer<AnimationToProcessComponent> outAnims,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
ref BlobArray<ChildMotionBlob> motions,
|
||||
float weight,
|
||||
float normalizedStateTime,
|
||||
uint motionId
|
||||
)
|
||||
{
|
||||
for (int i = 0; i < miws.Length; ++i)
|
||||
{
|
||||
var miw = miws[i];
|
||||
ref var m = ref motions[miw.motionIndex];
|
||||
var finalWeight = weight * miw.weight;
|
||||
if (finalWeight > 0)
|
||||
AddMotionForEntity(ref outAnims, ref m.motion, runtimeParams, finalWeight, normalizedStateTime, (uint)(i + motionId));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int AddMotionForEntity
|
||||
(
|
||||
ref DynamicBuffer<AnimationToProcessComponent> outAnims,
|
||||
ref MotionBlob mb,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float weight,
|
||||
float normalizedStateTime,
|
||||
uint motionId
|
||||
)
|
||||
{
|
||||
var startLen = outAnims.Length;
|
||||
|
||||
switch (mb.type)
|
||||
{
|
||||
case MotionBlob.Type.None:
|
||||
break;
|
||||
case MotionBlob.Type.AnimationClip:
|
||||
AddAnimationForEntity(ref outAnims, ref mb, weight, normalizedStateTime, motionId);
|
||||
break;
|
||||
}
|
||||
|
||||
var childMotions = ScriptedAnimator.GetChildMotionsList(ref mb, runtimeParams);
|
||||
if (childMotions.IsCreated)
|
||||
{
|
||||
AddMotionsFromBlendtree(childMotions, ref outAnims, runtimeParams, ref mb.blendTree.motions, weight, normalizedStateTime, motionId);
|
||||
}
|
||||
|
||||
return outAnims.Length - startLen;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float GetDurationTime(ref StateBlob sb, in NativeArray<AnimatorControllerParameterComponent> runtimeParams, float normalizedDuration)
|
||||
{
|
||||
var timeDuration = normalizedDuration;
|
||||
if (sb.timeParameterIndex >= 0)
|
||||
{
|
||||
timeDuration = runtimeParams[sb.timeParameterIndex].FloatValue;
|
||||
}
|
||||
var stateCycleOffset = sb.cycleOffset;
|
||||
if (sb.cycleOffsetParameterIndex >= 0)
|
||||
{
|
||||
stateCycleOffset = runtimeParams[sb.cycleOffsetParameterIndex].FloatValue;
|
||||
}
|
||||
timeDuration += stateCycleOffset;
|
||||
return timeDuration;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
[WithAll(typeof(RecordComponent))]
|
||||
partial struct CopyAnimatorEventsToWaybackMachineRecordingJob: IJobEntity
|
||||
{
|
||||
public NativeList<AnimatorControllerEventComponent> outEvents;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in DynamicBuffer<AnimatorControllerEventComponent> acec)
|
||||
{
|
||||
outEvents.AddRange(acec.AsNativeArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9daf9e1f7afc80459aad91d4b092a7e
|
||||
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/AnimatorController/FillAnimationsFromControllerSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct InternalAnimatorDataSingleton: IComponentData
|
||||
{
|
||||
internal NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>> animatorOverrideAnimationsMap;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static InternalAnimatorDataSingleton MakeDefault()
|
||||
{
|
||||
var rv = new InternalAnimatorDataSingleton()
|
||||
{
|
||||
animatorOverrideAnimationsMap = new (0xff, Allocator.Persistent)
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kv in animatorOverrideAnimationsMap)
|
||||
kv.Value.Dispose();
|
||||
animatorOverrideAnimationsMap.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7c1e038272b2944aab7454e9960315e
|
||||
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/AnimatorController/InternalAnimatorDataSingleton.cs
|
||||
uploadId: 897522
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
using Unity.Collections;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
using Unity.NetCode;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct RuntimeAnimatorData
|
||||
{
|
||||
public struct StateSnapshot
|
||||
{
|
||||
public int id;
|
||||
public uint motionId;
|
||||
public float weight;
|
||||
public float normalizedTime;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct StateData
|
||||
{
|
||||
public int id;
|
||||
public uint motionId;
|
||||
public float normalizedDuration;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct TransitionData
|
||||
{
|
||||
public int id;
|
||||
public float normalizedDuration;
|
||||
public float length;
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public StateData MakeDefaultState() => new StateData() { id = -1, normalizedDuration = 0, motionId = GetNextMotionID() };
|
||||
public TransitionData MakeDefaultTransition() => new TransitionData() { id = -1, length = 0, normalizedDuration = 0 };
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint GetNextMotionID()
|
||||
{
|
||||
motionIdCounter += 0xff;
|
||||
return motionIdCounter;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public uint motionIdCounter;
|
||||
public StateData srcState;
|
||||
public StateData dstState;
|
||||
public TransitionData activeTransition;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
[GhostField(SendData = false)]
|
||||
#endif
|
||||
public FixedList64Bytes<StateSnapshot> srcStateSnapshots;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void ClearStateSnapshots() { srcStateSnapshots.Clear(); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void PushStateSnapshot(int stateID, float weight, float normalizedTime, uint motionId)
|
||||
{
|
||||
// If we are out of free space prune snapshot with the lowest weight
|
||||
if (srcStateSnapshots.length == srcStateSnapshots.Capacity)
|
||||
{
|
||||
var minWeight = 1.0f;
|
||||
var minWeightIndex = 0;
|
||||
for (var i = 0; i < srcStateSnapshots.length; ++i)
|
||||
{
|
||||
var w = srcStateSnapshots[i].weight;
|
||||
if (minWeight > w)
|
||||
{
|
||||
minWeight = w;
|
||||
minWeightIndex = i;
|
||||
}
|
||||
}
|
||||
if (minWeightIndex != srcStateSnapshots.length - 1)
|
||||
srcStateSnapshots[minWeightIndex] = srcStateSnapshots[^1];
|
||||
srcStateSnapshots.Length -= 1;
|
||||
}
|
||||
|
||||
// Scale existing weights
|
||||
for (var i = 0; i < srcStateSnapshots.length; ++i)
|
||||
{
|
||||
ref var sn = ref srcStateSnapshots.ElementAt(i);
|
||||
sn.weight *= 1 - weight;
|
||||
}
|
||||
var ss = new StateSnapshot() { id = stateID, weight = weight, normalizedTime = normalizedTime, motionId = motionId };
|
||||
srcStateSnapshots.Add(ss);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static RuntimeAnimatorData MakeDefault()
|
||||
{
|
||||
var rv = new RuntimeAnimatorData();
|
||||
rv.srcState = rv.MakeDefaultState();
|
||||
rv.dstState = rv.MakeDefaultState();
|
||||
rv.activeTransition = rv.MakeDefaultTransition();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da31f172d48d3614dac5465f62e8efe0
|
||||
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/AnimatorController/RuntimeAnimatorData.cs
|
||||
uploadId: 897522
|
||||
Reference in New Issue
Block a user