// Shared by terrain.comp and render.comp: the 70-scaled simplex noise, the biome-map topology lookup and the gradient of the procedural biomes (FUN_0087e8d0). // Needs: perm (binding 8), ffloor, ps, atab (areas.glsl) declared before the include. vec2 grad12(uint g) { const vec2 G[12] = vec2[12](vec2(1, 1), vec2(-1, 1), vec2(1, -1), vec2(-1, -1), vec2(1, 0), vec2(-1, 0), vec2(1, 0), vec2(-1, 0), vec2(0, 1), vec2(0, -1), vec2(0, 1), vec2(0, -1)); return G[g]; } float simplex70(float x, float y) { const float F2 = 0.36602540378, G2 = 0.21132486540; 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 n = 0.0; float t0 = 0.5 - x0 * x0 - y0 * y0; if (t0 >= 0.0) { t0 *= t0; n += t0 * t0 * dot(grad12(ps(ii + ps(jj)) % 12u), vec2(x0, y0)); } float t1 = 0.5 - x1 * x1 - y1 * y1; if (t1 >= 0.0) { t1 *= t1; n += t1 * t1 * dot(grad12(ps(ii + uint(i1) + ps(jj + uint(j1))) % 12u), vec2(x1, y1)); } float t2 = 0.5 - x2 * x2 - y2 * y2; if (t2 >= 0.0) { t2 *= t2; n += t2 * t2 * dot(grad12(ps(ii + 1u + ps(jj + 1u)) % 12u), vec2(x2, y2)); } return 70.0 * n; } float fbm70(float x, float y) { 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 = simplex70(x, y) * 0.5; float w = 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 += simplex70(x, y) * w; w *= 0.5; } return sum / 0.9375; } // The topology record (offset in atab) of a biome-map tile, 0xFFFFFFFF when there is none (FUN_0087d870: x wraps, y is clamped). uint tile_rec(int tx, int ty) { int W = int(atab[2]), H = int(atab[3]); tx = ((tx % W) + W) % W; ty = clamp(ty, 0, H - 1); return atab[atab[4] + uint(ty * W + tx)]; } // word 20 of the record: biome edge flags (bit 0 noise_biome_edges, bit 1 big_noise_biome_edges, bit 2 fat_biome_edges) bool bedge(uint rec, uint bit) { return (atab[rec + 20u] & bit) != 0u; } // FUN_0087d9a0: the biome-map tile whose biome a world cell belongs to. Within 42 cells of the edge of a tile whose neighbour is another biome, the lookup point is // displaced by noise (wavy biome borders): small (2.5 cells) unless both biomes have big_noise_biome_edges (default on): 11 * simplex + 30 * sin / cos. ivec2 border_tile(ivec2 w) { const int ix = w.x + 17920, iy = w.y + 7168; const int fx = ix & 511, fy = iy & 511; const int W = int(atab[2]), H = int(atab[3]); const int tx = ix >> 9, ty = clamp(iy >> 9, 0, H - 1); const ivec2 here = ivec2(tx, ty); const uint b = tile_rec(tx, ty); if (b == 0xFFFFFFFFu || !bedge(b, 1u) || bedge(b, 4u)) return here; ivec2 n = ivec2(-1000000); const bool l = fx <= 41, r = fx >= 471, t = fy <= 41, bt = fy >= 471; if (l && tile_rec(tx - 1, ty) != b) n = ivec2(tx - 1, ty); else if (t && tile_rec(tx, ty - 1) != b) n = ivec2(tx, ty - 1); else if (r && tile_rec(tx + 1, ty) != b) n = ivec2(tx + 1, ty); else if (bt && tile_rec(tx, ty + 1) != b) n = ivec2(tx, ty + 1); else if (l && t && tile_rec(tx - 1, ty - 1) != b) n = ivec2(tx - 1, ty - 1); else if (l && bt && tile_rec(tx - 1, ty + 1) != b) n = ivec2(tx - 1, ty + 1); else if (r && t && tile_rec(tx + 1, ty - 1) != b) n = ivec2(tx + 1, ty - 1); else if (r && bt && tile_rec(tx + 1, ty + 1) != b) n = ivec2(tx + 1, ty + 1); if (n.x == -1000000) return here; const uint nb = tile_rec(n.x, n.y); if (nb == 0xFFFFFFFFu || !bedge(nb, 1u)) return here; const float xf = float(ix), yf = float(iy); float xx, yy; if (!bedge(b, 2u) || !bedge(nb, 2u)) { const float d = simplex70(xf * 0.05, yf * 0.05) * 2.5; xx = xf + d; yy = yf + d; } else { const float n11 = simplex70(xf * 0.05, yf * 0.05) * 11.0; xx = xf + n11 + 30.0 * sin(yf * 0.005); yy = yf + n11 + 30.0 * cos(xf * 0.005); } const ivec2 t2 = ivec2(int(xx) >> 9, int(yy) >> 9); const uint r2 = tile_rec(t2.x, t2.y); // the displaced tile must have its area loaded: otherwise the cell would be placeholder rock (a column of rock stood in the sky at the edge of the loaded region) if (r2 != 0xFFFFFFFFu && bedge(r2, 1u) && t2.x >= 0 && t2.y >= 0 && t2.x < W && t2.y < H && atab[atab[1] + uint(t2.y) * atab[2] + uint(t2.x)] != 0u) return t2; return here; } uint topo_rec(ivec2 w) { // the topology record of the biome the world cell belongs to const ivec2 t = border_tile(w); return tile_rec(t.x, t.y); } int area_of_cell(ivec2 w) { // the loaded area of that biome, -1 when none const ivec2 t = border_tile(w); if (t.x < 0 || t.y < 0 || t.x >= int(atab[2]) || t.y >= int(atab[3])) return -1; return int(atab[atab[1] + uint(t.y) * atab[2] + uint(t.x)]) - 1; } float tf(uint rec, int k) { return uintBitsToFloat(atab[rec + uint(k)]); } vec2 grad_limits(uint rec, float X) { // FUN_0087eaf0: the start and end of the gradient at X float sY = tf(rec, 11), eY = tf(rec, 12); int mode = int((atab[rec] >> 16) & 15u); if (mode == 0) return vec2(sY, eY); if (mode == 3) { float d = (X - tf(rec, 13)) * tf(rec, 14); return vec2(sY + d, eY + d); } float xp = uintBitsToFloat(atab[6]) + X, sc = tf(rec, 15); // the noise offset of the world (header word 6) float s = (mode == 2 ? fbm70(xp * sc, sY * sc) : float(simplex70(xp * sc, sY * sc))) * tf(rec, 16) + sY; float e = float(simplex70(xp * sc, eY * sc)) * tf(rec, 17) + eY; return vec2(s, e); } float proc_gradient(ivec2 w, uint rec) { // FUN_0087e8d0: 0 above the start, 1 below the end, linear between; blended with the left neighbour within 41 cells of the tile edge float X = float(w.x), Y = float(w.y); vec2 L = grad_limits(rec, X); int fx = (w.x + 17920) & 511; if (Y <= 380.0 && fx <= 41) { ivec2 wl = ivec2(w.x - fx - 1, w.y); uint recL = topo_rec(wl); if (recL != 0xFFFFFFFFu && recL != rec) L = mix(L, grad_limits(recL, X), 1.0 - float(fx) / 41.0); } if (L.x >= Y) return 0.0; if (Y >= L.y || L.y == L.x) return 1.0; return (Y - L.x) / (L.y - L.x); }