#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); } } }