// Copyright Epic Games, Inc. All Rights Reserved. #include "/Engine/Private/Common.ush" #include "/Engine/Private/ComputeShaderUtils.ush" #include "/Engine/Private/GPUScene/GPUSceneWriter.ush" #include "NiagaraMeshParticleUtils.ush" Buffer SortedIndices; int SortedIndicesOffset; float3 SystemLWCTile; int bLocalSpace; float DeltaSeconds; uint FacingMode; int ScaleDataOffset; int RotationDataOffset; int PositionDataOffset; int VelocityDataOffset; int CameraOffsetDataOffset; int PrevScaleDataOffset; int PrevRotationDataOffset; int PrevPositionDataOffset; int PrevVelocityDataOffset; int PrevCameraOffsetDataOffset; float3 DefaultScale; float4 DefaultRotation; float3 DefaultPosition; float3 DefaultVelocity; float DefaultCameraOffset; float3 DefaultPrevScale; float4 DefaultPrevRotation; float3 DefaultPrevPosition; float3 DefaultPrevVelocity; float DefaultPrevCameraOffset; float3 MeshScale; float4 MeshRotation; int bMeshOffsetIsWorldSpace; float3 MeshOffset; uint bLockedAxisEnable; float3 LockedAxis; uint LockedAxisSpace; uint PrimitiveId; uint ParticleCount; Buffer GPUParticleCountBuffer; uint GPUParticleCountOffset; int MeshIndex; int MeshIndexDataOffset; int RendererVisibility; int VisibilityTagDataOffset; float3 LocalBoundingCenter; float2 DistanceCullRangeSquared; float4 LODScreenSize; int bNeedsPrevTransform; uint GetPayloadDataFlags() { uint Flags = INSTANCE_SCENE_DATA_FLAG_HAS_CUSTOM_DATA; if (bNeedsPrevTransform) { Flags |= INSTANCE_SCENE_DATA_FLAG_HAS_DYNAMIC_DATA; } return Flags; } void CullInstance(uint InstanceId) { // This will minimally initialize the instance data such that it will be ignored and culled WriteInstancePrimitiveIdAndFlags(InstanceId, PrimitiveId, INSTANCE_SCENE_DATA_FLAG_HIDDEN); } [numthreads(THREAD_GROUP_SIZE, 1, 1)] void UpdateMeshInstancesCS(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) { const uint DispatchId = GetUnWrappedDispatchThreadId(GroupId, GroupIndex, THREAD_GROUP_SIZE); // Retrieve the primitive data FPrimitiveSceneData PrimitiveData = GetPrimitiveData(PrimitiveId); const uint LocalInstanceId = DispatchId.x; const uint InstanceId = PrimitiveData.InstanceSceneDataOffset + LocalInstanceId; uint NumParticles = ParticleCount; if (LocalInstanceId >= NumParticles) { // Past the CPU particle count, bail and do nothing return; } if (GPUParticleCountOffset != (uint)-1) { NumParticles = GPUParticleCountBuffer[GPUParticleCountOffset]; if (LocalInstanceId >= NumParticles) { // Past the GPU particle count, flag the instance as culled CullInstance(InstanceId); return; } } uint ParticleIndex = LocalInstanceId; if (SortedIndicesOffset != -1) { ParticleIndex = SortedIndices[SortedIndicesOffset + LocalInstanceId]; } if (NiagaraSafeGetInt(MeshIndexDataOffset, ParticleIndex, 0) != MeshIndex || NiagaraSafeGetInt(VisibilityTagDataOffset, ParticleIndex, RendererVisibility) != RendererVisibility) { // Culled from either mesh index or visibility tag mismatch CullInstance(InstanceId); return; } NiagaraMeshParticleTransformsParams Params; Params.ParticleIndex = ParticleIndex; Params.SystemLWCTile = SystemLWCTile; Params.bLocalSpace = bLocalSpace; #if ENABLE_PRECISE_MOTION_VECTORS Params.bPreciseMotionVectors = true; #else Params.bPreciseMotionVectors = false; #endif Params.FacingMode = FacingMode; Params.DeltaSeconds = DeltaSeconds; Params.MeshScale = MeshScale; Params.MeshOffset = MeshOffset; Params.MeshRotation = MeshRotation; Params.bMeshOffsetIsWorldSpace = bMeshOffsetIsWorldSpace; Params.bLockedAxisEnable = bLockedAxisEnable; Params.LockedAxis = LockedAxis; Params.LockedAxisSpace = LockedAxisSpace; Params.ScaleDataOffset = ScaleDataOffset; Params.RotationDataOffset = RotationDataOffset; Params.PositionDataOffset = PositionDataOffset; Params.CameraOffsetDataOffset = CameraOffsetDataOffset; Params.PrevScaleDataOffset = PrevScaleDataOffset; Params.PrevRotationDataOffset = PrevRotationDataOffset; Params.PrevPositionDataOffset = PrevPositionDataOffset; Params.PrevCameraOffsetDataOffset = PrevCameraOffsetDataOffset; Params.DefaultScale = DefaultScale; Params.DefaultRotation = DefaultRotation; Params.DefaultPosition = DefaultPosition; Params.DefaultCameraOffset = DefaultCameraOffset; Params.DefaultPrevScale = DefaultPrevScale; Params.DefaultPrevRotation = DefaultPrevRotation; Params.DefaultPrevPosition = DefaultPrevPosition; Params.DefaultPrevCameraOffset = DefaultPrevCameraOffset; Params.VelocityDirMag = NiagaraGetVelocityDirMag(VelocityDataOffset, DefaultVelocity, ParticleIndex); Params.PrevVelocityDirMag = NiagaraGetVelocityDirMag(PrevVelocityDataOffset, DefaultPrevVelocity, ParticleIndex); Params.CameraOrigin = PrimaryView.TileOffset.WorldCameraOrigin; Params.CameraForwardDir = View.ViewForward; Params.CameraUpDir = View.ViewUp; Params.PrevCameraOrigin = PrimaryView.TileOffset.PrevWorldCameraOrigin; Params.PrevCameraForwardDir = View.PrevViewToTranslatedWorld[2].xyz; Params.PrevCameraUpDir = View.PrevViewToTranslatedWorld[1].xyz; Params.PrimitiveLocalToWorld = DFToTileOffset(PrimitiveData.LocalToWorld); Params.PrimitiveWorldToLocal = DFToTileOffset(PrimitiveData.WorldToLocal); Params.PrimitivePrevLocalToWorld = DFToTileOffset(PrimitiveData.PreviousLocalToWorld); Params.PrimitiveInvNonUniformScale = PrimitiveData.InvNonUniformScale; NiagaraMeshParticleTransforms Transforms = NiagaraCalculateMeshParticleTransforms(Params); // Transform the bounding center to world-space and distance cull { const FLWCVector3 BCenter = LWCMultiply(LocalBoundingCenter, Transforms.LocalToWorld); const float3 CamVec = LWCToFloat(LWCSubtract(BCenter, PrimaryView.TileOffset.WorldCameraOrigin)); const float CamDistSq = length2(CamVec); if (CamDistSq < DistanceCullRangeSquared.x || CamDistSq > DistanceCullRangeSquared.y) { // We are outside the distance ranges required CullInstance(InstanceId); return; } const float3 ParticleScale = NiagaraSafeGetVec3(Params.ScaleDataOffset, Params.ParticleIndex, Params.DefaultScale); const float3 FinalScale = ParticleScale * MeshScale * PrimitiveData.NonUniformScale.xyz; const float MaxScaleAxis = max3(abs(FinalScale.x), abs(FinalScale.y), abs(FinalScale.z)); const float ScaledCamDstSq = MaxScaleAxis > 0.0f ? CamDistSq / (MaxScaleAxis*MaxScaleAxis) : CamDistSq; const float ScreenRadius = saturate(LODScreenSize.z / max(1.0f, ScaledCamDstSq * LODScreenSize.w)); if ( ScreenRadius < LODScreenSize.x || ScreenRadius >= LODScreenSize.y ) { CullInstance(InstanceId); return; } } const uint PayloadDataFlags = GetPayloadDataFlags(); InitializeInstanceSceneDataWS( InstanceId, PrimitiveId, LocalInstanceId, PayloadDataFlags, 1, 0.0f, DFFromTileOffset(Transforms.LocalToWorld) ); FInstancePayloadDataOffsets PayloadOffsets = GetInstancePayloadDataOffsets(PrimitiveId, PayloadDataFlags, LocalInstanceId); WriteInstanceDynamicDataWS(PayloadOffsets, PrimitiveId, DFFromTileOffset(Transforms.PrevLocalToWorld)); WriteInstanceCustomData(PayloadOffsets, 0, float4(asfloat(ParticleIndex), 0.0f, 0.0f, 0.0f)); }