#version 330 core

// GPU particle simulation (transform feedback). Integrates motion under gravity and respawns dead
// particles from the emitter using a per-particle seed. Output is captured into the ping-pong buffer;
// there is no rasterization (RASTERIZER_DISCARD).
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec3 inVel;
layout(location = 2) in float inLife;
layout(location = 3) in float inSeed;

out vec3 outPos;
out vec3 outVel;
out float outLife;
out float outSeed;

uniform float uDt;
uniform float uTime;
uniform float uLifeMin;
uniform float uLifeMax;
uniform vec3 uGravity;
uniform vec3 uEmitPos;
uniform vec3 uEmitPosSpread;
uniform vec3 uEmitVel;
uniform vec3 uEmitVelSpread;

float hash(float n) {
	return fract(sin(n) * 43758.5453);
}

void main() {
	float life = inLife - uDt;
	vec3 pos = inPos;
	vec3 vel = inVel;

	if (life <= 0.0) {
		// Respawn. Vary the seed by time so a particle's successive lives differ.
		float b = inSeed * 97.13 + uTime * 1.7;
		vec3 rp = vec3(hash(b + 1.0), hash(b + 2.0), hash(b + 3.0)) * 2.0 - 1.0;
		vec3 rv = vec3(hash(b + 4.0), hash(b + 5.0), hash(b + 6.0)) * 2.0 - 1.0;
		pos = uEmitPos + rp * uEmitPosSpread;
		vel = uEmitVel + rv * uEmitVelSpread;
		life = uLifeMin + hash(b + 7.0) * max(uLifeMax - uLifeMin, 0.0);
	} else {
		vel += uGravity * uDt;
		pos += vel * uDt;
	}

	outPos = pos;
	outVel = vel;
	outLife = life;
	outSeed = inSeed;
	gl_Position = vec4(0.0); // unused
}
