#version 450 #extension GL_GOOGLE_include_directive : require layout(local_size_x = 8, local_size_y = 8) in; #include "common.glsl" #include "areas.glsl" #include "far.glsl" // Terrain generation on the GPU: GLSL port of the engine's wang-image -> cell path // (docs/ENGINE_ANALYSIS.md section 18). One invocation per grid cell. layout(std430, set = 0, binding = 6) readonly buffer Field { float field[]; }; // wang solidity plane layout(std430, set = 0, binding = 7) readonly buffer MatPlane { uint matplane[]; }; // CellData index + 1, 0 = none layout(std430, set = 0, binding = 8) readonly buffer Tables { uint perm[]; }; // [0,512) simplex, [512,1024) perlin layout(std430, set = 0, binding = 9) readonly buffer MatParams { vec2 mparams[]; }; // per material: warp, threshold layout(std430, set = 0, binding = 10) readonly buffer CellsOld { uint cells_old[]; }; layout(std430, set = 0, binding = 11) readonly buffer ExtOld { vec2 ext_old[]; }; layout(std430, set = 0, binding = 52) readonly buffer BirthOld { uint birth_old[]; }; // the ages before the scroll layout(std430, set = 0, binding = 66) readonly buffer TerrainAgeOld { uint terrain_age_old[]; }; layout(std430, set = 0, binding = 12) readonly buffer Scene { uint scene[]; }; // pixel scenes: 0 = none, else material + 1 layout(std430, set = 0, binding = 18) readonly buffer Override { uint ovr[]; }; // cells restored from the persistent store (0xFFFFFFFF = none) int ffloor(float v) { return int(floor(v)); } float fade(float t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } float sstep(float t) { return (3.0 - t * 2.0) * t * t; } float sgrad(uint hsh, float x, float y) { uint h = hsh & 7u; float u = h < 4u ? x : y; float v = h < 4u ? y : x; return (((h & 1u) != 0u) ? -u : u) + (((h & 2u) != 0u) ? -2.0 * v : 2.0 * v); } uint ps(uint i) { return perm[i]; } uint pp(uint i) { return perm[512u + i]; } float simplex2(float x, float y) { const float F2 = 0.366025404, G2 = 0.211324865; float s = (x + y) * F2; int i = ffloor(x + s), j = ffloor(y + s); float t = float(i + j) * G2; float x0 = x - (float(i) - t), y0 = y - (float(j) - t); int i1 = x0 > y0 ? 1 : 0, j1 = 1 - i1; float x1 = x0 - float(i1) + G2, y1 = y0 - float(j1) + G2; float x2 = x0 - 1.0 + 2.0 * G2, y2 = y0 - 1.0 + 2.0 * G2; uint ii = uint(i) & 255u, jj = uint(j) & 255u; float n0 = 0.0, n1 = 0.0, n2 = 0.0; float t0 = 0.5 - x0 * x0 - y0 * y0; if (t0 >= 0.0) { t0 *= t0; n0 = t0 * t0 * sgrad(ps(ii + ps(jj)), x0, y0); } float t1 = 0.5 - x1 * x1 - y1 * y1; if (t1 >= 0.0) { t1 *= t1; n1 = t1 * t1 * sgrad(ps(ii + uint(i1) + ps(jj + uint(j1))), x1, y1); } float t2 = 0.5 - x2 * x2 - y2 * y2; if (t2 >= 0.0) { t2 *= t2; n2 = t2 * t2 * sgrad(ps(ii + 1u + ps(jj + 1u)), x2, y2); } return 40.0 * (n0 + n1 + n2); } // 8 gradients of FUN_00872a20 vec2 pgrad(uint g) { const vec2 G[8] = vec2[8](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1), vec2(-1, -1), vec2(-1, 1), vec2(1, -1), vec2(1, 1)); return G[g]; } float perlin2(float x, float y) { int ix = ffloor(x), iy = ffloor(y); uint X = uint(ix) & 255u, Y = uint(iy) & 255u, X1 = uint(ix + 1) & 255u, Y1 = uint(iy + 1) & 255u; float fx = x - float(ix), fy = y - float(iy); uint pa = pp(X), pb = pp(X1); float na = dot(pgrad(pp(pa + Y) & 7u), vec2(fx, fy)); float nb = dot(pgrad(pp(pb + Y) & 7u), vec2(fx - 1.0, fy)); float nc = dot(pgrad(pp(pa + Y1) & 7u), vec2(fx, fy - 1.0)); float nd = dot(pgrad(pp(pb + Y1) & 7u), vec2(fx - 1.0, fy - 1.0)); float v = fade(fy), u = fade(fx); return mix(mix(na, nc, v), mix(nb, nd, v), u); } float value2(float x, float y) { int ix = ffloor(x), iy = ffloor(y); uint X = uint(ix) & 255u, Y = uint(iy) & 255u, X1 = uint(ix + 1) & 255u, Y1 = uint(iy + 1) & 255u; float fx = x - float(ix), fy = y - float(iy); uint pa = pp(X), pb = pp(X1); float v00 = float(pp(pa + Y)) / 255.0, v01 = float(pp(pa + Y1)) / 255.0; float v10 = float(pp(pb + Y)) / 255.0, v11 = float(pp(pb + Y1)) / 255.0; float v = fade(fy), u = fade(fx); return (mix(mix(v00, v01, v), mix(v10, v11, v), u) - 0.5) * 2.0; } int wrapi(int v, int n) { v = v % n; return v < 0 ? v + n : v; } int gpw, gph, gfo, gmo; // the area of the cell being generated: plane size and offsets float field_at(int x, int y) { return field[gfo + wrapi(y, gph) * gpw + wrapi(x, gpw)]; } // FUN_008712d0: nearest wang pixel (smoothstep(f) >= 0.5 rounds up); CellData index or -1 int material_pixel(float px, float py) { int ix = ffloor(px), iy = ffloor(py); float fx = px - float(ix), fy = py - float(iy); if (!(0.5 > sstep(fy))) ++iy; if (!(0.5 > sstep(fx))) ++ix; return int(matplane[gmo + wrapi(iy, gph) * gpw + wrapi(ix, gpw)]) - 1; } // FUN_00870e60: smoothstep-weighted bilinear sample of the solidity plane float bilinear(float px, float py) { int ix = ffloor(px), iy = ffloor(py); float sx = sstep(px - float(ix)), sy = sstep(py - float(iy)); float top = mix(field_at(ix, iy), field_at(ix + 1, iy), sx); float bot = mix(field_at(ix, iy + 1), field_at(ix + 1, iy + 1), sx); return mix(top, bot, sy); } // ---- biome evaluator (FUN_0086d2a0) and its noise: 70-scaled simplex (FUN_00872300), 4-octave fbm (FUN_009296d0), polka dots (FUN_00872140) #include "procgrad.glsl" // FUN_0086fd40 / FUN_0086fe30: the cell hash (one value, and three more from the same product) float cell_u(vec2 c) { float cx = floor(c.x * 0.014084507), cy = floor(c.y * 0.014084507); float a = c.x - cx * 71.0 + 26.0, b = c.y - cy * 71.0 + 161.0; return b * b * (a * a); } float polka(float x, float y, float rlow, float rhigh, bool boxed, float prob) { vec2 c = vec2(floor(x), floor(y)); float u = cell_u(c); if (fract(u * 0.0010131986) >= prob) return 0.0; float fx = x - c.x, fy = y - c.y; float o0 = fract(u * 0.0010513747), o1 = fract(u * 0.0015553312), o2 = fract(u * 0.0012450161); float r = (rhigh - rlow) * o2 + rlow; if (r <= 0.0) return 0.0; float inv = 2.0 / r; float dx = fx * inv - (inv - 1.0) + (inv - 2.0) * o0; float dy = fy * inv - (inv - 1.0) + (inv - 2.0) * o1; float d; if (boxed) { d = dy * dy; d *= d; float e = dx * dx; d += e * e; if (d > 1.0) return 0.0; } else { d = min(dx * dx + dy * dy, 1.0); } float q = 1.0 - d; return q * q * q; } // area a's component list: 20 words each (see AreaStore): [0] material, [1] flags, [2..3] min max, [4..5] add_perlin scale, [6..7] limit min max, [8..9] rare scale, // [10..11] rare offset, [12..13] required min max, [14..15] polka radius low high, [16] polka probability uint eval_materials_at(int base, int cnt, float v, ivec2 w) { float fx = float(w.x), fy = float(w.y); for (int k = 0; k < cnt; ++k) { int o = base + 20 * k; uint mat = atab[o]; uint fl = atab[o + 1]; if (mat == 0xFFFFFFFFu) continue; if ((fl & 1u) != 0u && (fy < uintBitsToFloat(atab[o + 6]) || fy > uintBitsToFloat(atab[o + 7]))) continue; float vv = v; if ((fl & 2u) != 0u) vv += simplex70(fx * uintBitsToFloat(atab[o + 4]), fy * uintBitsToFloat(atab[o + 5])); if (!(vv >= uintBitsToFloat(atab[o + 2]) && vv < uintBitsToFloat(atab[o + 3]))) continue; if ((fl & 4u) == 0u) return mat; float px = uintBitsToFloat(atab[o + 10]) + fx * uintBitsToFloat(atab[o + 8]); float py = uintBitsToFloat(atab[o + 11]) + fy * uintBitsToFloat(atab[o + 9]); float rmin = uintBitsToFloat(atab[o + 12]), rmax = uintBitsToFloat(atab[o + 13]); if ((fl & 8u) != 0u) { float n = (fl & 16u) != 0u ? fbm70(px, py) : simplex70(px, py); if (rmin >= n || n > rmax) continue; } if ((fl & 32u) == 0u) return mat; float n2 = polka(px, py, uintBitsToFloat(atab[o + 14]), uintBitsToFloat(atab[o + 15]), (fl & 64u) != 0u, uintBitsToFloat(atab[o + 16])); if (rmin < n2 && n2 <= rmax) return mat; } return 0u; } uint eval_materials(int a, float v, ivec2 w) { return eval_materials_at(arec(a, 14), arec(a, 15), v, w); } // ---- procedural biomes (FUN_0087e110): gradient between the biome's y limits, inside perlin, noise-type blend; the BitmapCaves image is not generated (value 1 everywhere) float sin_big(float t) { // sin of a large float: reduce by 2*pi in two steps first (the GPU sin loses its accuracy for large arguments) float k = floor(t * 0.15915494309189535 + 0.5); float r = fma(-k, 6.28125, t); r = fma(-k, 0.0019353071795864769, r); return sin(r); } float iq_hash(float n) { return fract(sin_big(n) * 43758.547); } float iq_mix(float n, float sx, float sy) { float a = iq_hash(n), b = iq_hash(n + 1.0), c = iq_hash(n + 57.0), d = iq_hash(n + 58.0); return mix(mix(a, b, sx), mix(c, d, sx), sy); } float iq_noise(float x, float y) { // FUN_00871630 float ix = floor(x), iy = floor(y); float fx = x - ix, fy = y - iy; return iq_mix(iy * 57.0 + ix, (3.0 - 2.0 * fx) * fx * fx, (3.0 - 2.0 * fy) * fy * fy); } float iq_smooth_par(float f, int i) { return (i % 2 == 0) ? (3.0 - 2.0 * f) * f * f : f; } float iq_noise2(float x, float y) { // FUN_00871850: the interpolation weight depends on the parity of the cell float ix = floor(x), iy = floor(y); float fx = x - ix, fy = y - iy; int jx = int(ix), jy = int(iy); float sx0 = iq_smooth_par(fx, jx), sx1 = iq_smooth_par(fx, jx + 1); float sy0 = iq_smooth_par(fy, jy), sy1 = iq_smooth_par(fy, jy + 1); return iq_mix(iy * 57.0 + ix, (sx1 - sx0) * fx + sx0, (sy1 - sy0) * fy + sy0); } float noise_t(int t, float x, float y) { // FUN_00873c00 (types 0-4 are not used by any biome of the game) if (t == 5) return simplex70(x, y); if (t == 6) return perlin2(x, y); if (t == 7) return value2(x, y); if (t == 8) return simplex2(x, y); return 0.0; } float fbm_t(int t, float x, float y) { // FUN_00873cc0 const vec4 M = vec4(0.84147, 0.5403, 0.5403, -0.84147); const float K[3] = float[3](2.02, 2.33, 2.01); float sum = noise_t(t, x, y) * 0.5, wgt = 0.25; for (int o = 0; o < 3; ++o) { float nx = (M.x * x + M.z * y) * K[o], ny = (M.y * x + M.w * y) * K[o]; x = nx; y = ny; sum += noise_t(t, x, y) * wgt; wgt *= 0.5; } return sum / 0.9375; } float inside_perlin(uint rec, float X, float Y) { // FUN_0087e7a0 uint fl = atab[rec]; float x = X * 0.05243442 * tf(rec, 1) + 0.1 + tf(rec, 5); float y = Y * 0.05243442 * tf(rec, 2) + 0.1 + tf(rec, 6); if ((fl & 64u) != 0u) { float sd = float(atab[5]); x += sd * 1.312e-5; y += sd * 2.331e-5; } int it = int((fl >> 12) & 15u); float n = (fl & 2u) != 0u ? fbm_t(it, x, y) : noise_t(it, x, y); float v = n; if ((fl & 4u) != 0u) v = n * n; float mn = tf(rec, 3), mx = tf(rec, 4); if ((fl & 8u) != 0u) { if (!(mx > v)) v = mx; if (!(v > mn)) v = mn; } if ((fl & 16u) != 0u) v = (v * 0.5 + 0.5) * (mx - mn) + mn; return v; } float cave_at(uint base, int cw, int ch, int x, int y) { // the cave image: both axes wrap x = ((x % cw) + cw) % cw; y = ((y % ch) + ch) % ch; return field[base + uint(y * cw + x)]; } float cave_sample(uint base, int cw, int ch, float xf, float yf) { // FUN_00870e60 int ix = int(floor(xf)), iy = int(floor(yf)); float fx = xf - float(ix), fy = yf - float(iy); float sx = (3.0 - fx * 2.0) * (fx * fx), sy = (3.0 - fy * 2.0) * (fy * fy); float a = cave_at(base, cw, ch, ix, iy), b = cave_at(base, cw, ch, ix + 1, iy), c = cave_at(base, cw, ch, ix, iy + 1), d = cave_at(base, cw, ch, ix + 1, iy + 1); float top = (b - a) * sx + a, bot = (d - c) * sx + c; return (bot - top) * sy + top; } float proc_value(ivec2 w, uint rec) { float X = float(w.x), Y = float(w.y); float v = 1.0; if (atab[rec + 18u] != 0xFFFFFFFFu) { // the cave image of the biome (see proc_biome.cpp) uint cw = atab[rec + 19u] & 0xFFFFu, chh = atab[rec + 19u] >> 16; float px = (X * 0.49162514 + 2560.0) * 0.1, py = (2560.0 * 6.86035959282328e-07 + Y * 0.49162514) * 0.1; v = cave_sample(atab[rec + 18u], int(cw), int(chh), px, py); if (1.0 > v) v = v + simplex70(px, py) * ((1.0 - v) * 0.495); if (0.0 >= v) return 0.0; } v = proc_gradient(w, rec) * v; float s8 = 0.0, s18 = 0.0, s1c = 0.0; float mulp = tf(rec, 9); uint fl = atab[rec]; if (v > 0.51 && mulp != 0.0) { s18 = (v - 0.5) * 2.0; s8 = inside_perlin(rec, X, Y); } if (v > 0.85) { s1c = tf(rec, 7); float nz; if (((fl >> 8) & 15u) == 3u) { // SIN_CAPPED_SIMPLEX float amp = iq_noise(X * 0.02, Y * 0.021) * 0.05 + 0.05; float t1 = sin(Y * 0.0009765625) * 1024.0 * 0.5 * 0.06 * amp, t2 = cos(X * 0.0009765625) * 1024.0 * 0.5 * 0.06 * amp; nz = float(simplex70(t2, t1)) * v; } else { // the default noise type float amp = iq_noise2(X * 0.025, Y * 0.025) * 0.05 + 0.05; nz = simplex2(X * 0.01 * amp, Y * 0.01 * amp) * v; } v = v + (nz - v) * ((v - 0.85) / 0.1); } if ((fl & 32u) != 0u) s8 = ((v * s8) - s8) * (Y * 0.001) + s8; return mulp * s18 * s8 + tf(rec, 8) * v + s1c; } // Probe mode (shift_mode 2, docs: the teleport destination test): the world cells listed in probe[] get their cell word: from the live window when inside it // (flag 4, flag 2 when the cell is falling: ext.x > 0), else generated as the window would generate them (flag 1 = an area is loaded there). layout(std430, set = 0, binding = 53) buffer Probe { uvec4 phead; ivec4 probe[]; }; bool g_unloaded = false; uint gen_cell(ivec2 w) { uint hh = hash3(uint(w.x), uint(w.y), 77u); uint var = (hh >> 8) & 0xFFu; // LoadPixelScene images override the terrain (the scenes of every loaded area: a scene may reach into a neighbour) bool forceSolid = false; int whiteTile = -1; // the biome-map tile whose biome the white pixels of the scene use (the scene's origin) for (int a = 0; a < int(atab[0]); ++a) { int si = scene_index(a, w); if (si < 0) continue; uint sv = scene[si]; if (sv >= 0xE000u) { forceSolid = true; whiteTile = sv == 0xFFFFu ? -1 : int(sv - 0xE000u); break; } if (sv != 0u) { return (sv == 1u ? 0u : ((sv - 1u) | (var << 16))); } } const int ai = area_of_cell(w); if (ai < 0) { g_unloaded = true; return uint(pc.m_rock) | (var << 16); } // no area loaded here if (forceSolid) { // a white scene pixel: solid, the material from the of the biome with the wang value 1 float xw = float(w.x), yw = float(w.y); float n3 = value2(xw * 0.035, yw * 0.07) * 15.5; float s = simplex2((xw + n3) * 0.0489275, (yw + n3) * 0.0489275); const float wv = max(0.5, s * (0.0625 * 5.35 * 0.95) + 1.0); uint fm; const uint wrec = whiteTile >= 0 ? tile_rec(whiteTile % int(atab[2]), whiteTile / int(atab[2])) : 0xFFFFFFFFu; if (wrec != 0xFFFFFFFFu && atab[wrec + 22u] != 0u) fm = eval_materials_at(int(atab[wrec + 21u]), int(atab[wrec + 22u]), wv, w); // the biome of the scene origin else fm = eval_materials(ai, wv, w); return fm == 0u ? 0u : (fm | (var << 16)); } { // a procedural biome (no wang template): the value function, then the list (FUN_0087d0e0, type 0) const uint trec = topo_rec(w); if (trec != 0xFFFFFFFFu && (atab[trec] & 1u) != 0u) { const uint pm = eval_materials(ai, proc_value(w, trec), w); return pm == 0u ? 0u : (pm | (var << 16)); } } gpw = arec(ai, 2); gph = arec(ai, 3); gfo = arec(ai, 4); gmo = arec(ai, 5); ivec2 o = w - ivec2(arec(ai, 0), arec(ai, 1)); // cell relative to the centre of wang pixel (0,0) float px = (float(o.x) + 0.5) * 0.1, py = (float(o.y) + 0.5) * 0.1; if (py < -0.5) { return 0u; } // open sky above the area // the last wang pixel is 5 cells short of the right / bottom edge of the area's tiles: those cells continue the edge pixel (a 13 cell wall of rock stood there) px = clamp(px, -0.5, float(gpw) - 0.5001); py = clamp(py, -0.5, float(gph) - 0.5001); float warp = 1.0, thr = 0.5; int m0 = material_pixel(px, py); if (m0 > 0) { vec2 mp = mparams[m0]; warp = mp.x; thr = mp.y; } float qx = px, qy = py; if (warp > 0.0) { float xf = float(o.x), yf = float(o.y); float n1 = simplex2(xf * 0.13715, yf * 0.13717); float a = n1 * 0.45 + 0.1; float xs = xf * 0.11111111, ys = yf * 0.11111111; float n2 = perlin2(ys, xs); float t = ((1.0 - a) * 0.33 + 0.111) * warp; float n3 = value2(xs, ys); qx = px + n3 * (a * warp); qy = py + n2 * t; } float f = bilinear(qx, qy); if (f < thr) { return 0u; } int m1 = material_pixel(qx, qy); uint mat; if (m1 > 0) { mat = uint(m1); } else { // the biome's : FUN_0087d010 value, then the first matching component (none = air) float xw = float(w.x), yw = float(w.y); float n = value2(xw * 0.035, yw * 0.07); float n3 = n * 15.5; float h = (f - 0.5) * 0.5; float s = simplex2((xw + n3) * 0.0489275, (yw + n3) * 0.0489275); float v = max(0.5, s * (h * h * 5.35 * 0.95) + f); mat = eval_materials(ai, v, w); } return mat == 0u ? 0u : (mat | (var << 16)); } // The world rules of the cells that enter the window: the header in the tables before the 4 colour-grading words (main.cpp kWorldRules): [0] EVERYTHING_TO_GOLD, // [1] gold, [2] gold_static; the cuts in binding 55: [0] count, then from word 4, 12 words per GameCutThroughWorldVertical (x, y_min, y_max, radius, edge, the engine's // loaded region at the call: x0, y0, x1, y1, seed), every cut of the run. A generated cell (one that never was in the window) inside a cut's region takes the cut (007392d0: off = floor(3 sin(0.02 y) // +- 0.5), air for left < x <= right, the static stainable cells within edge darkened by (1 - sqrt(d / edge)) x (0.4 + 0.3 rand), as the burn level of bits 28-30); // with EVERYTHING_TO_GOLD every non-air cell is gold (the static ones gold_static), as the engine's redefined materials (00704f70) const uint kWorldRules = 4u; layout(std430, set = 0, binding = 55) readonly buffer CutRules { uint cuts[]; }; uint cut_hash(uint a, uint b) { uint h = a * 0x9E3779B1u ^ (b + 0x7F4A7C15u); h ^= h >> 15; h *= 0x2C1B3C6Du; h ^= h >> 12; h *= 0x297A2D39u; h ^= h >> 15; return h; } uint rules_word(uint c, ivec2 w, bool fresh) { const uint wb = uint(perm.length()) - 4u - kWorldRules; if (fresh) { const uint n = min(cuts[0], (uint(cuts.length()) - 4u) / 12u); for (uint k = 0u; k < n; ++k) { const uint b = 4u + 12u * k; const int x = int(cuts[b]), y0 = int(cuts[b + 1u]), y1 = int(cuts[b + 2u]), r = int(cuts[b + 3u]), edge = int(cuts[b + 4u]); if (w.y < y0 || w.y >= y1 || w.x < x - r - edge || w.x >= x + r + edge) continue; if (w.x < int(cuts[b + 5u]) || w.y < int(cuts[b + 6u]) || w.x >= int(cuts[b + 7u]) || w.y >= int(cuts[b + 8u])) continue; const float v = sin(float(w.y) * 0.02) * 3.0; const int off = int(floor(v + (v < 0.0 ? -0.5 : 0.5))); const int left = x - r + off, right = x + r + off; if (w.x > left && w.x <= right) { c = 0u; continue; } const uint m = cell_mat(c); if (edge <= 0 || m == 0u || (mflags(m) & F_STATIC) == 0u || (mflags(m) & F_NOSTAIN) != 0u || (c & BURN_BIT) != 0u) continue; const float rnd = 0.4 + 0.3 * float(cut_hash(uint(w.y), cuts[b + 9u]) & 0xFFFFu) / 65536.0; const float d = float(w.x <= left ? left - w.x : w.x - right); const float s = max(0.0, (1.0 - sqrt(d / float(edge))) * rnd); const float keep = (1.0 - float((c >> 28) & 7u) / 7.0) * (1.0 - s); const uint L = uint(clamp(int(round((1.0 - keep) * 7.0)), 0, 7)); c = (c & ~(7u << 28)) | (L << 28); } } if (perm[wb] != 0u) { const uint m = cell_mat(c); if (m != 0u && m != perm[wb + 1u] && m != perm[wb + 2u]) c = ((mflags(m) & F_STATIC) != 0u ? perm[wb + 2u] : perm[wb + 1u]) | (c & (0xFFu << 16)); } return c; } // The spell test room (main.cpp --spelltest; cut rules words 1..3): every generated cell is rock, but the hall (x0 <= x < x1, y0 <= y < y1) is air inside 16 cells of // temple brick uint spell_room(uint c, ivec2 w) { if (cuts[1] == 0u) return c; const ivec2 a = ivec2(int(cuts[2] << 16) >> 16, int(cuts[2]) >> 16), b = ivec2(int(cuts[3] << 16) >> 16, int(cuts[3]) >> 16); const uint var = (cut_hash(uint(w.x), uint(w.y)) >> 8) & 0xFFu; if (w.x >= a.x && w.x < b.x && w.y >= a.y && w.y < b.y) return 0u; if (w.x >= a.x - 16 && w.x < b.x + 16 && w.y >= a.y - 16 && w.y < b.y + 16) return (cuts[1] >> 16) | (var << 16); return (cuts[1] & 0xFFFFu) | (var << 16); } void world_rules(int i, ivec2 w, bool fresh) { const uint c = rules_word(cells[i], w, fresh); if (c != cells[i]) { cells[i] = c; ext[i] = vec2(0.0); } } void main() { if (pc.shift_mode == 3) { // far blocks (far.glsl): job z generates its block's cells into its slot - a store-backed block keeps the cells the store had const int j = int(gl_GlobalInvocationID.z); if (j >= farmap[kFarJobCount]) return; const int slot = farmap[kFarJobList + 4 * j], bx = farmap[kFarJobList + 4 * j + 1], by = farmap[kFarJobList + 4 * j + 2], kept = farmap[kFarJobList + 4 * j + 3]; const ivec2 w = ivec2(bx * 64, by * 64) + ivec2(gl_GlobalInvocationID.xy); const int fi = slot * 4096 + (w.y & 63) * 64 + (w.x & 63); if (kept != 0 && farcells[fi] != 0xFFFFFFFFu) return; farcells[fi] = rules_word(spell_room(gen_cell(w), w), w, true); return; } if (pc.shift_mode == 2) { const uint q = gl_GlobalInvocationID.y * gl_NumWorkGroups.x * 8u + gl_GlobalInvocationID.x; if (q >= phead.x) return; const ivec2 w = probe[q].xy; const ivec2 lp = w - ivec2(pc.view_x, pc.view_y); if (lp.x >= 0 && lp.y >= 0 && lp.x < pc.gw && lp.y < pc.gh) { const int li = lp.y * pc.gw + lp.x; probe[q].z = int(cells[li]); probe[q].w = 1 | 4 | (ext[li].x > 0.0 ? 2 : 0); return; } g_unloaded = false; probe[q].z = int(rules_word(spell_room(gen_cell(w), w), w, true)); probe[q].w = g_unloaded ? 0 : 1; return; } ivec2 p = ivec2(gl_GlobalInvocationID.xy); if (p.x >= pc.gw || p.y >= pc.gh) return; int i = p.y * pc.gw + p.x; if (pc.shift_mode == 1) { // scrolling: reuse the simulated overlap, generate only the exposed strips ivec2 src = p + ivec2(pc.shift_x, pc.shift_y); if (src.x >= 0 && src.y >= 0 && src.x < pc.gw && src.y < pc.gh) { terrain_age[i] = terrain_age_old[src.y * pc.gw + src.x]; cells[i] = cells_old[src.y * pc.gw + src.x]; ext[i] = ext_old[src.y * pc.gw + src.x]; birth[i] = birth_old[src.y * pc.gw + src.x]; // a cell that stays keeps its age (0070d1e0: the age belongs to the cell) return; } } birth[i] = birth_frame(); terrain_age[i] = 0u; if (ovr[i] != 0xFFFFFFFFu) { // a cell that comes back from the store (only its word is kept): a burning cell or a flame gets the fade a new cell of its material has // (0070f450: the material's temperature_of_fire), so a fire that went out of the window goes on when it comes back instead of dying at its next update const uint oc = ovr[i]; cells[i] = oc; ext[i] = vec2(0.0); const uint om = cell_mat(oc); if (om != 0u && ((oc & BURN_BIT) != 0u || (mflags(om) & F_FIRE) != 0u)) birth[i] = with_fade(birth_frame(), int((mats[3u * om + 2u].w >> 8) & 255u)); world_rules(i, p + ivec2(pc.view_x, pc.view_y), false); return; } ivec2 w = p + ivec2(pc.view_x, pc.view_y); // world cell ext[i] = vec2(0.0); cells[i] = spell_room(gen_cell(w), w); world_rules(i, w, true); }