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;
}