Files
Project-M/Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationApplicationSystem.cs
T
2026-05-31 14:27:52 -07:00

178 lines
7.2 KiB
C#

using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Deformations;
using Unity.Entities;
using Unity.Jobs;
using Unity.Rendering;
using Unity.Transforms;
/////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
[DisableAutoCreation]
[UpdateAfter(typeof(RukhankaAnimationInjectionSystemGroup))]
partial struct AnimationApplicationSystem: ISystem
{
private EntityQuery
boneObjectEntitiesWithParentQuery,
boneObjectEntitiesNoParentQuery;
NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> rigToSkinnedMeshRemapTables;
int newRemapTablesCounter;
/////////////////////////////////////////////////////////////////////////////////
[BurstCompile]
public void OnCreate(ref SystemState ss)
{
var eqb0 = new EntityQueryBuilder(Allocator.Temp)
.WithAll<AnimatorEntityRefComponent, Parent>()
.WithAllRW<LocalTransform>();
boneObjectEntitiesWithParentQuery = ss.GetEntityQuery(eqb0);
var eqb1 = new EntityQueryBuilder(Allocator.Temp)
.WithAll<AnimatorEntityRefComponent>()
.WithNone<Parent>()
.WithAllRW<LocalTransform>();
boneObjectEntitiesNoParentQuery = ss.GetEntityQuery(eqb1);
var rq = new EntityQueryBuilder(Allocator.Temp)
.WithAll<AnimatorEntityRefComponent>()
.Build(ref ss);
ss.RequireForUpdate(rq);
rigToSkinnedMeshRemapTables = new NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>>(128, Allocator.Persistent);
}
/////////////////////////////////////////////////////////////////////////////////
[BurstCompile]
public void OnDestroy(ref SystemState ss)
{
foreach (var kv in rigToSkinnedMeshRemapTables)
kv.Value.Dispose();
rigToSkinnedMeshRemapTables.Dispose();
}
/////////////////////////////////////////////////////////////////////////////////
[BurstCompile]
public void OnUpdate(ref SystemState ss)
{
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
var fillRigToSkinnedMeshRemapTablesJH = FillRigToSkinBonesRemapTableCache(ref ss, ss.Dependency);
// Propagate local animated transforms to the entities with and without parents
var propagateTRSToEntitiesWithParentsJH = PropagateAnimatedBonesToEntitiesTRS(ref ss, runtimeData, boneObjectEntitiesWithParentQuery, true, ss.Dependency);
var propagateTRSToEntitiesNoParentsJH = PropagateAnimatedBonesToEntitiesTRS(ref ss, runtimeData, boneObjectEntitiesNoParentQuery, false, propagateTRSToEntitiesWithParentsJH);
// Make corresponding skin matrices for all skinned meshes
var jh = JobHandle.CombineDependencies(fillRigToSkinnedMeshRemapTablesJH, propagateTRSToEntitiesNoParentsJH);
var applySkinJH = ApplySkinning(ref ss, runtimeData, jh);
// Update render bounds for meshes that request this
var updateRenderBoundsJH = UpdateRenderBounds(ref ss, runtimeData, ss.Dependency);
ss.Dependency = JobHandle.CombineDependencies(applySkinJH, updateRenderBoundsJH);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle FillRigToSkinBonesRemapTableCache(ref SystemState ss, JobHandle dependsOn)
{
var rigDefinitionComponentLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
newRemapTablesCounter = 0;
// Count new remap tables count
var countNumberOfNewRemapTablesJob = new CountNumberOfNewRemapTablesJob()
{
rigDefinitionArr = rigDefinitionComponentLookup,
numberOfNewRemapTables = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref newRemapTablesCounter)),
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables
};
var countNumberOfNewRemapTablesJH = countNumberOfNewRemapTablesJob.ScheduleParallel(dependsOn);
// Reserve necessary space in remap table cache
var increaseRigRemapTableCapacityJob = new IncreaseRigRemapTableCapacityJob()
{
numberOfNewRemapTables = (int*)UnsafeUtility.AddressOf(ref newRemapTablesCounter),
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables
};
var increaseRigRemapTableCapacityJH = increaseRigRemapTableCapacityJob.Schedule(countNumberOfNewRemapTablesJH);
// Fill table cache with new tables
var fillRigToSkinBonesRemapTableCacheJob = new FillRigToSkinBonesRemapTableCacheJob()
{
rigDefinitionArr = rigDefinitionComponentLookup,
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables.AsParallelWriter(),
newRemapTablesCounter = (int*)UnsafeUtility.AddressOf(ref newRemapTablesCounter),
};
var rv = fillRigToSkinBonesRemapTableCacheJob.ScheduleParallelByRef(increaseRigRemapTableCapacityJH);
return rv;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JobHandle UpdateRenderBounds(ref SystemState ss, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
{
var updateSkinnedMeshBoundsJob = new UpdateSkinnedMeshBoundsJob()
{
worldBonePoses = runtimeData.worldSpaceBonesBuffer,
rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true)
};
var updateSkinnedMeshBoundsJH = updateSkinnedMeshBoundsJob.ScheduleParallel(dependsOn);
return updateSkinnedMeshBoundsJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JobHandle PropagateAnimatedBonesToEntitiesTRS(ref SystemState ss, in RuntimeAnimationData runtimeData, EntityQuery eq, bool withParents, JobHandle dependsOn)
{
var propagateAnimationJob = new PropagateBoneTransformToEntityTRSJob()
{
boneTransforms = withParents ? runtimeData.animatedBonesBuffer : runtimeData.worldSpaceBonesBuffer,
postTransformMatrixLookup = SystemAPI.GetComponentLookup<PostTransformMatrix>(),
rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true),
gpuEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true)
};
var jh = propagateAnimationJob.ScheduleParallel(eq, dependsOn);
return jh;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JobHandle ApplySkinning(ref SystemState ss, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
{
var rigDefinitionComponentLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
var cullAnimationsTagComponentLookup = SystemAPI.GetComponentLookup<CullAnimationsTag>(true);
var localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
var rigBoneLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
var gpuAnimationEngineLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true);
var animationApplyJob = new ApplyAnimationToSkinnedMeshJob()
{
boneTransforms = runtimeData.worldSpaceBonesBuffer,
rigDefinitionLookup = rigDefinitionComponentLookup,
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables,
cullAnimationsTagLookup = cullAnimationsTagComponentLookup,
localTransformLookup = localTransformLookup,
parentLookup = parentLookup,
rigBoneLookup = rigBoneLookup,
gpuAnimationEngineTagLookup = gpuAnimationEngineLookup
};
var jh = animationApplyJob.ScheduleParallel(dependsOn);
return jh;
}
}
}