I’m working on some new Cascade modules, and taking a look at how the current ones are setup in engine code. Unfortunately the documentation/comments on it are pretty sparse or non-existent so it’s making it incredibly difficult to learn WHY things are done.
So, taking the ‘Size By Life’ module as an example, anyone able to explain what most of this is doing? I can get my head around the basics, but anything beyond that and I’m clueless. There really should be more commenting in the code
/*-----------------------------------------------------------------------------
UParticleModuleSizeMultiplyLife implementation.
-----------------------------------------------------------------------------*/
UParticleModuleSizeMultiplyLife::UParticleModuleSizeMultiplyLife(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
bSpawnModule = true;
bUpdateModule = true;
MultiplyX = true;
MultiplyY = true;
MultiplyZ = true;
}
void UParticleModuleSizeMultiplyLife::InitializeDefaults()
{
if (!LifeMultiplier.Distribution)
{
LifeMultiplier.Distribution = NewNamedObject<UDistributionVectorConstant>(this, TEXT("DistributionLifeMultiplier"));
}
}
void UParticleModuleSizeMultiplyLife::PostInitProperties()
{
Super::PostInitProperties();
if (!HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
{
InitializeDefaults();
}
}
void UParticleModuleSizeMultiplyLife::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_MOVE_DISTRIBUITONS_TO_POSTINITPROPS)
{
FDistributionHelpers::RestoreDefaultConstant(LifeMultiplier.Distribution, TEXT("DistributionLifeMultiplier"), FVector::ZeroVector);
}
}
void UParticleModuleSizeMultiplyLife::CompileModule( FParticleEmitterBuildInfo& EmitterInfo )
{
FVector AxisScaleMask(
MultiplyX ? 1.0f : 0.0f,
MultiplyY ? 1.0f : 0.0f,
MultiplyZ ? 1.0f : 0.0f
);
FVector AxisKeepMask(
1.0f - AxisScaleMask.X,
1.0f - AxisScaleMask.Y,
1.0f - AxisScaleMask.Z
);
EmitterInfo.SizeScale.Initialize( LifeMultiplier.Distribution );
EmitterInfo.SizeScale.ScaleByConstantVector( AxisScaleMask );
EmitterInfo.SizeScale.AddConstantVector( AxisKeepMask );
}
#if WITH_EDITOR
void UParticleModuleSizeMultiplyLife::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
InitializeDefaults();
Super::PostEditChangeProperty(PropertyChangedEvent);
}
bool UParticleModuleSizeMultiplyLife::IsValidForLODLevel(UParticleLODLevel* LODLevel, FString& OutErrorString)
{
if (LODLevel->TypeDataModule && LODLevel->TypeDataModule->IsA(UParticleModuleTypeDataGpu::StaticClass()))
{
if(!IsDistributionAllowedOnGPU(LifeMultiplier.Distribution))
{
OutErrorString = GetDistributionNotAllowedOnGPUText(StaticClass()->GetName(), "LifeMultiplier" ).ToString();
return false;
}
}
return true;
}
#endif // WITH_EDITOR
void UParticleModuleSizeMultiplyLife::Spawn(FParticleEmitterInstance* Owner, int32 Offset, float SpawnTime, FBaseParticle* ParticleBase)
{
SPAWN_INIT;
FVector SizeScale = LifeMultiplier.GetValue(Particle.RelativeTime, Owner->Component);
if(MultiplyX)
{
Particle.Size.X *= SizeScale.X;
}
if(MultiplyY)
{
Particle.Size.Y *= SizeScale.Y;
}
if(MultiplyZ)
{
Particle.Size.Z *= SizeScale.Z;
}
}
void UParticleModuleSizeMultiplyLife::Update(FParticleEmitterInstance* Owner, int32 Offset, float DeltaTime)
{
if ((Owner == NULL) || (Owner->ActiveParticles <= 0) ||
(Owner->ParticleData == NULL) || (Owner->ParticleIndices == NULL))
{
return;
}
const FRawDistribution* FastDistribution = LifeMultiplier.GetFastRawDistribution();
FPlatformMisc::Prefetch(Owner->ParticleData, (Owner->ParticleIndices[0] * Owner->ParticleStride));
FPlatformMisc::Prefetch(Owner->ParticleData, (Owner->ParticleIndices[0] * Owner->ParticleStride) + CACHE_LINE_SIZE);
if (MultiplyX && MultiplyY && MultiplyZ)
{
if (FastDistribution)
{
FVector SizeScale;
// fast path
BEGIN_UPDATE_LOOP;
FastDistribution->GetValue3None(Particle.RelativeTime, &SizeScale.X);
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride));
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride) + CACHE_LINE_SIZE);
Particle.Size.X *= SizeScale.X;
Particle.Size.Y *= SizeScale.Y;
Particle.Size.Z *= SizeScale.Z;
END_UPDATE_LOOP;
}
else
{
BEGIN_UPDATE_LOOP
{
FVector SizeScale = LifeMultiplier.GetValue(Particle.RelativeTime, Owner->Component);
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride));
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride) + CACHE_LINE_SIZE);
Particle.Size.X *= SizeScale.X;
Particle.Size.Y *= SizeScale.Y;
Particle.Size.Z *= SizeScale.Z;
}
END_UPDATE_LOOP;
}
}
else
{
if (
( MultiplyX && !MultiplyY && !MultiplyZ) ||
(!MultiplyX && MultiplyY && !MultiplyZ) ||
(!MultiplyX && !MultiplyY && MultiplyZ)
)
{
int32 Index = MultiplyX ? 0 : (MultiplyY ? 1 : 2);
BEGIN_UPDATE_LOOP
{
FVector SizeScale = LifeMultiplier.GetValue(Particle.RelativeTime, Owner->Component);
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride));
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride) + CACHE_LINE_SIZE);
Particle.Size[Index] *= SizeScale[Index];
}
END_UPDATE_LOOP;
}
else
{
BEGIN_UPDATE_LOOP
{
FVector SizeScale = LifeMultiplier.GetValue(Particle.RelativeTime, Owner->Component);
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride));
FPlatformMisc::Prefetch(ParticleData, (ParticleIndices[i+1] * ParticleStride) + CACHE_LINE_SIZE);
if(MultiplyX)
{
Particle.Size.X *= SizeScale.X;
}
if(MultiplyY)
{
Particle.Size.Y *= SizeScale.Y;
}
if(MultiplyZ)
{
Particle.Size.Z *= SizeScale.Z;
}
}
END_UPDATE_LOOP;
}
}
}
void UParticleModuleSizeMultiplyLife::SetToSensibleDefaults(UParticleEmitter* Owner)
{
LifeMultiplier.Distribution = Cast<UDistributionVectorConstantCurve>(StaticConstructObject(UDistributionVectorConstantCurve::StaticClass(), this));
UDistributionVectorConstantCurve* LifeMultiplierDist = Cast<UDistributionVectorConstantCurve>(LifeMultiplier.Distribution);
if (LifeMultiplierDist)
{
// Add two points, one at time 0.0f and one at 1.0f
for (int32 Key = 0; Key < 2; Key++)
{
int32 KeyIndex = LifeMultiplierDist->CreateNewKey(Key * 1.0f);
for (int32 SubIndex = 0; SubIndex < 3; SubIndex++)
{
LifeMultiplierDist->SetKeyOut(SubIndex, KeyIndex, 1.0f);
}
}
LifeMultiplierDist->bIsDirty = true;
}
}