Skip to content

Instantly share code, notes, and snippets.

@alexpana
Created December 18, 2017 22:27
Show Gist options
  • Save alexpana/8c8a906ee072128a1cf5d088bcae98d7 to your computer and use it in GitHub Desktop.
Save alexpana/8c8a906ee072128a1cf5d088bcae98d7 to your computer and use it in GitHub Desktop.
float FParticleRibbonEmitterInstance::Spawn(float DeltaTime)
{
bool bProcessSpawnRate = Spawn_Source(DeltaTime);
if (bProcessSpawnRate == false)
{
return SpawnFraction;
}
UParticleLODLevel* LODLevel = SpriteTemplate->LODLevels[0];
check(LODLevel);
check(LODLevel->RequiredModule);
// Iterate over each trail
int32 TrailIdx = 0;
float MovementSpawnRate = 0.0f;
int32 MovementSpawnCount = 0;
float SpawnRate = 0.0f;
int32 SpawnCount = 0;
int32 BurstCount = 0;
float OldLeftover = SpawnFraction;
// For now, we are not supporting bursts on trails...
bool bProcessBurstList = false;
// Figure out spawn rate for this tick.
if (bProcessSpawnRate)
{
float RateScale = LODLevel->SpawnModule->RateScale.GetValue(EmitterTime, Component) * LODLevel->SpawnModule->GetGlobalRateScale();
float QualityMult = SpriteTemplate->GetQualityLevelSpawnRateMult();
SpawnRate += LODLevel->SpawnModule->Rate.GetValue(EmitterTime, Component) * FMath::Clamp<float>(QualityMult, 0.0f, 1.0);
}
// Take Bursts into account as well...
if (bProcessBurstList)
{
int32 Burst = 0;
float BurstTime = GetCurrentBurstRateOffset(DeltaTime, Burst);
BurstCount += Burst;
}
const int32 LocalMaxParticleInTrailCount = TrailTypeData->MaxParticleInTrailCount;
float SafetyLeftover = OldLeftover;
float NewLeftover = OldLeftover + DeltaTime * SpawnRate;
int32 SpawnNumber = FMath::FloorToInt(NewLeftover);
float SliceIncrement = (SpawnRate > 0.0f) ? (1.f / SpawnRate) : 0.0f;
float SpawnStartTime = DeltaTime + OldLeftover * SliceIncrement - SliceIncrement;
SpawnFraction = NewLeftover - SpawnNumber;
int32 TotalCount = MovementSpawnCount + SpawnNumber + BurstCount;
bool bNoLivingParticles = (ActiveParticles == 0);
//@todo. Don't allow more than TrailCount trails...
if (LocalMaxParticleInTrailCount > 0)
{
int32 KillCount = (TotalCount + ActiveParticles) - LocalMaxParticleInTrailCount;
if (KillCount > 0)
{
KillParticles(TrailIdx, KillCount);
}
// Don't allow the spawning of more particles than allowed...
TotalCount = FMath::Max<int32>(TotalCount,LocalMaxParticleInTrailCount);
}
// Handle growing arrays.
bool bProcessSpawn = true;
int32 NewCount = ActiveParticles + TotalCount;
if (NewCount >= MaxActiveParticles)
{
if (DeltaTime < 0.25f)
{
bProcessSpawn = Resize(NewCount + FMath::TruncToInt(FMath::Sqrt((float)NewCount)) + 1);
}
else
{
bProcessSpawn = Resize((NewCount + FMath::TruncToInt(FMath::Sqrt((float)NewCount)) + 1), false);
}
}
if (bProcessSpawn == false)
{
return SafetyLeftover;
}
// Find the start particle of the current trail...
FBaseParticle* StartParticle = NULL;
int32 StartIndex = -1;
FRibbonTypeDataPayload* StartTrailData = NULL;
GetTrailStart<FRibbonTypeDataPayload>(TrailIdx, StartIndex, StartTrailData, StartParticle);
bNoLivingParticles = (StartParticle == NULL);
bool bTilingTrail = !FMath::IsNearlyZero(TrailTypeData->TilingDistance);
FParticleEventInstancePayload* EventPayload = NULL;
if (LODLevel->EventGenerator)
{
EventPayload = (FParticleEventInstancePayload*)GetModuleInstanceData(LODLevel->EventGenerator);
if (EventPayload && !EventPayload->bSpawnEventsPresent && !EventPayload->bBurstEventsPresent)
{
EventPayload = NULL;
}
}
float ElapsedTime = RunningTime;//SecondsSinceCreation;
// Do we have SpawnRate driven spawning?
if ((SpawnRate > 0.0f) && (SpawnNumber > 0))
{
float Increment = (SpawnRate > 0.0f) ? (1.f / SpawnRate) : 0.0f;
float StartTime = DeltaTime + OldLeftover * Increment - Increment;
// Spawn particles.
// NOTE: SpawnRate assumes that the ParticleSystemComponent is the 'source'
FVector CurrentUp;
if (TrailTypeData->RenderAxis == Trails_SourceUp)
{
CurrentUp = Component->GetComponentTransform().GetScaledAxis( EAxis::Z );
}
else
{
CurrentUp = FVector(0.0f, 0.0f, 1.0f);
}
float InvCount = 1.0f / SpawnNumber;
for (int32 SpawnIdx = 0; SpawnIdx < SpawnNumber; SpawnIdx++)
{
check(ActiveParticles <= MaxActiveParticles);
int32 ParticleIndex = ParticleIndices[ActiveParticles];
DECLARE_PARTICLE_PTR(Particle, ParticleData + ParticleStride * ParticleIndex);
FRibbonTypeDataPayload* TrailData = ((FRibbonTypeDataPayload*)((uint8*)Particle + TypeDataOffset));
float SpawnTime = StartTime - SpawnIdx * Increment;
float TimeStep = FMath::Clamp<float>(InvCount * (SpawnIdx + 1), 0.0f, 1.0f);
float StoredSpawnTime = DeltaTime * TimeStep;
PreSpawn(Particle, Location, FVector::ZeroVector);
SetDeadIndex(TrailData->TrailIndex, ParticleIndex);
if (LODLevel->TypeDataModule)
{
UParticleModuleTypeDataBase* pkBase = Cast<UParticleModuleTypeDataBase>(LODLevel->TypeDataModule);
pkBase->Spawn(this, TypeDataOffset, SpawnTime, Particle);
}
for (int32 ModuleIndex = 0; ModuleIndex < LODLevel->SpawnModules.Num(); ModuleIndex++)
{
UParticleModule* SpawnModule = LODLevel->SpawnModules[ModuleIndex];
if (SpawnModule->bEnabled)
{
UParticleModule* OffsetModule = LODLevel->SpawnModules[ModuleIndex];
SpawnModule->Spawn(this, GetModuleDataOffset(OffsetModule), SpawnTime, Particle);
}
}
PostSpawn(Particle, 1.f - float(SpawnIdx + 1) / float(SpawnNumber), SpawnTime);
GetParticleLifetimeAndSize(TrailIdx, Particle, bNoLivingParticles, Particle->OneOverMaxLifetime, Particle->Size.X);
Particle->RelativeTime = SpawnTime * Particle->OneOverMaxLifetime;
Particle->Size.Y = Particle->Size.X;
Particle->Size.Z = Particle->Size.Z;
Particle->BaseSize = Particle->Size;
if (EventPayload)
{
LODLevel->EventGenerator->HandleParticleSpawned(this, EventPayload, Particle);
}
// Trail specific...
// Clear the next and previous - just to be safe
TrailData->Flags = TRAIL_EMITTER_SET_NEXT(TrailData->Flags, TRAIL_EMITTER_NULL_NEXT);
TrailData->Flags = TRAIL_EMITTER_SET_PREV(TrailData->Flags, TRAIL_EMITTER_NULL_PREV);
// Set the trail-specific data on this particle
TrailData->TrailIndex = TrailIdx;
TrailData->Tangent = -Particle->Velocity * DeltaTime;
TrailData->SpawnTime = ElapsedTime + StoredSpawnTime;
TrailData->SpawnDelta = SpawnIdx * Increment;
// Set the location and up vectors
TrailData->Up = CurrentUp;
TrailData->bMovementSpawned = false;
// If this is the true spawned particle, store off the spawn interpolated count
TrailData->bInterpolatedSpawn = false;
TrailData->SpawnedTessellationPoints = 1;
bool bAddedParticle = false;
// Determine which trail to attach to
if (bNoLivingParticles)
{
// These are the first particles!
// Tag it as the 'only'
TrailData->Flags = TRAIL_EMITTER_SET_ONLY(TrailData->Flags);
TiledUDistanceTraveled[TrailIdx] = 0.0f;
TrailData->TiledU = 0.0f;
bNoLivingParticles = false;
bAddedParticle = true;
SetStartIndex(TrailData->TrailIndex, ParticleIndex);
}
else if (StartParticle)
{
bAddedParticle = AddParticleHelper(TrailIdx,
StartIndex, (FTrailsBaseTypeDataPayload*)StartTrailData,
ParticleIndex, (FTrailsBaseTypeDataPayload*)TrailData
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
, Component
#endif
);
}
if (bAddedParticle)
{
if (bTilingTrail == true)
{
if (StartParticle == NULL)
{
TrailData->TiledU = 0.0f;
}
else
{
FVector PositionDelta = Particle->Location - StartParticle->Location;
TiledUDistanceTraveled[TrailIdx] += PositionDelta.Size();
TrailData->TiledU = TiledUDistanceTraveled[TrailIdx] / TrailTypeData->TilingDistance;
//@todo. Is there going to be a problem when distance gets REALLY high?
}
}
StartParticle = Particle;
StartIndex = ParticleIndex;
StartTrailData = TrailData;
ActiveParticles++;
if (StartTrailData->Tangent.IsNearlyZero())
{
FBaseParticle* NextSpawnedParticle = NULL;
FRibbonTypeDataPayload* NextSpawnedTrailData = NULL;
FTrailsBaseTypeDataPayload* TempPayload = NULL;
GetParticleInTrail(true, StartParticle, StartTrailData, GET_Next, GET_Spawned, NextSpawnedParticle, TempPayload);
NextSpawnedTrailData = (FRibbonTypeDataPayload*)(TempPayload);
if (NextSpawnedParticle != NULL)
{
FVector PositionDelta = (StartParticle->Location - NextSpawnedParticle->Location);
float TimeDelta = StartTrailData->SpawnTime - NextSpawnedTrailData->SpawnTime;
StartTrailData->Tangent = PositionDelta / TimeDelta;
}
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if ((int32)ActiveParticles > LocalMaxParticleInTrailCount)
{
if (Component && Component->GetWorld())
{
FString ErrorMessage =
FString::Printf(TEXT("Ribbon with too many particles: %5d vs. %5d, %s"),
ActiveParticles, LocalMaxParticleInTrailCount,
Component->Template ? *Component->Template->GetName() : TEXT("No template"));
FColor ErrorColor(255,0,0);
GEngine->AddOnScreenDebugMessage((uint64)((PTRINT)this), 5.0f, ErrorColor,ErrorMessage);
UE_LOG(LogParticles, Log, TEXT("%s"), *ErrorMessage);
}
}
#endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
INC_DWORD_STAT(STAT_TrailParticlesSpawned);
if ((TrailTypeData->bEnablePreviousTangentRecalculation == true)
&& (TrailTypeData->bTangentRecalculationEveryFrame == false))
{
// Find the 2 next SPAWNED particles in the trail (not interpolated).
// If there are 2, then the one about to be spawned will make a chain of 3
// giving us data to better calculate the middle ones tangent.
// After doing so, we must also recalculate the tangents of the interpolated
// particles in the chain.
// The most recent spawned particle in the trail...
FBaseParticle* NextSpawnedParticle = NULL;
FRibbonTypeDataPayload* NextSpawnedTrailData = NULL;
// The second most recent spawned particle in the trail...
FBaseParticle* NextNextSpawnedParticle = NULL;
FRibbonTypeDataPayload* NextNextSpawnedTrailData = NULL;
FTrailsBaseTypeDataPayload* TempPayload = NULL;
// Grab the latest two spawned particles in the trail
GetParticleInTrail(true, StartParticle, StartTrailData, GET_Next, GET_Spawned, NextSpawnedParticle, TempPayload);
NextSpawnedTrailData = (FRibbonTypeDataPayload*)(TempPayload);
GetParticleInTrail(true, NextSpawnedParticle, NextSpawnedTrailData, GET_Next, GET_Spawned, NextNextSpawnedParticle, TempPayload);
NextNextSpawnedTrailData = (FRibbonTypeDataPayload*)(TempPayload);
if (NextSpawnedParticle != NULL)
{
FVector NewTangent;
if (NextNextSpawnedParticle != NULL)
{
// Calculate the new tangent from the previous and next position...
// LastSourcePosition will be that position of the first particle that will be spawned this frame
FVector PositionDelta = (StartParticle->Location - NextNextSpawnedParticle->Location);
float TimeDelta = StartTrailData->SpawnTime - NextNextSpawnedTrailData->SpawnTime;
NewTangent = PositionDelta / TimeDelta;
NextSpawnedTrailData->Tangent = NewTangent;
}
else //if (NextNextSpawnedParticle == NULL)
{
// This is the second spawned particle in a trail...
// Calculate the new tangent from the previous and next position...
// LastSourcePosition will be that position of the first particle that will be spawned this frame
FVector PositionDelta = (StartParticle->Location - NextSpawnedParticle->Location);
float TimeDelta = StartTrailData->SpawnTime - NextSpawnedTrailData->SpawnTime;
NewTangent = PositionDelta / TimeDelta;
NextSpawnedTrailData->Tangent = NewTangent;
}
}
}
TrailSpawnTimes[0] = TrailData->SpawnTime;
}
else
{
check(TEXT("Failed to add particle to trail!!!!"));
}
INC_DWORD_STAT_BY(STAT_TrailParticles, ActiveParticles);
INC_DWORD_STAT(STAT_SpriteParticlesSpawned);
}
}
//TickCount++;
return SpawnFraction;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment