|
|
@@ -44,191 +44,85 @@
|
|
|
#define ALPHA_PROP ParticleProperty::kUserDefined1 // Initial alpha, final alpha, current alpha
|
|
|
#define SIZE_PROP ParticleProperty::kUserDefined2 // Initial size, final size
|
|
|
|
|
|
-void appendAlive(GpuSceneParticleEmitter2 emitter, Vec3 particlePos, F32 particleScale)
|
|
|
+struct ParticleInterface
|
|
|
{
|
|
|
- // Add the alive particle index to the array
|
|
|
- U32 count;
|
|
|
- InterlockedAdd(g_scratch[0].m_aliveParticleCount, 1, count);
|
|
|
- BAB_STORE(g_gpuScene, U32, emitter.m_aliveParticleIndicesOffset + count * sizeof(U32), g_particleIdx);
|
|
|
-
|
|
|
- // Update the AABB
|
|
|
- const F32 toCentimeters = 100.0;
|
|
|
- const IVec3 quatizedPosMin = floor((particlePos + emitter.m_particleAabbMin * particleScale) * toCentimeters);
|
|
|
- const IVec3 quatizedPosMax = ceil((particlePos + emitter.m_particleAabbMax * particleScale) * toCentimeters);
|
|
|
- [unroll] for(U32 i = 0; i < 3; ++i)
|
|
|
+ AnKiParticleEmitterProperties m_props;
|
|
|
+
|
|
|
+ void initAnKiParticleEmitterProperties(GpuSceneParticleEmitter2 emitter)
|
|
|
{
|
|
|
- InterlockedMin(g_scratch[0].m_aabbMin[i], quatizedPosMin[i]);
|
|
|
- InterlockedMax(g_scratch[0].m_aabbMax[i], quatizedPosMax[i]);
|
|
|
+ const AnKiParticleEmitterProperties props = loadAnKiParticleEmitterProperties(g_gpuScene, emitter.m_particleEmitterPropertiesOffset);
|
|
|
+ m_props = props;
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-void simulateParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmitter2 emitter, F32 lifeFactor)
|
|
|
-{
|
|
|
- const F32 m = readProp<F32>(emitter, ParticleProperty::kMass);
|
|
|
- const Vec3 g = readProp<Vec4>(emitter, GRAVITY_PROP).xyz;
|
|
|
- const Vec3 F = m * g;
|
|
|
- Vec3 v = readProp<Vec3>(emitter, ParticleProperty::kVelocity);
|
|
|
- Vec3 x = readProp<Vec3>(emitter, ParticleProperty::kPosition);
|
|
|
- const Vec3 prevX = x;
|
|
|
-
|
|
|
- simulatePhysics(F, m, g_consts.m_dt, v, x, false);
|
|
|
-
|
|
|
- // Write
|
|
|
- writeProp(emitter, ParticleProperty::kVelocity, v);
|
|
|
- const F32 maxLife = readProp<F32>(emitter, ParticleProperty::kMaxLife);
|
|
|
- writeProp(emitter, ParticleProperty::kLifeFactor, lifeFactor + g_consts.m_dt / maxLife);
|
|
|
- writeProp(emitter, ParticleProperty::kPosition, x);
|
|
|
- writeProp(emitter, ParticleProperty::kPreviousPosition, prevX);
|
|
|
-
|
|
|
- const Vec4 sizeProp = readProp<Vec4>(emitter, SIZE_PROP);
|
|
|
- const F32 scale = lerp(sizeProp.x, sizeProp.y, lifeFactor);
|
|
|
- writeProp(emitter, ParticleProperty::kScale, Vec3(scale, scale, scale));
|
|
|
-
|
|
|
- const Vec4 alphaProp = readProp<Vec4>(emitter, ALPHA_PROP);
|
|
|
- const F32 alpha = lerp(alphaProp.x, alphaProp.y, lifeFactor);
|
|
|
- writeProp(emitter, ALPHA_PROP, Vec4(alphaProp.x, alphaProp.y, alpha, 0.0));
|
|
|
-
|
|
|
- // Append alive particle
|
|
|
- appendAlive(emitter, x, scale);
|
|
|
-}
|
|
|
|
|
|
-void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmitter2 emitter, Mat3x4 emitterTrf, Bool makeAlive)
|
|
|
-{
|
|
|
- const F32 m = getRandomRange(props.m_minMass, props.m_maxMass);
|
|
|
-
|
|
|
- Vec3 g = normalize(getRandomRange(props.m_minGravityDirection, props.m_maxGravityDirection));
|
|
|
- g = mul(emitterTrf, Vec4(g, 0.0));
|
|
|
- g = normalize(g); // Because the emitter's trf might have scale
|
|
|
- g *= getRandomRange(props.m_minGravityMagnitude, props.m_maxGravityMagnitude);
|
|
|
-
|
|
|
- const F32 dt = 1.0 / 60;
|
|
|
- const Vec3 v = g * dt;
|
|
|
- const Vec3 x = getRandomRange(props.m_minStartingPosition, props.m_maxStartingPosition) + emitterTrf.getTranslationPart().xyz;
|
|
|
-
|
|
|
- // Store
|
|
|
- writeProp(emitter, ParticleProperty::kVelocity, v);
|
|
|
- writeProp(emitter, ParticleProperty::kLifeFactor, makeAlive ? 0.0 : 1.0);
|
|
|
- writeProp(emitter, ParticleProperty::kMaxLife, getRandomRange(props.m_minParticleLife, props.m_maxParticleLife));
|
|
|
- writeProp(emitter, ParticleProperty::kPosition, x);
|
|
|
- writeProp(emitter, ParticleProperty::kPreviousPosition, x);
|
|
|
- writeProp(emitter, ParticleProperty::kMass, m);
|
|
|
- writeProp(emitter, GRAVITY_PROP, Vec4(g, 0.0));
|
|
|
-
|
|
|
- const F32 initialAlpha = getRandomRange(props.m_minInitialAlpha, props.m_maxInitialAlpha);
|
|
|
- const F32 finalAlpha = getRandomRange(props.m_minFinalAlpha, props.m_maxFinalAlpha);
|
|
|
- writeProp(emitter, ALPHA_PROP, Vec4(initialAlpha, finalAlpha, initialAlpha, 0.0));
|
|
|
-
|
|
|
- const F32 initialSize = getRandomRange(props.m_minInitialSize, props.m_maxInitialSize);
|
|
|
- const F32 finalSize = getRandomRange(props.m_minFinalSize, props.m_maxFinalSize);
|
|
|
- writeProp(emitter, SIZE_PROP, Vec4(initialSize, finalSize, 0.0, 0.0));
|
|
|
-
|
|
|
- writeProp(emitter, ParticleProperty::kScale, Vec3(initialSize, initialSize, initialSize));
|
|
|
-
|
|
|
- // Append into the alive particles
|
|
|
- appendAlive(emitter, x, initialSize);
|
|
|
-}
|
|
|
-
|
|
|
-[numthreads(ANKI_WAVE_SIZE, 1, 1)] void main(COMPUTE_ARGS)
|
|
|
-{
|
|
|
- particlesInitGlobals(svDispatchThreadId.x);
|
|
|
-
|
|
|
- GpuSceneParticleEmitter2 emitter = SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex);
|
|
|
- const AnKiParticleEmitterProperties props = loadAnKiParticleEmitterProperties(g_gpuScene, emitter.m_particleEmitterPropertiesOffset);
|
|
|
- const Mat3x4 emitterTrf = SBUFF(g_gpuSceneTransforms, emitter.m_worldTransformsIndex);
|
|
|
-
|
|
|
- const Bool reinit = emitter.m_reinitializeOnNextUpdate;
|
|
|
- const Bool canEmitThisFrame = emitter.m_timeLeftForNextEmission - g_consts.m_dt <= 0.0;
|
|
|
-
|
|
|
- if(g_particleIdx < emitter.m_particleCount)
|
|
|
+ void simulateParticle(GpuSceneParticleEmitter2 emitter, F32 lifeFactor, out Vec3 particlePosition, out F32 particleScale)
|
|
|
{
|
|
|
- // Decide what to do
|
|
|
- Bool init = false;
|
|
|
- Bool makeAlive = false;
|
|
|
- Bool simulate = false;
|
|
|
- F32 lifeFactor = 1.0;
|
|
|
-
|
|
|
- if(reinit)
|
|
|
- {
|
|
|
- U32 emittedParticleCount;
|
|
|
- InterlockedAdd(g_scratch[0].m_emittedParticleCount, 1, emittedParticleCount);
|
|
|
-
|
|
|
- init = true;
|
|
|
- makeAlive = emittedParticleCount < emitter.m_particlesPerEmission;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- lifeFactor = readProp<F32>(emitter, ParticleProperty::kLifeFactor);
|
|
|
- const Bool alive = lifeFactor < 1.0;
|
|
|
-
|
|
|
- if(alive)
|
|
|
- {
|
|
|
- simulate = true;
|
|
|
- }
|
|
|
- else if(canEmitThisFrame)
|
|
|
- {
|
|
|
- U32 emittedParticleCount;
|
|
|
- InterlockedAdd(g_scratch[0].m_emittedParticleCount, 1, emittedParticleCount);
|
|
|
-
|
|
|
- init = emittedParticleCount < emitter.m_particlesPerEmission;
|
|
|
- makeAlive = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Do the actual work
|
|
|
- if(simulate)
|
|
|
- {
|
|
|
- simulateParticle(props, emitter, lifeFactor);
|
|
|
- }
|
|
|
- else if(init)
|
|
|
- {
|
|
|
- initializeParticle(props, emitter, emitterTrf, makeAlive);
|
|
|
- }
|
|
|
+ const F32 m = readProp<F32>(emitter, ParticleProperty::kMass);
|
|
|
+ const Vec3 g = readProp<Vec4>(emitter, GRAVITY_PROP).xyz;
|
|
|
+ const Vec3 F = m * g;
|
|
|
+ Vec3 v = readProp<Vec3>(emitter, ParticleProperty::kVelocity);
|
|
|
+ Vec3 x = readProp<Vec3>(emitter, ParticleProperty::kPosition);
|
|
|
+ const Vec3 prevX = x;
|
|
|
+
|
|
|
+ simulatePhysics(F, m, g_consts.m_dt, v, x, false);
|
|
|
+
|
|
|
+ // Write
|
|
|
+ writeProp(emitter, ParticleProperty::kVelocity, v);
|
|
|
+ const F32 maxLife = readProp<F32>(emitter, ParticleProperty::kMaxLife);
|
|
|
+ writeProp(emitter, ParticleProperty::kLifeFactor, lifeFactor + g_consts.m_dt / maxLife);
|
|
|
+ writeProp(emitter, ParticleProperty::kPosition, x);
|
|
|
+ writeProp(emitter, ParticleProperty::kPreviousPosition, prevX);
|
|
|
+
|
|
|
+ const Vec4 sizeProp = readProp<Vec4>(emitter, SIZE_PROP);
|
|
|
+ const F32 scale = lerp(sizeProp.x, sizeProp.y, lifeFactor);
|
|
|
+ writeProp(emitter, ParticleProperty::kScale, Vec3(scale, scale, scale));
|
|
|
+
|
|
|
+ const Vec4 alphaProp = readProp<Vec4>(emitter, ALPHA_PROP);
|
|
|
+ const F32 alpha = lerp(alphaProp.x, alphaProp.y, lifeFactor);
|
|
|
+ writeProp(emitter, ALPHA_PROP, Vec4(alphaProp.x, alphaProp.y, alpha, 0.0));
|
|
|
+
|
|
|
+ particlePosition = x;
|
|
|
+ particleScale = scale;
|
|
|
}
|
|
|
|
|
|
- // Sync to make sure all the atomic ops have finished before the following code reads them
|
|
|
- AllMemoryBarrierWithGroupSync();
|
|
|
-
|
|
|
- // Check if it's the last threadgroup running
|
|
|
- if(svGroupIndex == 0)
|
|
|
+ void initializeParticle(GpuSceneParticleEmitter2 emitter, Mat3x4 emitterTrf, Bool makeAlive, out Vec3 particlePosition, out F32 particleScale)
|
|
|
{
|
|
|
- U32 threadgroupIdx;
|
|
|
- InterlockedAdd(g_scratch[0].m_threadgroupCount, 1, threadgroupIdx);
|
|
|
- const U32 threadgroupCount = (emitter.m_particleCount + ANKI_WAVE_SIZE - 1) / ANKI_WAVE_SIZE;
|
|
|
- const Bool lastThreadExecuting = (threadgroupIdx + 1 == threadgroupCount);
|
|
|
-
|
|
|
- if(lastThreadExecuting)
|
|
|
- {
|
|
|
- // Inform about the bounding volume
|
|
|
- const F32 toMeters = 1.0 / 100.0;
|
|
|
- ParticleSimulationCpuFeedback feedback = (ParticleSimulationCpuFeedback)0;
|
|
|
- feedback.m_aabbMin = g_scratch[0].m_aabbMin * toMeters;
|
|
|
- feedback.m_aabbMax = g_scratch[0].m_aabbMax * toMeters;
|
|
|
- feedback.m_uuid = emitter.m_uuid;
|
|
|
- g_cpuFeedback[0] = feedback;
|
|
|
-
|
|
|
- // Update the GPU scene emitter
|
|
|
- if(canEmitThisFrame)
|
|
|
- {
|
|
|
- SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_timeLeftForNextEmission = emitter.m_emissionPeriod;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_timeLeftForNextEmission -= g_consts.m_dt;
|
|
|
- }
|
|
|
-
|
|
|
- if(reinit)
|
|
|
- {
|
|
|
- SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_reinitializeOnNextUpdate = 0;
|
|
|
- }
|
|
|
-
|
|
|
- SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_aliveParticleCount = g_scratch[0].m_aliveParticleCount;
|
|
|
-
|
|
|
- // Reset the scratch struct for next frame
|
|
|
- g_scratch[0].m_aabbMin = kMaxI32;
|
|
|
- g_scratch[0].m_aabbMax = kMinI32;
|
|
|
- g_scratch[0].m_threadgroupCount = 0;
|
|
|
- g_scratch[0].m_emittedParticleCount = 0;
|
|
|
- g_scratch[0].m_aliveParticleCount = 0;
|
|
|
- }
|
|
|
+ const F32 m = getRandomRange(m_props.m_minMass, m_props.m_maxMass);
|
|
|
+
|
|
|
+ Vec3 g = normalize(getRandomRange(m_props.m_minGravityDirection, m_props.m_maxGravityDirection));
|
|
|
+ g = mul(emitterTrf, Vec4(g, 0.0));
|
|
|
+ g = normalize(g); // Because the emitter's trf might have scale
|
|
|
+ g *= getRandomRange(m_props.m_minGravityMagnitude, m_props.m_maxGravityMagnitude);
|
|
|
+
|
|
|
+ const F32 dt = 1.0 / 60;
|
|
|
+ const Vec3 v = g * dt;
|
|
|
+ const Vec3 x = getRandomRange(m_props.m_minStartingPosition, m_props.m_maxStartingPosition) + emitterTrf.getTranslationPart().xyz;
|
|
|
+
|
|
|
+ // Store
|
|
|
+ writeProp(emitter, ParticleProperty::kVelocity, v);
|
|
|
+ writeProp(emitter, ParticleProperty::kLifeFactor, makeAlive ? 0.0 : 1.0);
|
|
|
+ writeProp(emitter, ParticleProperty::kMaxLife, getRandomRange(m_props.m_minParticleLife, m_props.m_maxParticleLife));
|
|
|
+ writeProp(emitter, ParticleProperty::kPosition, x);
|
|
|
+ writeProp(emitter, ParticleProperty::kPreviousPosition, x);
|
|
|
+ writeProp(emitter, ParticleProperty::kMass, m);
|
|
|
+ writeProp(emitter, GRAVITY_PROP, Vec4(g, 0.0));
|
|
|
+
|
|
|
+ const F32 initialAlpha = getRandomRange(m_props.m_minInitialAlpha, m_props.m_maxInitialAlpha);
|
|
|
+ const F32 finalAlpha = getRandomRange(m_props.m_minFinalAlpha, m_props.m_maxFinalAlpha);
|
|
|
+ writeProp(emitter, ALPHA_PROP, Vec4(initialAlpha, finalAlpha, initialAlpha, 0.0));
|
|
|
+
|
|
|
+ const F32 initialSize = getRandomRange(m_props.m_minInitialSize, m_props.m_maxInitialSize);
|
|
|
+ const F32 finalSize = getRandomRange(m_props.m_minFinalSize, m_props.m_maxFinalSize);
|
|
|
+ writeProp(emitter, SIZE_PROP, Vec4(initialSize, finalSize, 0.0, 0.0));
|
|
|
+
|
|
|
+ writeProp(emitter, ParticleProperty::kScale, Vec3(initialSize, initialSize, initialSize));
|
|
|
+
|
|
|
+ particlePosition = x;
|
|
|
+ particleScale = initialSize;
|
|
|
}
|
|
|
+};
|
|
|
+
|
|
|
+[numthreads(ANKI_WAVE_SIZE, 1, 1)] void main(COMPUTE_ARGS)
|
|
|
+{
|
|
|
+ ParticleInterface iface;
|
|
|
+ particleMain(svDispatchThreadId.x, svGroupIndex, iface);
|
|
|
}
|