Files
2026-07-13 13:13:17 +08:00

163 lines
4.6 KiB
HLSL

#include "shared/point.hlsl"
#include "shared/quat-functions.hlsl"
cbuffer Params : register(b0)
{
float Speed;
float Drag;
float InitialVelocity;
float Time;
float OrientTowardsVelocity;
float RadiusFromW;
float LifeTime;
}
cbuffer IntParams : register(b1)
{
int TriggerEmit;
int TriggerReset;
int CollectCycleIndex;
int SetFx1To;
int SetFx2To;
int EmitMode;
int IsAutoCount;
int EmitVelocityFactor;
}
StructuredBuffer<Point> EmitPoints : t0;
RWStructuredBuffer<Particle> Particles : u0;
RWStructuredBuffer<Point> ResultPoints : u1;
#define W_KEEP_ORIGINAL 0
#define W_PARTICLE_AGE 1
#define W_PARTICLE_SPEED 2
[numthreads(64, 1, 1)] void main(uint3 i : SV_DispatchThreadID)
{
uint newPointCount, pointStride;
EmitPoints.GetDimensions(newPointCount, pointStride);
uint maxParticleCount, pointStride2;
Particles.GetDimensions(maxParticleCount, pointStride2);
uint gi = i.x;
if (gi >= maxParticleCount)
return;
if (TriggerReset > 0.5)
{
Particles[gi].BirthTime = NAN;
Particles[gi].Position = NAN;
ResultPoints[gi].Scale = NAN;
}
// Insert emit points
int addIndex = 0;
if (EmitMode == 0)
{
addIndex = (gi + CollectCycleIndex + maxParticleCount) % maxParticleCount;
}
else
{
int t = (gi + CollectCycleIndex / newPointCount) % maxParticleCount;
int blockSize = maxParticleCount / newPointCount;
int particleBlock = t / blockSize;
int t2 = t - (particleBlock * blockSize);
addIndex = t2 > 0 ? -1 : particleBlock;
}
if (TriggerEmit && addIndex >= 0 && addIndex < (int)newPointCount)
{
if (EmitMode != 0)
{
Particles[(gi - 1) % maxParticleCount].BirthTime = NAN;
Particles[(gi - 1) % maxParticleCount].Radius = NAN;
}
Point emitPoint = EmitPoints[addIndex];
Particles[gi].Position = emitPoint.Position;
Particles[gi].Rotation = emitPoint.Rotation;
Particles[gi].Radius = emitPoint.Scale.x * RadiusFromW;
Particles[gi].BirthTime = Time;
float emitVelocity = InitialVelocity * (EmitVelocityFactor == 0 ? 1 : (EmitVelocityFactor == 1 ? emitPoint.FX1 : emitPoint.FX2));
Particles[gi].Velocity = qRotateVec3(float3(0, 0, 1), normalize(Particles[gi].Rotation)) * emitVelocity;
// These will not change over lifetime...
Particles[gi].Color = emitPoint.Color;
ResultPoints[gi].Scale = emitPoint.Scale;
ResultPoints[gi].FX1 = emitPoint.FX1;
ResultPoints[gi].FX2 = emitPoint.FX2;
ResultPoints[gi].Color = emitPoint.Color;
}
if (Particles[gi].BirthTime == NAN)
return;
float3 velocity = Particles[gi].Velocity;
// drag step (frame-rate independent)
// velocity *= (1 - Drag);
velocity *= pow(1.0f - Drag, Speed); // improved version that better aligns
Particles[gi].Velocity = velocity;
// position step
float3 pos = Particles[gi].Position;
pos += velocity * Speed * 0.01;
Particles[gi].Position = pos;
float speed = length(velocity);
if (speed > 0.0001)
{
float f = saturate(speed * OrientTowardsVelocity);
Particles[gi].Rotation = qSlerp(Particles[gi].Rotation, qLookAt(velocity / speed, float3(0, 1, 0)), f);
}
// Copy result
// Todo: This could by optimized by not copying color
// ResultPoints[gi] = Particles[gi];
ResultPoints[gi].Position = Particles[gi].Position;
ResultPoints[gi].Rotation = Particles[gi].Rotation;
ResultPoints[gi].Color = Particles[gi].Color;
// Attempt with lerping to smooth position updates
// ResultPoints[gi].position = lerp(Particles[gi].p.position, ResultPoints[gi].position, 0);
// ResultPoints[gi].rotation = Particles[gi].p.rotation;
// ResultPoints[gi].w = Particles[gi].p.w;
float lifeTime = LifeTime < 0.0
? (IsAutoCount ? 100000 : (float)(maxParticleCount / (newPointCount * 60.0)))
: LifeTime;
float normalizedAge = (IsAutoCount && LifeTime < 0) ? 1 : (Time - Particles[gi].BirthTime) / lifeTime;
bool tooOld = normalizedAge > 1;
if (SetFx1To == W_PARTICLE_AGE)
{
ResultPoints[gi].FX1 = normalizedAge;
}
else if (SetFx1To == W_PARTICLE_SPEED)
{
ResultPoints[gi].FX1 = speed * 100;
}
if (SetFx2To == W_PARTICLE_AGE)
{
ResultPoints[gi].FX2 = normalizedAge;
}
else if (SetFx2To == W_PARTICLE_SPEED)
{
ResultPoints[gi].FX2 = speed * 100;
}
if (tooOld)
{
ResultPoints[gi].Scale = NAN;
}
}