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 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(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void OnDestroy(ref SystemState ss) { if (SystemAPI.TryGetSingletonRW(out var deformationRuntimeData)) deformationRuntimeData.ValueRW.Dispose(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [BurstCompile] public void OnUpdate(ref SystemState ss) { renderMeshArrayTypeHandle.Update(ref ss); ref var deformationRuntimeData = ref SystemAPI.GetSingletonRW().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() .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() .Build(); var getNewSkinnedMeshesJob = new GetFrameNewSkinnedMeshesJob() { existingSkinnedMeshes = drd.registeredSkinnedMeshesMap, newSkinnedMeshes = drd.newSkinnedMeshesToRegister.AsParallelWriter(), renderMeshArrays = drd.renderMeshArrays, materialMeshInfoTypeHandle = SystemAPI.GetComponentTypeHandle(true), renderMeshArrayTypeHandle = renderMeshArrayTypeHandle, animatedSkinnedMeshTypeHandle = SystemAPI.GetComponentTypeHandle(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(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(true), entityToSMRFrameData = drd.entityToSMRFrameDataMap.AsParallelWriter(), skinMatrixBufferLookup = SystemAPI.GetBufferLookup(true), blendShapeWeightBufferLookup = SystemAPI.GetBufferLookup(true), lodRangeLookup = SystemAPI.GetComponentLookup(true), lodWorldRefPointLookup = SystemAPI.GetComponentLookup(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; } } }