54 lines
2.7 KiB
Plaintext
54 lines
2.7 KiB
Plaintext
#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; }
|
|
}
|