void FNiagaraSimulation::Tick(float DeltaSeconds) { SCOPE_CYCLE_COUNTER(STAT_NiagaraTick); SimpleTimer TickTime; UNiagaraEmitterProperties* PinnedProps = Props.Get(); if (!PinnedProps || !bIsEnabled || TickState == NTS_Suspended || TickState == NTS_Dead) { return; } Age += DeltaSeconds; check(Data.GetNumVariables() > 0); check(PinnedProps->SpawnScriptProps.Script); check(PinnedProps->UpdateScriptProps.Script); TickEvents(DeltaSeconds); // Figure out how many we will spawn. int32 OrigNumParticles = Data.GetNumInstances(); int32 NumToSpawn = CalcNumToSpawn(DeltaSeconds); int32 MaxNewParticles = OrigNumParticles + NumToSpawn; Data.Allocate(MaxNewParticles); ExternalConstants.SetOrAdd(BUILTIN_CONST_EMITTERAGE, FVector4(Age, Age, Age, Age)); ExternalConstants.SetOrAdd(BUILTIN_CONST_DELTATIME, FVector4(DeltaSeconds, DeltaSeconds, DeltaSeconds, DeltaSeconds)); // Simulate particles forward by DeltaSeconds. if (TickState==NTS_Running || TickState==NTS_Dieing) { SCOPE_CYCLE_COUNTER(STAT_NiagaraSimulate); RunVMScript(PinnedProps->UpdateScriptProps, EUnusedAttributeBehaviour::PassThrough); } //Init new particles with the spawn script. if (TickState==NTS_Running) { SCOPE_CYCLE_COUNTER(STAT_NiagaraSpawn); Data.SetNumInstances(MaxNewParticles); //For now, zero any unused attributes here. But as this is really uninitialized data we should maybe make this a more serious error. RunVMScript(PinnedProps->SpawnScriptProps, EUnusedAttributeBehaviour::Zero, OrigNumParticles, NumToSpawn); if (bGenerateSpawnEvents) { SpawnEventGenerator.OnSpawned(OrigNumParticles, NumToSpawn); } } CPUTimeMS = TickTime.GetElapsedMilliseconds(); INC_DWORD_STAT_BY(STAT_NiagaraNumParticles, Data.GetNumInstances()); }
/** Update render data buffer from attributes */ FNiagaraDynamicDataBase *NiagaraEffectRendererSprites::GenerateVertexData(const FNiagaraEmitterParticleData &Data) { SCOPE_CYCLE_COUNTER(STAT_NiagaraGenSpriteVertexData); SimpleTimer VertexDataTimer; FNiagaraDynamicDataSprites *DynamicData = new FNiagaraDynamicDataSprites; TArray<FParticleSpriteVertex>& RenderData = DynamicData->VertexData; RenderData.Reset(Data.GetNumParticles()); //CachedBounds.Init(); const FVector4 *PosPtr = Data.GetAttributeData("Position"); const FVector4 *ColPtr = Data.GetAttributeData("Color"); const FVector4 *AgePtr = Data.GetAttributeData("Age"); const FVector4 *RotPtr = Data.GetAttributeData("Rotation"); uint32 NumSubImages = 1; if (Properties) { NumSubImages = Properties->SubImageInfo.X*Properties->SubImageInfo.Y; } float ParticleId = 0.0f, IdInc = 1.0f / Data.GetNumParticles(); RenderData.AddUninitialized(Data.GetNumParticles()); for (uint32 ParticleIndex = 0; ParticleIndex < Data.GetNumParticles(); ParticleIndex++) { FParticleSpriteVertex& NewVertex = RenderData[ParticleIndex]; NewVertex.Position = PosPtr[ParticleIndex]; NewVertex.OldPosition = NewVertex.Position; NewVertex.Color = FLinearColor(ColPtr[ParticleIndex]); NewVertex.ParticleId = ParticleId; ParticleId += IdInc; NewVertex.RelativeTime = AgePtr[ParticleIndex].X; NewVertex.Size = FVector2D(RotPtr[ParticleIndex].Y, RotPtr[ParticleIndex].Z); NewVertex.Rotation = RotPtr[ParticleIndex].X; NewVertex.SubImageIndex = RotPtr[ParticleIndex].W * NumSubImages; FPlatformMisc::Prefetch(PosPtr + ParticleIndex+1); FPlatformMisc::Prefetch(RotPtr + ParticleIndex + 1); FPlatformMisc::Prefetch(ColPtr + ParticleIndex + 1); FPlatformMisc::Prefetch(AgePtr + ParticleIndex + 1); //CachedBounds += NewVertex.Position; } //CachedBounds.ExpandBy(MaxSize); CPUTimeMS = VertexDataTimer.GetElapsedMilliseconds(); return DynamicData; }
void FNiagaraSimulation::Tick(float DeltaSeconds) { SCOPE_CYCLE_COUNTER(STAT_NiagaraTick); UNiagaraEmitterProperties* PinnedProps = Props.Get(); if (!PinnedProps || !bIsEnabled || TickState==NTS_Suspended || TickState==NTS_Dead) return; SimpleTimer TickTime; check(Data.GetNumVariables() > 0); check(PinnedProps->SpawnScriptProps.Script); check(PinnedProps->UpdateScriptProps.Script); //Handle Event Actions. auto CallEventActions = [&](FNiagaraEventReceiverProperties& Receiver) { for (auto& Action : Receiver.EmitterActions) { if (Action) Action->PerformAction(*this, Receiver); } }; for (auto& Receiver : Props->SpawnScriptProps.EventReceivers) { CallEventActions(Receiver); } for (auto& Receiver : Props->UpdateScriptProps.EventReceivers) { CallEventActions(Receiver); } int32 OrigNumParticles = Data.GetNumInstances(); int32 NumToSpawn = 0; // Figure out how many we will spawn. NumToSpawn = CalcNumToSpawn(DeltaSeconds); int32 MaxNewParticles = OrigNumParticles + NumToSpawn; Data.Allocate(MaxNewParticles); Age += DeltaSeconds; Constants.SetOrAdd(TEXT("Emitter Age"), FVector4(Age, Age, Age, Age)); Constants.SetOrAdd(TEXT("Delta Time"), FVector4(DeltaSeconds, DeltaSeconds, DeltaSeconds, DeltaSeconds)); // Simulate particles forward by DeltaSeconds. if (TickState==NTS_Running || TickState==NTS_Dieing) { SCOPE_CYCLE_COUNTER(STAT_NiagaraSimulate); RunVMScript(PinnedProps->UpdateScriptProps, EUnusedAttributeBehaviour::Copy); } DebuggerHook_PreSpawn(this, OrigNumParticles, NumToSpawn); //Init new particles with the spawn script. if (TickState==NTS_Running) { SCOPE_CYCLE_COUNTER(STAT_NiagaraSpawn); Data.SetNumInstances(MaxNewParticles); //For now, zero any unused attributes here. But as this is really uninitialized data we should maybe make this a more serious error. RunVMScript(PinnedProps->SpawnScriptProps, EUnusedAttributeBehaviour::Zero, OrigNumParticles, NumToSpawn); } if (bGenerateSpawnEvents) { SpawnEventGenerator.OnSpawned(OrigNumParticles, NumToSpawn); } CPUTimeMS = TickTime.GetElapsedMilliseconds(); INC_DWORD_STAT_BY(STAT_NiagaraNumParticles, Data.GetNumInstances()); }
void FNiagaraSimulation::Tick(float DeltaSeconds) { SCOPE_CYCLE_COUNTER(STAT_NiagaraTick); if (!bIsEnabled || TickState==NTS_Suspended || TickState==NTS_Dead) return; SimpleTimer TickTime; check(Data.GetNumAttributes() > 0); check(Props->SpawnScript); check(Props->UpdateScript); // Cache the ComponentToWorld transform. // CachedComponentToWorld = Component.GetComponentToWorld(); Data.SwapBuffers(); Data.SetNumParticles(Data.GetPrevNumParticles()); int32 OrigNumParticles = Data.GetNumParticles(); int32 NumToSpawn = 0; // Figure out how many we will spawn. NumToSpawn = CalcNumToSpawn(DeltaSeconds); int32 MaxNewParticles = OrigNumParticles + NumToSpawn; Data.Allocate(MaxNewParticles); Age += DeltaSeconds; Constants.SetOrAdd(TEXT("Emitter Age"), FVector4(Age, Age, Age, Age)); Constants.SetOrAdd(TEXT("Delta Time"), FVector4(DeltaSeconds, DeltaSeconds, DeltaSeconds, DeltaSeconds)); // Simulate particles forward by DeltaSeconds. if (TickState==NTS_Running || TickState==NTS_Dieing) { SCOPE_CYCLE_COUNTER(STAT_NiagaraSimulate); RunVMScript(Props->UpdateScript, EUnusedAttributeBehaviour::Copy); } //Init new particles with the spawn script. if (TickState==NTS_Running) { SCOPE_CYCLE_COUNTER(STAT_NiagaraSpawn); Data.SetNumParticles(MaxNewParticles); //For now, zero any unused attributes here. But as this is really uninitialized data we should maybe make this a more serious error. RunVMScript(Props->SpawnScript, EUnusedAttributeBehaviour::Zero, OrigNumParticles, NumToSpawn); } // Iterate over looking for dead particles and move from the end of the list to the dead location, compacting in the process { SCOPE_CYCLE_COUNTER(STAT_NiagaraKill); int32 CurNumParticles = OrigNumParticles = Data.GetNumParticles(); int32 ParticleIndex = 0; const FVector4* ParticleRelativeTimes = Data.GetAttributeData(FNiagaraVariableInfo(FName(TEXT("Age")), ENiagaraDataType::Vector)); if (ParticleRelativeTimes) { while (ParticleIndex < OrigNumParticles) { if (ParticleRelativeTimes[ParticleIndex].X > 1.0f) { // Particle is dead, move one from the end here. MoveParticleToIndex(--CurNumParticles, ParticleIndex); } ParticleIndex++; } } Data.SetNumParticles(CurNumParticles); // check if the emitter has officially died if (GetTickState() == NTS_Dieing && CurNumParticles == 0) { SetTickState(NTS_Dead); } } CPUTimeMS = TickTime.GetElapsedMilliseconds(); DECLARE_DWORD_COUNTER_STAT(TEXT("NumParticles"), STAT_NiagaraNumParticles, STATGROUP_Niagara); INC_DWORD_STAT_BY(STAT_NiagaraNumParticles, Data.GetNumParticles()); }
void NiagaraEffectRendererSprites::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector, const FNiagaraSceneProxy *SceneProxy) const { SCOPE_CYCLE_COUNTER(STAT_NiagaraRender); SCOPE_CYCLE_COUNTER(STAT_NiagaraRenderSprites); SimpleTimer MeshElementsTimer; check(DynamicDataRender) if (DynamicDataRender->VertexData.Num() == 0) { return; } const bool bIsWireframe = ViewFamily.EngineShowFlags.Wireframe; FMaterialRenderProxy* MaterialRenderProxy = Material->GetRenderProxy(SceneProxy->IsSelected(), SceneProxy->IsHovered()); int32 SizeInBytes = DynamicDataRender->VertexData.GetTypeSize() * DynamicDataRender->VertexData.Num(); FGlobalDynamicVertexBuffer::FAllocation LocalDynamicVertexAllocation = FGlobalDynamicVertexBuffer::Get().Allocate(SizeInBytes); if (LocalDynamicVertexAllocation.IsValid()) { // Update the primitive uniform buffer if needed. if (!WorldSpacePrimitiveUniformBuffer.IsInitialized()) { FPrimitiveUniformShaderParameters PrimitiveUniformShaderParameters = GetPrimitiveUniformShaderParameters( FMatrix::Identity, SceneProxy->GetActorPosition(), SceneProxy->GetBounds(), SceneProxy->GetLocalBounds(), SceneProxy->ReceivesDecals(), false, SceneProxy->UseEditorDepthTest() ); WorldSpacePrimitiveUniformBuffer.SetContents(PrimitiveUniformShaderParameters); WorldSpacePrimitiveUniformBuffer.InitResource(); } // Copy the vertex data over. FMemory::Memcpy(LocalDynamicVertexAllocation.Buffer, DynamicDataRender->VertexData.GetData(), SizeInBytes); // Compute the per-view uniform buffers. for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; FNiagaraMeshCollectorResourcesSprite& CollectorResources = Collector.AllocateOneFrameResource<FNiagaraMeshCollectorResourcesSprite>(); FParticleSpriteUniformParameters PerViewUniformParameters;// = UniformParameters; PerViewUniformParameters.AxisLockRight = FVector4(0.0f, 0.0f, 0.0f, 0.0f); PerViewUniformParameters.AxisLockUp = FVector4(0.0f, 0.0f, 0.0f, 0.0f); PerViewUniformParameters.RotationScale = 1.0f; PerViewUniformParameters.RotationBias = 0.0f; PerViewUniformParameters.TangentSelector = FVector4(0.0f, 0.0f, 0.0f, 1.0f); PerViewUniformParameters.InvDeltaSeconds = 0.0f; if (Properties) { PerViewUniformParameters.SubImageSize = FVector4(Properties->SubImageInfo.X, Properties->SubImageInfo.Y, 1.0f / Properties->SubImageInfo.X, 1.0f / Properties->SubImageInfo.Y); } PerViewUniformParameters.NormalsType = 0; PerViewUniformParameters.NormalsSphereCenter = FVector4(0.0f, 0.0f, 0.0f, 1.0f); PerViewUniformParameters.NormalsCylinderUnitDirection = FVector4(0.0f, 0.0f, 1.0f, 0.0f); PerViewUniformParameters.PivotOffset = FVector2D(-0.5f, -0.5f); PerViewUniformParameters.MacroUVParameters = FVector4(0.0f, 0.0f, 1.0f, 1.0f); // Collector.AllocateOneFrameResource uses default ctor, initialize the vertex factory CollectorResources.VertexFactory.SetFeatureLevel(ViewFamily.GetFeatureLevel()); CollectorResources.VertexFactory.SetParticleFactoryType(PVFT_Sprite); PerViewUniformParameters.MacroUVParameters = FVector4(0.0f, 0.0f, 1.0f, 1.0f); CollectorResources.UniformBuffer = FParticleSpriteUniformBufferRef::CreateUniformBufferImmediate(PerViewUniformParameters, UniformBuffer_SingleFrame); CollectorResources.VertexFactory.InitResource(); CollectorResources.VertexFactory.SetSpriteUniformBuffer(CollectorResources.UniformBuffer); CollectorResources.VertexFactory.SetInstanceBuffer( LocalDynamicVertexAllocation.VertexBuffer, LocalDynamicVertexAllocation.VertexOffset, sizeof(FParticleSpriteVertex), true ); CollectorResources.VertexFactory.SetDynamicParameterBuffer(NULL, 0, 0, true); FMeshBatch& MeshBatch = Collector.AllocateMesh(); MeshBatch.VertexFactory = &CollectorResources.VertexFactory; MeshBatch.CastShadow = SceneProxy->CastsDynamicShadow(); MeshBatch.bUseAsOccluder = false; MeshBatch.ReverseCulling = SceneProxy->IsLocalToWorldDeterminantNegative(); MeshBatch.Type = PT_TriangleList; MeshBatch.DepthPriorityGroup = SceneProxy->GetDepthPriorityGroup(View); MeshBatch.bCanApplyViewModeOverrides = true; MeshBatch.bUseWireframeSelectionColoring = SceneProxy->IsSelected(); if (bIsWireframe) { MeshBatch.MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(SceneProxy->IsSelected(), SceneProxy->IsHovered()); } else { MeshBatch.MaterialRenderProxy = MaterialRenderProxy; } FMeshBatchElement& MeshElement = MeshBatch.Elements[0]; MeshElement.IndexBuffer = &GParticleIndexBuffer; MeshElement.FirstIndex = 0; MeshElement.NumPrimitives = 2; MeshElement.NumInstances = DynamicDataRender->VertexData.Num(); MeshElement.MinVertexIndex = 0; MeshElement.MaxVertexIndex = MeshElement.NumInstances * 4 - 1; MeshElement.PrimitiveUniformBufferResource = &WorldSpacePrimitiveUniformBuffer; Collector.AddMesh(ViewIndex, MeshBatch); } } } CPUTimeMS += MeshElementsTimer.GetElapsedMilliseconds(); }
FNiagaraDynamicDataBase *NiagaraEffectRendererRibbon::GenerateVertexData(const FNiagaraEmitterParticleData &Data) { SCOPE_CYCLE_COUNTER(STAT_NiagaraGenRibbonVertexData); SimpleTimer VertexDataTimer; FNiagaraDynamicDataRibbon *DynamicData = new FNiagaraDynamicDataRibbon; TArray<FParticleBeamTrailVertex>& RenderData = DynamicData->VertexData; RenderData.Reset(Data.GetNumParticles() * 2); //CachedBounds.Init(); // build a sorted list by age, so we always get particles in order // regardless of them being moved around due to dieing and spawning TArray<int32> SortedIndices; for (uint32 Idx = 0; Idx < Data.GetNumParticles(); Idx++) { SortedIndices.Add(Idx); } const FVector4 *AgeData = Data.GetAttributeData("Age"); SortedIndices.Sort( [&AgeData](const int32& A, const int32& B) { return AgeData[A].X < AgeData[B].X; } ); FVector2D UVs[4] = { FVector2D(0.0f, 0.0f), FVector2D(1.0f, 0.0f), FVector2D(1.0f, 1.0f), FVector2D(0.0f, 1.0f) }; const FVector4 *PosPtr = Data.GetAttributeData("Position"); const FVector4 *ColorPtr = Data.GetAttributeData("Color"); const FVector4 *AgePtr = Data.GetAttributeData("Age"); const FVector4 *RotPtr = Data.GetAttributeData("Rotation"); FVector PrevPos, PrevPos2, PrevDir(0.0f, 0.0f, 0.1f); for (int32 i = 0; i < SortedIndices.Num() - 1; i++) { uint32 Index1 = SortedIndices[i]; uint32 Index2 = SortedIndices[i + 1]; const FVector ParticlePos = PosPtr[Index1]; FVector ParticleDir = PosPtr[Index2] - ParticlePos; if (ParticleDir.Size() <= SMALL_NUMBER) { ParticleDir = PrevDir*0.1f; } FVector NormDir = ParticleDir.GetSafeNormal(); FVector ParticleRight = FVector::CrossProduct(NormDir, FVector(0.0f, 0.0f, 1.0f)); ParticleRight *= RotPtr[Index1].Y; FVector ParticleRightRot = ParticleRight.RotateAngleAxis(RotPtr[Index1].X, NormDir); if (i == 0) { AddRibbonVert(RenderData, ParticlePos + ParticleRightRot, Data, UVs[0], ColorPtr[Index1], AgePtr[Index1], RotPtr[i]); AddRibbonVert(RenderData, ParticlePos - ParticleRightRot, Data, UVs[1], ColorPtr[Index1], AgePtr[Index1], RotPtr[i]); } else { AddRibbonVert(RenderData, PrevPos2, Data, UVs[0], ColorPtr[Index1], AgePtr[Index1], RotPtr[i]); AddRibbonVert(RenderData, PrevPos, Data, UVs[1], ColorPtr[Index1], AgePtr[Index1], RotPtr[i]); } ParticleRightRot = ParticleRight.RotateAngleAxis(RotPtr[Index2].X, NormDir); AddRibbonVert(RenderData, ParticlePos - ParticleRightRot + ParticleDir, Data, UVs[2], ColorPtr[Index2], AgePtr[Index2], RotPtr[i]); AddRibbonVert(RenderData, ParticlePos + ParticleRightRot + ParticleDir, Data, UVs[3], ColorPtr[Index2], AgePtr[Index2], RotPtr[i]); PrevPos = ParticlePos - ParticleRightRot + ParticleDir; PrevPos2 = ParticlePos + ParticleRightRot + ParticleDir; PrevDir = ParticleDir; } CPUTimeMS = VertexDataTimer.GetElapsedMilliseconds(); return DynamicData; }