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

163 lines
7.4 KiB
C#

using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Rendering;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
[WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)]
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
[CreateAfter(typeof(RegisterMaterialsAndMeshesSystem))]
public partial struct SkinnedMeshPreparationSystem: ISystem
{
SharedComponentTypeHandle<RenderMeshArray> renderMeshArrayTypeHandle;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void OnCreate(ref SystemState ss)
{
#if !HYBRID_RENDERER_DISABLED
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
#endif
{
ss.Enabled = false;
return;
}
var deformationRuntimeData = DeformationRuntimeData.Construct(ref ss);
ss.EntityManager.CreateSingleton(deformationRuntimeData, "Rukhanka Deformation Runtime Data");
renderMeshArrayTypeHandle = ss.GetSharedComponentTypeHandle<RenderMeshArray>();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void OnDestroy(ref SystemState ss)
{
if (SystemAPI.TryGetSingletonRW<DeformationRuntimeData>(out var deformationRuntimeData))
deformationRuntimeData.ValueRW.Dispose();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[BurstCompile]
public void OnUpdate(ref SystemState ss)
{
renderMeshArrayTypeHandle.Update(ref ss);
ref var deformationRuntimeData = ref SystemAPI.GetSingletonRW<DeformationRuntimeData>().ValueRW;
// Reset data used in current frame
var resetFrameDataJH = ResetNewFrameData(ref ss, ref deformationRuntimeData, ss.Dependency);
// Gather meshes that new in this frame and absent in previous frames
var getNewSkinnedMeshesJH = GatherNewMeshes(ref ss, ref deformationRuntimeData, resetFrameDataJH);
// Register new meshes in internal skinned mesh database
var registerNewSkinnedMeshesJH = RegisterNewMeshes(ref ss, ref deformationRuntimeData, getNewSkinnedMeshesJH);
// Compute all frame deformed meshes skin matrices offsets in global GPU buffer
var computeFrameSkinMatrixDataJH = ComputeFrameSkinnedMeshData(ref ss, ref deformationRuntimeData, resetFrameDataJH);
var combinedJobHandle = JobHandle.CombineDependencies(computeFrameSkinMatrixDataJH, registerNewSkinnedMeshesJH);
ss.Dependency = combinedJobHandle;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle ResetNewFrameData(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
var frameSkinnedMeshesQuery = SystemAPI.QueryBuilder()
.WithAny<Rukhanka.SkinMatrix, Rukhanka.BlendShapeWeight>()
.Build();
var resetFrameDataJob = new ResetFrameDataJob()
{
frameSkinnedMeshesCount = frameSkinnedMeshesQuery.CalculateEntityCount(),
frameSkinMatrixCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameSkinMatrixCount)),
frameBlendShapeWeightCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameBlendShapeWeightsCount)),
newSkinnedMeshesToRegister = drd.newSkinnedMeshesToRegister,
entityToSMRFrameDataMap = drd.entityToSMRFrameDataMap,
frameDeformedVertexCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameDeformedVerticesCount)),
};
var resetFrameDataJH = resetFrameDataJob.Schedule(dependsOn);
return resetFrameDataJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JobHandle GatherNewMeshes(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
var deformedSubMeshQuery = SystemAPI.QueryBuilder()
.WithAll<MaterialMeshInfo, DeformedMeshIndex, SkinnedMeshRendererComponent>()
.Build();
var getNewSkinnedMeshesJob = new GetFrameNewSkinnedMeshesJob()
{
existingSkinnedMeshes = drd.registeredSkinnedMeshesMap,
newSkinnedMeshes = drd.newSkinnedMeshesToRegister.AsParallelWriter(),
renderMeshArrays = drd.renderMeshArrays,
materialMeshInfoTypeHandle = SystemAPI.GetComponentTypeHandle<MaterialMeshInfo>(true),
renderMeshArrayTypeHandle = renderMeshArrayTypeHandle,
animatedSkinnedMeshTypeHandle = SystemAPI.GetComponentTypeHandle<SkinnedMeshRendererComponent>(true),
};
var getNewSkinnedMeshesJH = getNewSkinnedMeshesJob.ScheduleParallel(deformedSubMeshQuery, dependsOn);
return getNewSkinnedMeshesJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle RegisterNewMeshes(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
var registerNewSkinnedMeshesJob = new RegisterNewSkinnedMeshesJob()
{
existingSkinnedMeshes = drd.registeredSkinnedMeshesMap,
newSkinnedMeshes = drd.newSkinnedMeshesToRegister,
totalSkinnedVerticesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.totalSkinnedVerticesCount)),
totalBoneWeightsCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.totalBoneWeightsCount)),
totalBlendShapeVerticesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.totalBlendShapeVerticesCount)),
maximumVerticesAcrossAllRegisteredMeshes = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.maximumVerticesAcrossAllRegisteredMeshes)),
maximumSkinMatrixCountAcrossAllRegisteredMeshes = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.maximumSkinMatrixCountAcrossAllRegisteredMeshes)),
};
var registerNewSkinnedMeshesJH = registerNewSkinnedMeshesJob.Schedule(dependsOn);
return registerNewSkinnedMeshesJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle ComputeFrameSkinnedMeshData(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
// If culling context is present then skin only meshes in visible LOD level
SystemAPI.TryGetSingleton<AnimationCullingContext>(out var animationCullingContext);
var computeFrameSkinnedMeshesJob = new ComputeFrameSkinnedMeshesJob()
{
skinMatrixOffsetCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameSkinMatrixCount)),
blendShapeWeightOffsetCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameBlendShapeWeightsCount)),
frameDeformedVerticesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameDeformedVerticesCount)),
cullAnimationsTagLookup = SystemAPI.GetComponentLookup<CullAnimationsTag>(true),
entityToSMRFrameData = drd.entityToSMRFrameDataMap.AsParallelWriter(),
skinMatrixBufferLookup = SystemAPI.GetBufferLookup<Rukhanka.SkinMatrix>(true),
blendShapeWeightBufferLookup = SystemAPI.GetBufferLookup<Rukhanka.BlendShapeWeight>(true),
lodRangeLookup = SystemAPI.GetComponentLookup<LODRange>(true),
lodWorldRefPointLookup = SystemAPI.GetComponentLookup<LODWorldReferencePoint>(true),
lodAffectors = animationCullingContext.lodAffectors,
// Disable culling in editor world
#if UNITY_EDITOR
isEditorWorld = ss.WorldUnmanaged.Flags == WorldFlags.Editor
#endif
};
dependsOn = computeFrameSkinnedMeshesJob.ScheduleParallel(dependsOn);
return dependsOn;
}
}
}