Files
Noita-GPU-Vulkan/shaders/displace.comp
T
2026-10-19 20:59:15 +02:00

91 lines
4.6 KiB
Plaintext

#version 450
#extension GL_GOOGLE_include_directive : require
layout(local_size_x = 64) in;
#include "rigid_common.glsl"
// Rigid bodies displace liquids: the liquid cells inside their volume are moved to the nearest free cell outside it, away from the centre (preferring upward).
// (One workgroup per entity, which return at once, then one per rigid body.)
layout(std430, set = 0, binding = 23) readonly buffer Ctrl { ivec4 inp; } ctrl;
// Liquids, and for bodies that do not go through sand (CellData solid_go_through_sand, bit 9 of the body flags) the powders too: a body that sinks into a pile pushes the grains out.
bool liquid_cell(uint c, bool powders) {
uint m = cell_mat(c);
if (m == 0u) return false;
uint f = mflags(m);
if ((f & F_STATIC) != 0u) return false;
return (f & F_LIQUID) != 0u && ((f & F_SAND) == 0u || powders);
}
// Moves the cell at world position w out of the volume: the volume is a box (bodyIdx < 0) or a rigid body's mask.
void push_out(ivec2 w, vec2 centre, ivec4 box, int bodyIdx) {
const bool powders = bodyIdx >= 0 && (bodies[bodyIdx].i.z & 512) == 0;
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return;
int i = p.y * pc.gw + p.x;
uint c = cells[i];
if (!liquid_cell(c, powders)) return;
vec2 d = vec2(w) + 0.5 - centre;
d.y -= 0.8; // up is preferred
d = length(d) < 0.01 ? vec2(0.0, -1.0) : normalize(d);
// the target is claimed first (only free cells are claimed), then the source is released; another invocation that already moved the source (bodies that overlap handle the
// same cell) makes this one give its target back. Nothing is lost or doubled: before, the source was emptied first and put back when no free cell was found, and a neighbour
// could fill the briefly empty source in between (a full pool with rubble in it lost its water)
const vec2 e = ext[i];
for (int r = 1; r <= 12; ++r) {
ivec2 t = w + ivec2(round(d * float(r)));
ivec2 q = t - ivec2(pc.view_x, pc.view_y);
if (q.x < 0 || q.y < 0 || q.x >= pc.gw || q.y >= pc.gh) break;
bool inside;
if (bodyIdx < 0) inside = t.x >= box.x && t.x < box.z && t.y >= box.y && t.y < box.w;
else {
Body b = bodies[bodyIdx];
int s = b.i.x;
vec2 dd = vec2(t) + 0.5 - b.p.xy;
float cs = cos(b.p.z), sn = sin(b.p.z);
vec2 im = vec2(cs * dd.x + sn * dd.y, -sn * dd.x + cs * dd.y) + vec2(shf(s, 5), shf(s, 6));
inside = shape_solid(s, int(floor(im.x)), int(floor(im.y)));
}
if (inside) continue;
int ti = q.y * pc.gw + q.x;
if (atomicCompSwap(cells[ti], 0u, c) == 0u) { // a free target, now holding the liquid
if (atomicCompSwap(cells[i], c, 0u) == c) { ext[ti] = e; ext[i] = vec2(0.0); }
else atomicCompSwap(cells[ti], c, 0u); // the source went elsewhere meanwhile: give the target back
return;
}
}
// nowhere to go: the liquid stays where it is
}
void main() {
int g = int(gl_WorkGroupID.x), t = int(gl_LocalInvocationID.x);
int nEnt = ctrl.inp.w;
if (g < nEnt) {
return; // creatures and the player displace through the particle path of entities.comp (docs/DISPLACEMENT_AND_FIRE.md); only rigid bodies are pushed out here (the engine's body rule is not recovered)
Ent e = ents[g];
int kind = e.st.x;
if ((kind != 1 && kind != 2 && kind != 3) || (e.st.w & 8) != 0) return;
ivec2 ip = ivec2(floor(e.a.xy));
ivec2 lo = ip + ivec2(int(e.box.x), int(e.box.z)), hi = ip + ivec2(int(e.box.y), int(e.box.w));
ivec2 sz = hi - lo;
if (sz.x <= 0 || sz.y <= 0 || sz.x * sz.y > 4096) return;
vec2 centre = vec2(lo + hi) * 0.5;
for (int k = t; k < sz.x * sz.y; k += 64) push_out(lo + ivec2(k % sz.x, k / sz.x), centre, ivec4(lo, hi), -1);
} else {
int bi = g - nEnt;
if (bi >= bhdr.x) return;
Body b = bodies[bi];
if ((b.i.z & 2) != 0) return;
int s = b.i.x;
float rad = shf(s, 9);
int n = int(2.0 * rad) + 2;
ivec2 lo = ivec2(floor(b.p.xy - vec2(rad)));
for (int k = t; k < n * n; k += 64) {
ivec2 w = lo + ivec2(k % n, k / n);
vec2 dd = vec2(w) + 0.5 - b.p.xy;
float cs = cos(b.p.z), sn = sin(b.p.z);
vec2 im = vec2(cs * dd.x + sn * dd.y, -sn * dd.x + cs * dd.y) + vec2(shf(s, 5), shf(s, 6));
if (shape_solid(s, int(floor(im.x)), int(floor(im.y)))) push_out(w, b.p.xy, ivec4(0), bi);
}
}
}