Lot of bugfixes
* Adding clipper to generate the terrain's negative powder-contour offset. * Fixing RDB issues (fracture, active trigger, performances) * Fixing blood emission * Fixing the freezing formula on liquids * Fixing Steve spawning due to a missed test. * Reaction table properly build
This commit is contained in:
1 parent
a34f42d53e
commit
00a9c1c26c
48 files changed
+5799
-435
No files matched your search
+133
-69
@@ -3,56 +3,61 @@
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
#include "common.glsl"
|
||||
|
||||
// Material reactions (liquids mixing, acid, water on lava, ...). The grid is cut into disjoint pairs of neighbouring cells; pc.pass_
|
||||
// (0..3) selects the pairing: 0/1 horizontal pairs starting at even/odd columns, 2/3 vertical pairs. Both cells of a pair evaluate the
|
||||
// same reaction (symmetric hash) and each applies its own side, so no cell is written twice. Four passes visit every adjacency once per frame.
|
||||
// Material reactions: pass 4 plans one attempt per source, pass 5 commits thermal
|
||||
// changes. Passes 0..3 visit disjoint horizontal/vertical pairs; one invocation
|
||||
// owns both outputs. Ordinary passes consume the plan, never a freshly converted material.
|
||||
layout(std430, set = 0, binding = 28) readonly buffer RHead { uint rhead[]; }; // [a * n + b] = (offset << 8) | count
|
||||
layout(std430, set = 0, binding = 23) buffer Ctrl { ivec4 inp; ivec4 inp2; ivec4 inp3; vec4 outp; vec4 outp2; vec4 view; vec4 hand0; ivec4 hand1; vec4 hand2; ivec4 burnN; ivec4 burnEv[256]; ivec4 reactN; ivec4 reactEv[64]; } ctrl; // reactEv: entities to spawn (x, y, entity index + 1)
|
||||
layout(std430, set = 0, binding = 29) readonly buffer RRec { uint rrec[]; }; // 5 words per entry: outs (a | b << 16, 0xFFFF = same), probability / 65536
|
||||
|
||||
void main() {
|
||||
ivec2 p = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (p.x >= pc.gw || p.y >= pc.gh) return;
|
||||
int ph = pc.pass_ & 3;
|
||||
ivec2 q;
|
||||
if (ph < 2) q = p + ivec2(((p.x & 1) == ph) ? 1 : -1, 0);
|
||||
else q = p + ivec2(0, ((p.y & 1) == (ph - 2)) ? 1 : -1);
|
||||
if (q.x < 0 || q.y < 0 || q.x >= pc.gw || q.y >= pc.gh) return;
|
||||
int i = p.y * pc.gw + p.x, j = q.y * pc.gw + q.x;
|
||||
uint ci = cells[i], cj = cells[j];
|
||||
uint mi = cell_mat(ci), mj = cell_mat(cj);
|
||||
if (mi == 0u && mj == 0u) return;
|
||||
uint n = mats[mats.length() - 1u].w;
|
||||
uint h = rhead[mi * n + mj];
|
||||
uint cnt = h & 255u;
|
||||
if (cnt == 0u) return;
|
||||
uint lo = uint(min(i, j)), hi = uint(max(i, j));
|
||||
uint rr = hash3(lo, hi, uint(pc.frame * 4 + ph) ^ 0x51u);
|
||||
uint k = 0u; // the record of the pair table: R % count with the roll R that also gates the attempt (0070eaf0)
|
||||
// 0070eaf0: every cell that has a reaction threshold (CellData + 0x22c: the largest probability of a reaction it is the first input of; air is no cell and never starts one) tries once per
|
||||
// update with ONE random neighbour of four, rolls R (0..10000) against min(that threshold, the record's probability) and the record must accept the direction: the pair gets the chance of
|
||||
// each of its two cells separately (rec / 4 for a material next to air, not rec / 2).
|
||||
{
|
||||
const bool iLo = i < j;
|
||||
const uint mA = iLo ? mi : mj, mB = iLo ? mj : mi;
|
||||
const ivec2 pA = iLo ? p : q, pB = iLo ? q : p;
|
||||
const uint hA = rhead[mA * n + mB], hB = rhead[mB * n + mA];
|
||||
const uint eA = ((hA >> 8) + k % max(hA & 255u, 1u)) * 5u, eB = ((hB >> 8) + k % max(hB & 255u, 1u)) * 5u;
|
||||
const uint pwA = rrec[eA + 1u], pwB = rrec[eB + 1u];
|
||||
const ivec2 dAB = pB - pA;
|
||||
const uint relA = dAB.y < 0 ? 1u : dAB.y > 0 ? 2u : dAB.x < 0 ? 3u : 4u, relB = relA == 1u ? 2u : relA == 2u ? 1u : relA == 3u ? 4u : 3u;
|
||||
const uint dirA = (pwA >> 16) & 7u, dirB = (pwB >> 16) & 7u;
|
||||
// each cell has its own roll R = next15 % 10001: the material threshold (CellData + 0x22c, positive) and the record's threshold both accept R <= threshold, the record is R % count
|
||||
const uint RA = (hash(rr ^ 0xA2u) & 0x7FFFu) % 10001u, RB = (hash(rr ^ 0xB2u) & 0x7FFFu) % 10001u;
|
||||
const uint kA = RA % max(hA & 255u, 1u), kB = RB % max(hB & 255u, 1u);
|
||||
const uint eA2 = ((hA >> 8) + kA) * 5u, eB2 = ((hB >> 8) + kB) * 5u;
|
||||
const uint matA = rhead[n * n + mA], matB = rhead[n * n + mB];
|
||||
const bool fireA = mA != 0u && (hA & 255u) != 0u && matA > 0u && RA <= matA && RA <= (rrec[eA2 + 1u] & 0xFFFFu) && (((rrec[eA2 + 1u] >> 16) & 7u) == 0u || ((rrec[eA2 + 1u] >> 16) & 7u) == relA) && (hash(rr ^ 0xA1u) & 3u) == 0u;
|
||||
const bool fireB = mB != 0u && (hB & 255u) != 0u && matB > 0u && RB <= matB && RB <= (rrec[eB2 + 1u] & 0xFFFFu) && (((rrec[eB2 + 1u] >> 16) & 7u) == 0u || ((rrec[eB2 + 1u] >> 16) & 7u) == relB) && (hash(rr ^ 0xB1u) & 3u) == 0u;
|
||||
if (!fireA && !fireB) return;
|
||||
k = fireA ? kA : kB;
|
||||
layout(std430, set = 0, binding = 8) readonly buffer Tables { uint tbl[]; };
|
||||
#include "biome_mods.glsl"
|
||||
const ivec2 RD4[4] = ivec2[4](ivec2(0, -1), ivec2(-1, 0), ivec2(1, 0), ivec2(0, 1));
|
||||
uint canonical(uint m) { const uint n = mats[mats.length() - 1u].w; return rhead[2u * n * (n + 1u) + m]; }
|
||||
uint draw15(uint seed, uint salt) { return hash(seed ^ salt) & 0x7FFFu; }
|
||||
// One plan per source/update, stored in movement scratch after movement completes.
|
||||
// y: 0 idle, 1 combined, 2 fast, 3 material change. z: shared R, w: neighbour / target.
|
||||
void plan_reaction(ivec2 p, int i) {
|
||||
const uint c = cells[i], m = cell_mat(c), f = mflags(m);
|
||||
const uint n = mats[mats.length() - 1u].w, stride = n * (n + 1u);
|
||||
const uint seed = hash3(uint(p.x + pc.view_x), uint(p.y + pc.view_y), uint(pc.frame) ^ 0x7C6E0u);
|
||||
uint route = 2u;
|
||||
// Worker +0x460 forces a full/combined update after ten ordinary updates.
|
||||
// Initial loaded cells also receive the initial full update.
|
||||
const bool full = (pc.frame % 11) == 0;
|
||||
const bool passive = (f & F_STATIC) != 0u || ((f & F_POWDER) != 0u && (c & ATTACH_BIT) != 0u);
|
||||
const bool slowBranch = full && (!passive || draw15(seed, 1u) % 101u < 20u);
|
||||
if (slowBranch) route = !passive || draw15(seed, 2u) % 101u < 50u ? 1u : 0u;
|
||||
if (m == 0u || (!full && !passive && (f & F_LIQCELL) != 0u && liquid_state(uint(i), m) == 3u && (terrain_age[i] & 255u) > 49u)) route = 0u;
|
||||
const uint R = draw15(seed, 3u) % 10001u, dir = draw15(seed, 4u) & 3u;
|
||||
intent[i] = uvec4(c, route, R, dir);
|
||||
if (route == 1u && (c & BURN_BIT) == 0u) {
|
||||
const uint bb = bm_bytes(vec2(p + ivec2(pc.view_x, pc.view_y)));
|
||||
const uint fc = (bb >> 16) & 255u, uc = bb >> 24;
|
||||
if ((fc | uc) != 0u) {
|
||||
const uvec2 cv = bm_conversions(m);
|
||||
const bool cold = fc != 0u && (cv.x & 65535u) != 0u && draw15(seed, 5u) % ((cv.x >> 16) + 1u) < fc && liquid_freeze_ready(p, c);
|
||||
const bool warm = uc != 0u && (cv.y & 65535u) != 0u && draw15(seed, 6u) % ((cv.y >> 16) + 1u) < uc;
|
||||
if (cold || warm) {
|
||||
// A successful cold flag owns the attempt, including a blocked contact.
|
||||
intent[i].y = 0u;
|
||||
const ivec2 q = p + RD4[dir];
|
||||
if (q.x < 0 || q.y < 0 || q.x >= pc.gw || q.y >= pc.gh) return;
|
||||
const uint nm = cell_mat(cells[q.y * pc.gw + q.x]);
|
||||
const bool allow = cold ? nm == 0u || (mflags(nm) & F_LIQCELL) == 0u || (mflags(nm) & F_POWDER) != 0u : canonical(nm) != canonical(m);
|
||||
if (allow) { intent[i].y = 3u; intent[i].w = ((cold ? cv.x : cv.y) & 65535u) - 1u; }
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint e = ((h >> 8) + k % cnt) * 5u;
|
||||
// 0070ad30 is reached only on the passive full-update tail, after neither
|
||||
// reaction method returned. It is not unconditional terrain cleanup.
|
||||
if (full && passive && route != 1u && rhead[stride + n * n + m] == 0u && (c & BURN_BIT) == 0u && p.x > 0 && p.y > 0 && p.x + 1 < pc.gw && p.y + 1 < pc.gh) {
|
||||
if (cell_mat(cells[i-1]) == 0u && cell_mat(cells[i+1]) == 0u && cell_mat(cells[i-pc.gw]) == 0u && cell_mat(cells[i+pc.gw]) == 0u) { intent[i].y = 3u; intent[i].w = 0u; }
|
||||
}
|
||||
}
|
||||
|
||||
void apply_side(ivec2 p, ivec2 q, int i, int j, uint ci, uint cj, uint mi, uint mj, uint e, uint rr) {
|
||||
uint outs = rrec[e];
|
||||
uint third = rrec[e + 2u];
|
||||
uint blob = rrec[e + 3u];
|
||||
@@ -60,31 +65,13 @@ void main() {
|
||||
bool second = (mi == mj) && (i > j); // equal materials: the lower cell takes the first output
|
||||
uint o = second ? (outs >> 16) : (outs & 0xFFFFu);
|
||||
const bool firstIsI0 = (rrec[e + 4u] & 0x80000000u) == 0u; // (the record is the mirrored one when the first input is the second material of the pair)
|
||||
const uint reqLife = blob >> 20;
|
||||
if (reqLife > 0u && birth_age(birth[firstIsI0 ? i : j]) < reqLife) return; // req_lifetime: the first cell must be old enough
|
||||
if (((blob >> 19) & 1u) != 0u && firstIsI0 && !second) { // destroy_horizontally_lonely_pixels: the first output becomes air when both horizontal neighbours are empty (both probed)
|
||||
const bool le = p.x > 0 && cell_mat(cells[i - 1]) == 0u, ri = p.x + 1 < pc.gw && cell_mat(cells[i + 1]) == 0u;
|
||||
if (le && ri) o = 0u;
|
||||
}
|
||||
// three inputs (0070ebf0): a third cell next to the first input's cell (a cardinal neighbour chosen at random; the second cell's place is mirrored to the other side)
|
||||
// must hold the third material; the thread of the first cell writes its output
|
||||
if ((third & 0xFFFFu) != 0xFFFFu) {
|
||||
const bool firstIsI = firstIsI0;
|
||||
const ivec2 pf = firstIsI ? p : q, ps = firstIsI ? q : p;
|
||||
const ivec2 dirs[4] = ivec2[4](ivec2(0, -1), ivec2(-1, 0), ivec2(1, 0), ivec2(0, 1));
|
||||
ivec2 p3 = pf + dirs[(rr >> 20) & 3u];
|
||||
if (p3 == ps) p3 = 2 * pf - ps;
|
||||
if (p3.x < 0 || p3.y < 0 || p3.x >= pc.gw || p3.y >= pc.gh) return;
|
||||
const int k3 = p3.y * pc.gw + p3.x;
|
||||
const uint c3 = cells[k3];
|
||||
if (cell_mat(c3) != (third & 0xFFFFu)) return;
|
||||
const uint o3 = (third >> 16) & 0xFFFFu;
|
||||
if (firstIsI && o3 != 0xFFFFu && o3 != cell_mat(c3)) { atomicCompSwap(cells[k3], c3, o3 | (c3 & 0x00FF0000u)); birth[k3] = birth_frame(); }
|
||||
}
|
||||
if (o == 0xFFFFu || o == mi) return; // this side stays as it is
|
||||
cells[i] = o | (ci & 0x00FF0000u); // keeps the colour variation, clears burning and direction bits
|
||||
ext[i] = vec2(0.0);
|
||||
birth[i] = birth_frame();
|
||||
material_changed(i, ci, o);
|
||||
if (entityIdx != 0u && firstIsI0 && !second) { // the reaction loads an entity at the position of the first input (the host spawns it)
|
||||
const int ei = atomicAdd(ctrl.reactN.x, 1);
|
||||
if (ei < 64) ctrl.reactEv[ei] = ivec4(p.x + pc.view_x, p.y + pc.view_y, int(entityIdx), 0);
|
||||
@@ -100,14 +87,16 @@ void main() {
|
||||
for (int ray = 0; ray < nr; ++ray) {
|
||||
const float a = float(ray) * 6.2831853 / float(nr);
|
||||
const vec2 dir = vec2(cos(a), sin(a));
|
||||
vec2 samplePos = vec2(p + ivec2(pc.view_x, pc.view_y));
|
||||
for (int st = 1; st <= int(rad); ++st) {
|
||||
const ivec2 t = p + ivec2(trunc(dir * float(st)));
|
||||
samplePos += dir;
|
||||
const ivec2 t = ivec2(samplePos) - ivec2(pc.view_x, pc.view_y);
|
||||
if (t.x < 0 || t.y < 0 || t.x >= pc.gw || t.y >= pc.gh) break;
|
||||
const int ti = t.y * pc.gw + t.x;
|
||||
const uint tc = cells[ti], tm = cell_mat(tc);
|
||||
if (tm == o) continue;
|
||||
if (tm != mi) break;
|
||||
if (atomicCompSwap(cells[ti], tc, o | (tc & 0x00FF0000u)) == tc) { ext[ti] = vec2(0.0); birth[ti] = birth_frame(); } // a converted cell starts afresh (0070d0b0)
|
||||
if (canonical(tm) == canonical(o)) continue;
|
||||
if (canonical(tm) != canonical(mi)) break;
|
||||
if (atomicCompSwap(cells[ti], tc, o | (tc & 0x00FF0000u)) == tc) { material_changed(ti, tc, o); } // same-class movement state survives (0070d0b0)
|
||||
}
|
||||
}
|
||||
} else if (rad > 0u) { // direct mode (0071ab50, restriction explicitly off): every loaded cell of the disk dx^2 + dy^2 < r^2 takes the output, whatever it holds
|
||||
@@ -120,8 +109,83 @@ void main() {
|
||||
const int ti = t.y * pc.gw + t.x;
|
||||
const uint tc = cells[ti];
|
||||
if (cell_mat(tc) == o) continue;
|
||||
if (atomicCompSwap(cells[ti], tc, o | (tc & 0x00FF0000u)) == tc) { ext[ti] = vec2(0.0); birth[ti] = birth_frame(); }
|
||||
if (atomicCompSwap(cells[ti], tc, o | (tc & 0x00FF0000u)) == tc) { material_changed(ti, tc, o); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 p = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (p.x >= pc.gw || p.y >= pc.gh) return;
|
||||
const int i = p.y * pc.gw + p.x;
|
||||
if (pc.pass_ == 4) { plan_reaction(p, i); return; }
|
||||
if (pc.pass_ == 5) {
|
||||
if (intent[i].y == 3u && cells[i] == intent[i].x) {
|
||||
cells[i] = intent[i].w | (intent[i].x & 0xFF0000u);
|
||||
material_changed(i, intent[i].x, intent[i].w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int ph = pc.pass_ & 3;
|
||||
ivec2 q;
|
||||
if (ph < 2) q = p + ivec2(((p.x & 1) == ph) ? 1 : -1, 0);
|
||||
else q = p + ivec2(0, ((p.y & 1) == (ph - 2)) ? 1 : -1);
|
||||
if (q.x < 0 || q.y < 0 || q.x >= pc.gw || q.y >= pc.gh) return;
|
||||
const int j = q.y * pc.gw + q.x;
|
||||
if (i > j) return; // One invocation owns both outputs; no half-reaction race.
|
||||
uint ci = cells[i], cj = cells[j];
|
||||
// A conversion must not start a second reaction as its new material this tick.
|
||||
if (ci != intent[i].x || cj != intent[j].x) return;
|
||||
uint mi = cell_mat(ci), mj = cell_mat(cj);
|
||||
if (mi == 0u && mj == 0u) return;
|
||||
const uint n = mats[mats.length() - 1u].w, stride = n * (n + 1u);
|
||||
const int a = min(i, j), b = max(i, j);
|
||||
const ivec2 pa = i < j ? p : q, pb = i < j ? q : p;
|
||||
const uint ma = cell_mat(intent[a].x), mb = cell_mat(intent[b].x);
|
||||
uint selectedBase = 0u, k = 0u;
|
||||
bool found = false;
|
||||
for (int side = 0; side < 2; ++side) {
|
||||
const int src = side == 0 ? a : b;
|
||||
const uint route = intent[src].y;
|
||||
if (route != 1u && route != 2u) continue;
|
||||
const ivec2 delta = side == 0 ? pb - pa : pa - pb;
|
||||
if (RD4[intent[src].w] != delta) continue;
|
||||
const uint sm = side == 0 ? ma : mb, tm = side == 0 ? mb : ma;
|
||||
const uint base = route == 2u ? stride : 0u;
|
||||
const uint h = rhead[base + sm * n + tm], cnt = h & 255u, R = intent[src].z;
|
||||
if (sm == 0u || cnt == 0u || rhead[base + n * n + sm] == 0u || R > rhead[base + n * n + sm]) continue;
|
||||
const uint candidate = R % cnt, pw = rrec[((h >> 8) + candidate) * 5u + 1u];
|
||||
const uint rel = delta.y < 0 ? 1u : delta.y > 0 ? 2u : delta.x < 0 ? 3u : 4u;
|
||||
if (R > (pw & 65535u) || (((pw >> 16) & 7u) != 0u && ((pw >> 16) & 7u) != rel)) continue;
|
||||
selectedBase = base; k = candidate; found = true; break;
|
||||
}
|
||||
if (!found) return;
|
||||
const uint h = rhead[selectedBase + mi * n + mj], cnt = h & 255u;
|
||||
if (cnt == 0u) return;
|
||||
const uint rr = hash3(uint(a), uint(b), uint(pc.frame) ^ 0x51u);
|
||||
uint e = ((h >> 8) + k % cnt) * 5u;
|
||||
const uint third = rrec[e + 2u];
|
||||
const bool firstIsI0 = (rrec[e + 4u] & 0x80000000u) == 0u;
|
||||
const uint reqLife = rrec[e + 3u] >> 20;
|
||||
if (reqLife > 0u && birth_age(birth[firstIsI0 ? i : j]) < reqLife) return;
|
||||
// three inputs (0070ebf0): a third cell next to the first input's cell (a cardinal neighbour chosen at random; the second cell's place is mirrored to the other side)
|
||||
// must hold the third material; the thread of the first cell writes its output
|
||||
if ((third & 0xFFFFu) != 0xFFFFu) {
|
||||
const bool firstIsI = firstIsI0;
|
||||
const ivec2 pf = firstIsI ? p : q, ps = firstIsI ? q : p;
|
||||
const ivec2 dirs[4] = ivec2[4](ivec2(0, -1), ivec2(-1, 0), ivec2(1, 0), ivec2(0, 1));
|
||||
ivec2 p3 = pf + dirs[(rr >> 20) & 3u];
|
||||
if (p3 == ps) p3 = 2 * pf - ps;
|
||||
if (p3.x < 0 || p3.y < 0 || p3.x >= pc.gw || p3.y >= pc.gh) return;
|
||||
const int k3 = p3.y * pc.gw + p3.x;
|
||||
const uint c3 = cells[k3];
|
||||
if (canonical(cell_mat(c3)) != canonical(third & 0xFFFFu)) return;
|
||||
const uint o3 = (third >> 16) & 0xFFFFu;
|
||||
if (o3 != 0xFFFFu && o3 != cell_mat(c3)) { if (atomicCompSwap(cells[k3], c3, o3 | (c3 & 0x00FF0000u)) == c3) material_changed(k3, c3, o3); }
|
||||
}
|
||||
apply_side(p, q, i, j, ci, cj, mi, mj, e, rr);
|
||||
const uint reverseHead = rhead[selectedBase + mj * n + mi];
|
||||
if ((reverseHead & 255u) != 0u)
|
||||
apply_side(q, p, j, i, cj, ci, mj, mi, ((reverseHead >> 8) + k % (reverseHead & 255u)) * 5u, rr);
|
||||
}
|
||||
Reference in new issue
Block a user