#version 450 #extension GL_GOOGLE_include_directive : require layout(local_size_x = 64) in; #include "rigid_common.glsl" // Explosions (from projectiles on the GPU, from props on the host) push the rigid bodies and wake them. layout(std430, set = 0, binding = 37) buffer Xev { ivec4 xhead; vec4 xe[512]; }; void main() { uint bi = gl_GlobalInvocationID.x; if (int(bi) >= bhdr.x) return; Body B = bodies[bi]; if ((B.i.z & B_REMOVED) != 0) return; int s = B.i.x; float mass = shf(s, 7), radius = shf(s, 9); bool touched = false; for (int k = 0; k < min(xhead.x, 512); ++k) { vec4 e = xe[k]; // x, y, radius, power if (e.w < -0.5) { // an explosion with the force of 00657960: this entry and the next one (force min, force max) vec4 f2 = xe[min(k + 1, 511)]; ++k; const float pixels = float(shi(s, 4) > 0 ? int(shf(s, 7)) : 1); const float massKg = max(pixels * B.q.z * 0.01, 1.0e-4); vec2 O = e.xy, C = B.p.xy; float dist = length(C - O) * 0.1, Ru = e.z * 0.1; // physics units (10 pixels each) if (dist > Ru) continue; // the original needs the angular bin of the explosion to reach this body: approximated by a line of sight through the grid that no solid cell interrupts bool blocked = false; const int steps = int(dist * 10.0); for (int q = 1; q < steps && !blocked; ++q) blocked = solid_at(ivec2(floor(mix(O, C, float(q) / float(steps))))); if (blocked) continue; float t = Ru == 0.0 ? 1.0 : 1.0 - dist / Ru; float A = (f2.y - f2.x) * t + f2.x; vec2 n = dist >= 1.19e-7 ? normalize(C - O) : vec2(0.0); // F = n A (x mass if the body weighs less than 1), x 360 N, dv = F / m / 360 per frame in pixels: = n A min(1, m) / m B.v.xy += n * A * min(1.0, massKg) / massKg; const float inertiaKg = max(shf(s, 8) * B.q.z * 1.0e-4, 1.0e-6); const float tau = float(hash3(bi, uint(xhead.x), 91u) & 0x7FFFu) / 32767.0 * 30.0 - 15.0; // the random torque of the original B.p.w += tau / (3600.0 * inertiaKg); touched = true; continue; } vec2 d = B.p.xy - e.xy - vec2(0.0, 2.0); float dist = length(d); if (dist > e.z + radius) continue; float f = e.w * max(0.0, 1.0 - dist / (e.z + radius)) / sqrt(max(1.0, mass * B.q.z) / 20.0); vec2 n = dist > 1e-3 ? d / dist : vec2(0.0, -1.0); B.v.xy += n * f; B.p.w += (n.x > 0.0 ? 1.0 : -1.0) * f * 0.05; touched = true; } if (touched) { B.i.z &= ~B_ASLEEP; B.i.w = 0; bodies[bi] = B; } }