pub const PARTICLE_EMIT_GLSL: &str = r#"
#version 430 core
layout(local_size_x = 64) in;
struct Particle {
vec4 position;
vec4 velocity;
vec4 color;
float size;
float mass;
uint flags;
uint attractor;
};
layout(std430, binding = 0) buffer ParticleBuffer { Particle particles[]; };
layout(std430, binding = 1) buffer DeadList { uint dead_count; uint dead_indices[]; };
layout(std430, binding = 2) readonly buffer EmitBuffer { uint emit_count; uvec4 emit_data[]; };
layout(std140, binding = 0) uniform EmitParams {
vec3 origin;
float spread;
vec4 color_a;
vec4 color_b;
float lifetime_min;
float lifetime_max;
float speed_min;
float speed_max;
float size_min;
float size_max;
float time;
uint seed;
};
// Simple hash function for pseudo-randomness
float hash(uint n) {
n = (n ^ 61u) ^ (n >> 16u);
n *= 9u; n ^= n >> 4u;
n *= 0x27d4eb2du; n ^= n >> 15u;
return float(n) / float(0xFFFFFFFFu);
}
vec3 random_dir(uint seed) {
float theta = hash(seed) * 6.2831853;
float phi = hash(seed + 1u) * 3.1415927;
return vec3(sin(phi)*cos(theta), cos(phi), sin(phi)*sin(theta));
}
void main() {
uint idx = gl_GlobalInvocationID.x;
if (idx >= emit_count) return;
// Claim a dead particle slot
uint dead_idx_pos = atomicAdd(dead_count, uint(-1));
if (dead_idx_pos == 0u) return; // No dead particles available
uint slot = dead_indices[dead_idx_pos - 1u];
uint s = seed + idx * 7u;
float lifetime = mix(lifetime_min, lifetime_max, hash(s));
float speed = mix(speed_min, speed_max, hash(s + 2u));
float psize = mix(size_min, size_max, hash(s + 3u));
vec3 dir = random_dir(s + 4u);
vec3 pos = origin + dir * spread * hash(s + 5u);
particles[slot].position = vec4(pos, lifetime);
particles[slot].velocity = vec4(dir * speed, 0.0);
particles[slot].color = mix(color_a, color_b, hash(s + 6u));
particles[slot].size = psize;
particles[slot].mass = 1.0;
particles[slot].flags = 1u;
particles[slot].attractor = 0u;
}
"#;Expand description
Particle emit compute shader — spawns new particles from dead list.