#version 450 #extension GL_GOOGLE_include_directive : require layout(local_size_x = 64) in; #include "fx_common.glsl" // Particles requested by the host (effects of the rigid bodies: the splash of a body that hits something, ...): count, then per request (position, velocity), (life, gravity), // (colour bits, mode, material). Mode 0: a cosmetic droplet that ends at the first cell it meets, stains it and leaves a cell of `material`, mode 2: the same without the cell (stains only), mode 1: a real particle of the material. struct PartReq { vec4 a; vec4 b; uvec4 c; }; layout(std430, set = 0, binding = 51) buffer PartReqBuf { uvec4 rhead; PartReq reqs[]; }; void main() { const uint i = gl_GlobalInvocationID.x; if (i >= min(rhead.x, 1024u)) return; const PartReq r = reqs[i]; if (r.c.y == 0u) emit_particle_hit(r.a.xy, r.a.zw, r.b.x, r.b.y, r.c.x, int(r.c.z)); else if (r.c.y == 2u) { const uint ps = emit_particle_hit(r.a.xy, r.a.zw, r.b.x, r.b.y, r.c.x, int(r.c.z)); if (ps != 0xFFFFFFFFu) fparts[ps].c.w = 1u; } // stains only, leaves no cell else if (r.c.y == 3u) emit_particle(r.a.xy, r.a.zw, r.b.x, 0.0, r.c.x, true, true); // a light ray (SpriteParticleEmitterComponent, additive): fades, no cell else if (r.c.y == 4u) { if ((r.c.w & 2u) == 0u || cell_free_at(r.a.xy)) { // c.w bit 2: a glowing material (gfx_glow): additive, the colour scaled by the material's alpha; else bits 8-15 = 255 - alpha, the opacity const bool glow = (r.c.w & 4u) != 0u; const float al = 1.0 - float((r.c.w >> 8) & 0xFFu) / 255.0; const uint rgb = glow ? ((uint(float((r.c.x >> 16) & 0xFFu) * al + 0.5) << 16) | (uint(float((r.c.x >> 8) & 0xFFu) * al + 0.5) << 8) | uint(float(r.c.x & 0xFFu) * al + 0.5)) : r.c.x; const int ps = emit_particle_g(r.a.xy, r.a.zw, r.b.x, r.b.y, rgb, (r.c.w & 8u) == 0u, glow, (r.c.w & 1u) == 0u); // c.w bit 3: fade_based_on_lifetime 0 if (ps >= 0 && !glow) fparts[ps].c.z = (r.c.w >> 8) & 0xFFu; if (ps >= 0 && r.b.z > 0.0) fparts[ps].c.w |= (min(uint(r.b.z * 1000.0 + 0.5), 4095u) << 20) | (min(uint(r.b.w * 4096.0 + 0.5), 4095u) << 8); // airflow_force (b.z), airflow_scale (b.w) } } // a cosmetic particle (ParticleEmitterComponent emit_cosmetic_particles): fades, no cell; c.w bit 0: collide_with_grid 0, bit 1: cosmetic_force_create 0 else if (r.c.y == 5u) { // create_real_particles: the cell itself, in the empty cell (or over a gas / liquid unless only-if-space: c.w bit 1) const ivec2 q = ivec2(floor(r.a.xy)) - ivec2(pc.view_x, pc.view_y); if (q.x < 0 || q.y < 0 || q.x >= pc.gw || q.y >= pc.gh) return; const int ci = q.y * pc.gw + q.x; const uint nc = r.c.z | (((hash3(uint(ci), uint(pc.frame), 63u) >> 16) & 0xFFu) << 16); const uint old = atomicCompSwap(cells[ci], 0u, nc); if (old == 0u) { ext[ci] = vec2(0.0); birth[ci] = birth_frame(); } else if ((r.c.w & 2u) == 0u && (mflags(cell_mat(old)) & (F_GAS | F_LIQUID)) != 0u && (mflags(cell_mat(old)) & (F_STATIC | F_SAND)) == 0u && atomicCompSwap(cells[ci], old, nc) == old) { ext[ci] = vec2(0.0); birth[ci] = birth_frame(); } } else if (r.c.y == 6u) emit_displaced(r.a.xy, r.a.zw, r.c.z | (((hash3(i, uint(pc.frame), 64u) >> 16) & 0xFFu) << 16), 2u, birth_frame()); // GameCreateParticle (007b3120 -> 00716af0): a cell of the material handed to the particle system like a displaced one (AddAsParticle 007168c0): it falls and goes back into the grid, waiting for a free place else emit_material_particle(r.a.xy, r.a.zw, r.b.x, r.b.y, r.c.x, int(r.c.z)); }