#ifndef DC_SHADER_SOURCE_H
#define DC_SHADER_SOURCE_H
// =========================================================================
// The density compute shader's GLSL source, as plain C strings.
//
// WHY THIS IS ITS OWN FILE, and godot-free.
//
// It used to live in dc_compute.cpp, which pulls in godot_cpp for
// RDShaderSource / RenderingDevice. Nothing about the SOURCE needs Godot —
// only its COMPILATION does — but living in that translation unit meant the
// only way to run these shaders was to boot the engine. So the GPU pipeline
// had no oracle: every question about it (per-stage cost, occupancy, whether
// the mesh stage and the density stage agree) could only be answered by
// launching the game and looking, which is why the one performance audit we
// have (docs/plans/2026-03-24-gpu-pipeline-optimization-clas-spec.md, "the
// GPU is 99% idle") records a conclusion and no method.
//
// THE INVARIANT THIS FILE EXISTS TO HOLD:
//
// The oracle and the engine compile the SAME STRING.
//
// Not a copy kept in step by hand. A GPU oracle with its own GLSL certifies a
// shader the game does not run, which is the gated-oracle failure hyper-games
// #43 §6 paid four rounds for — a test whose subject is not the shipping
// artifact proves only that the test is self-consistent. Anything that needs
// this source includes this header; there is one copy and it is this one.
//
// Assembly is HEADER + <body> + MAIN, where <body> defines
// float evaluate_density(float x, float y, float z)
// and is either DENSITY_GLSL_BODY (built-in terrain) or a custom generator's
// (proc-nodes). HEADER fixes the buffer layout and MAIN the dispatch logic, so
// a custom body cannot change either — that is the whole point of the split.
// =========================================================================
#include <string>
// -------------------------------------------------------------------------
// Embedded GLSL shader source — split into HEADER / BODY / MAIN
// so custom shaders (proc-nodes) can replace the body while keeping
// the same buffer layout and dispatch logic.
// -------------------------------------------------------------------------
// HEADER: version, layouts, uniform blocks.
// Custom shaders are sandwiched between HEADER and MAIN.
static const char* DENSITY_GLSL_HEADER = R"(
#version 450
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
layout(set = 0, binding = 0, std430) buffer DensityBuf { float densities[]; };
layout(set = 0, binding = 1, std430) buffer NormalXBuf { float normal_x[]; };
layout(set = 0, binding = 2, std430) buffer NormalYBuf { float normal_y[]; };
layout(set = 0, binding = 3, std430) buffer NormalZBuf { float normal_z[]; };
layout(set = 0, binding = 4, std140) uniform Params {
int grid_stride;
float voxel_scale;
float origin_x;
float origin_y;
float origin_z;
float primary_frequency;
float primary_strength;
float tunnel_frequency;
float tunnel_strength;
float tunnel_radius;
float ridge_frequency;
float ridge_strength;
float warp_frequency;
float warp_strength;
float detail_frequency;
float detail_strength;
float ground_bias;
float solid_floor_depth;
float sky_height;
float flat_center_radius;
float floor_strength;
float floor_height_min;
float floor_height_max;
float room_height;
float terrain_size;
int primary_octaves;
int tunnel_octaves;
int ridge_octaves;
int warp_octaves;
int detail_octaves;
int compute_normals;
int seed;
int voronoi_cell_count;
int use_noise;
int spherical;
float planet_radius;
float planet_center_x;
float planet_center_y;
float planet_center_z;
float island_radius;
int flags; // bit0 = bake brick, bit1 = sample brick
};
#define FLAG_BAKE 1
#define FLAG_BRICK 2
layout(set = 0, binding = 5, std430) buffer VoronoiBuf {
int voronoi_count;
float voronoi_data[];
};
layout(set = 0, binding = 6, std430) buffer PermBuf {
int perm_tables[];
};
struct GpuEdit {
uint edit_type; // 0=sphere_sub,1=sphere_add,2=box_sub,3=box_add,4=mesh_sub,5=mesh_add,6=sphere_hard_sub,7=sphere_hard_add
float pos_x, pos_y, pos_z;
float half_x, half_y, half_z; // mesh: half_x = scale
float radius;
float strength;
uint _pad0, _pad1, _pad2;
};
layout(set = 0, binding = 7, std430) readonly buffer EditBuf {
uint edit_count;
uint _edit_pad0, _edit_pad1, _edit_pad2;
GpuEdit gpu_edits[];
};
// --- Mesh SDF BVH structures (bindings 8-10) ---
struct GpuBVHNode {
float aabb_min_x, aabb_min_y, aabb_min_z;
uint left_or_start; // interior: left child, leaf: tri_start
float aabb_max_x, aabb_max_y, aabb_max_z;
uint count; // 0 = interior (right = left+1), >0 = leaf tri_count
};
struct GpuTriangle {
float v0x, v0y, v0z;
float v1x, v1y, v1z;
float v2x, v2y, v2z;
float fnx, fny, fnz;
float en0x, en0y, en0z;
float en1x, en1y, en1z;
float en2x, en2y, en2z;
float vn0x, vn0y, vn0z;
float vn1x, vn1y, vn1z;
float vn2x, vn2y, vn2z;
float _tri_pad0, _tri_pad1;
};
layout(set = 0, binding = 8, std430) readonly buffer MeshBVHBuf {
uint mesh_node_count;
uint _mbvh_pad0, _mbvh_pad1, _mbvh_pad2;
GpuBVHNode mesh_nodes[];
};
layout(set = 0, binding = 9, std430) readonly buffer MeshTriBuf {
GpuTriangle mesh_triangles[];
};
layout(set = 0, binding = 10, std430) readonly buffer MeshTriIdxBuf {
uint mesh_tri_indices[];
};
// Settled-edit SDF delta brick (binding 11). Chunk-local grid at the density
// stride (grid_stride). Trilinearly sampled so it agrees with the mesh stage's
// fractional evals (noise_sdf.glsl apply_brick_delta — keep identical). Written
// in bake mode (FLAG_BAKE), read in sample mode (FLAG_BRICK) — NOT readonly.
// Zero buffer / FLAG_BRICK unset => exact no-op.
layout(set = 0, binding = 11, std430) buffer BrickBuf {
float brick_delta[];
};
// Convex-brush half-space planes (binding 12). A CONVEX edit (etype 8/9)
// references a contiguous run [ _pad1, _pad1+_pad2 ) of vec4(nx,ny,nz,d);
// inside the brush = (n·p <= d). MUST match edge_intersect.glsl binding 11.
layout(set = 0, binding = 12, std430) readonly buffer ConvexPlaneBuf {
vec4 convex_planes[];
};
// --- Baked custom data (hyper-games #50 section 5.3) ---
// A terrain-diffusion block, elevation in METRES, row-major, row = world Z.
// The generator emits custom_data_height_m() around this with the block's geometry
// baked in as literals, so no Params field is needed and the std140 layout --
// which is declared here, in edge_intersect.glsl and in C++, and desynchronises
// SILENTLY -- is untouched. Unbound, this is a one-float placeholder that
// nothing references.
layout(set = 0, binding = 13, std430) readonly buffer CustomDataBuf {
float custom_data[];
};
// binding 14: THE SAME DATA AS A TEXTURE.
//
// Declared unconditionally because a uniform set must match the shader's
// bindings exactly; whether it is READ is chosen by HBLK_USE_ATLAS in the
// emitted sampler. Carrying both lets the two be compared rather than the swap
// being taken on faith.
//
// Linear index i maps to (i % HBLK_ATLAS_W, i / HBLK_ATLAS_W) — a transport
// swap, not a layout change, so both hold identical values at identical
// indices. The generator emits HBLK_ATLAS_W beside the block constants.
layout(set = 0, binding = 14, r32f) uniform readonly image2D custom_data_tex;
// GENERATOR_SAMPLER_BINDINGS
// Bindings 15..22 (eight noise volumes) are injected HERE, and only when the
// generator body samples them -- see _build_custom_shader. NOTE the numbers
// differ from edge_intersect.glsl (14..21): binding 14 is already custom_data_tex
// on this side. The body refers to the volumes BY NAME, so the two shaders need
// not agree on numbering, only on the names.
// --- Mesh SDF helper functions ---
float aabb_point_dist_sq(vec3 p, vec3 bmin, vec3 bmax) {
vec3 d = max(bmin - p, vec3(0.0)) + max(p - bmax, vec3(0.0));
return dot(d, d);
}
// Ericson closest-point + Baerentzen pseudonormal sign.
// Returns signed squared distance: positive = outside, negative = inside.
float point_tri_dist_sq(vec3 P, GpuTriangle tri, out vec3 normal) {
vec3 A = vec3(tri.v0x, tri.v0y, tri.v0z);
vec3 B = vec3(tri.v1x, tri.v1y, tri.v1z);
vec3 C = vec3(tri.v2x, tri.v2y, tri.v2z);
vec3 AB = B - A, AC = C - A, AP = P - A;
float d1 = dot(AB, AP), d2 = dot(AC, AP);
if (d1 <= 0.0 && d2 <= 0.0) {
normal = vec3(tri.vn0x, tri.vn0y, tri.vn0z);
vec3 diff = P - A; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
vec3 BP = P - B;
float d3 = dot(AB, BP), d4 = dot(AC, BP);
if (d3 >= 0.0 && d4 <= d3) {
normal = vec3(tri.vn1x, tri.vn1y, tri.vn1z);
vec3 diff = P - B; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
float vc = d1 * d4 - d3 * d2;
if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) {
float v = d1 / (d1 - d3);
vec3 closest = A + AB * v;
normal = vec3(tri.en0x, tri.en0y, tri.en0z);
vec3 diff = P - closest; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
vec3 CP = P - C;
float d5 = dot(AB, CP), d6 = dot(AC, CP);
if (d6 >= 0.0 && d5 <= d6) {
normal = vec3(tri.vn2x, tri.vn2y, tri.vn2z);
vec3 diff = P - C; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
float vb = d5 * d2 - d1 * d6;
if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) {
float w = d2 / (d2 - d6);
vec3 closest = A + AC * w;
normal = vec3(tri.en2x, tri.en2y, tri.en2z);
vec3 diff = P - closest; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
float va = d3 * d6 - d5 * d4;
if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) {
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
vec3 closest = B + (C - B) * w;
normal = vec3(tri.en1x, tri.en1y, tri.en1z);
vec3 diff = P - closest; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
float denom = 1.0 / (va + vb + vc);
float v = vb * denom, w = vc * denom;
vec3 closest = A + AB * v + AC * w;
normal = vec3(tri.fnx, tri.fny, tri.fnz);
vec3 diff = P - closest; float sd = dot(diff, normal);
return (sd >= 0.0) ? dot(diff, diff) : -dot(diff, diff);
}
// BVH traversal: returns signed distance (positive=outside, negative=inside)
float bvh_mesh_sdf(vec3 p) {
if (mesh_node_count == 0u) return 1e10;
float best_dsq = 1e30;
vec3 best_n = vec3(0, 1, 0);
int stack[32];
int sp = 0;
stack[sp++] = 0;
while (sp > 0) {
int ni = stack[--sp];
GpuBVHNode node = mesh_nodes[ni];
vec3 bmin = vec3(node.aabb_min_x, node.aabb_min_y, node.aabb_min_z);
vec3 bmax = vec3(node.aabb_max_x, node.aabb_max_y, node.aabb_max_z);
float adsq = aabb_point_dist_sq(p, bmin, bmax);
if (adsq > abs(best_dsq)) continue;
if (node.count > 0u) {
for (uint i = 0u; i < node.count; i++) {
uint ti = mesh_tri_indices[node.left_or_start + i];
vec3 n;
float dsq = point_tri_dist_sq(p, mesh_triangles[ti], n);
if (abs(dsq) < abs(best_dsq)) {
best_dsq = dsq;
best_n = n;
}
}
} else {
uint left = node.left_or_start;
uint right = left + 1u;
vec3 lmin = vec3(mesh_nodes[left].aabb_min_x, mesh_nodes[left].aabb_min_y, mesh_nodes[left].aabb_min_z);
vec3 lmax = vec3(mesh_nodes[left].aabb_max_x, mesh_nodes[left].aabb_max_y, mesh_nodes[left].aabb_max_z);
vec3 rmin = vec3(mesh_nodes[right].aabb_min_x, mesh_nodes[right].aabb_min_y, mesh_nodes[right].aabb_min_z);
vec3 rmax = vec3(mesh_nodes[right].aabb_max_x, mesh_nodes[right].aabb_max_y, mesh_nodes[right].aabb_max_z);
float dl = aabb_point_dist_sq(p, lmin, lmax);
float dr = aabb_point_dist_sq(p, rmin, rmax);
// Push far child first (popped last = traversed last)
if (dl < dr) {
if (dr < abs(best_dsq)) stack[sp++] = int(right);
if (dl < abs(best_dsq)) stack[sp++] = int(left);
} else {
if (dl < abs(best_dsq)) stack[sp++] = int(left);
if (dr < abs(best_dsq)) stack[sp++] = int(right);
}
}
}
float sign = (best_dsq >= 0.0) ? 1.0 : -1.0;
return sqrt(abs(best_dsq)) * sign;
}
// --- Settled-edit brick (trilinear; MUST match noise_sdf.glsl) ---
float apply_brick_delta(float wx, float wy, float wz) {
int stride = grid_stride;
vec3 lv = (vec3(wx, wy, wz) - vec3(origin_x, origin_y, origin_z)) / voxel_scale;
lv = clamp(lv, vec3(0.0), vec3(float(stride - 1) - 1e-4));
ivec3 i0 = ivec3(floor(lv));
vec3 f = lv - vec3(i0);
int sx = stride, sxy = stride * stride;
int b = i0.z * sxy + i0.y * sx + i0.x;
float c00 = mix(brick_delta[b], brick_delta[b + 1], f.x);
float c10 = mix(brick_delta[b + sx], brick_delta[b + sx + 1], f.x);
float c01 = mix(brick_delta[b + sxy], brick_delta[b + sxy + 1], f.x);
float c11 = mix(brick_delta[b + sxy + sx], brick_delta[b + sxy + sx + 1], f.x);
return mix(mix(c00, c10, f.y), mix(c01, c11, f.y), f.z);
}
// --- Edit application ---
float apply_gpu_edits(float d, float wx, float wy, float wz) {
for (uint i = 0u; i < edit_count; i++) {
GpuEdit e = gpu_edits[i];
uint etype = e.edit_type;
// Bounding sphere early reject
float edx = wx - e.pos_x;
float edy = wy - e.pos_y;
float edz = wz - e.pos_z;
float dist_sq = edx*edx + edy*edy + edz*edz;
if (dist_sq > e.radius * e.radius) continue;
if (etype <= 1u) {
// Sphere: smooth quadratic falloff
float r_sq = e.radius * e.radius;
float t = 1.0 - dist_sq / r_sq;
d += e.strength * t * t;
} else if (etype <= 3u) {
// Box: SDF-based CSG
float dx = abs(wx - e.pos_x) - e.half_x;
float dy = abs(wy - e.pos_y) - e.half_y;
float dz = abs(wz - e.pos_z) - e.half_z;
float outside = sqrt(
max(dx, 0.0) * max(dx, 0.0) +
max(dy, 0.0) * max(dy, 0.0) +
max(dz, 0.0) * max(dz, 0.0));
float inside = min(max(max(dx, dy), dz), 0.0);
float box_sdf = outside + inside;
if (e.strength < 0.0) {
d = min(d, box_sdf);
} else {
d = max(d, -box_sdf);
}
} else if (etype == 6u || etype == 7u) {
// Hard sphere CSG: SDF = dist - radius (neg inside, pos outside —
// same convention as box_sdf). Union/difference by strength sign.
float sphere_sdf = sqrt(dist_sq) - e.radius;
if (e.strength < 0.0) {
d = min(d, sphere_sdf);
} else {
d = max(d, -sphere_sdf);
}
} else if (etype == 8u || etype == 9u) {
// Convex brush = intersection of half-spaces: f = max_i(n_i·p - d_i)
// (neg inside, pos outside — same convention as box_sdf). Planes live
// in [_pad1, _pad1+_pad2) of convex_planes[]. Mirror box CSG exactly.
uint poff = e._pad1;
uint pcnt = e._pad2;
float f = -1e30;
for (uint pi = 0u; pi < pcnt; pi++) {
vec4 pl = convex_planes[poff + pi];
f = max(f, pl.x * wx + pl.y * wy + pl.z * wz - pl.w);
}
if (e.strength < 0.0) {
d = min(d, f);
} else {
d = max(d, -f);
}
} else {
// Mesh SDF: transform to local space, query BVH
float scale = e.half_x; // scale stored in half_x for mesh edits
if (scale <= 0.0) continue;
vec3 local_p = vec3(edx, edy, edz) / scale;
float mesh_d = bvh_mesh_sdf(local_p);
// mesh_d: positive = outside, negative = inside (pseudonormal convention)
// Same CSG as CPU: mesh_sdf = -mesh_d, then min/max
float mesh_sdf = -mesh_d;
if (e.strength < 0.0) {
d = min(d, mesh_sdf);
} else {
d = max(d, -mesh_sdf);
}
}
}
return d;
}
)";
// MAIN: dispatch logic that calls evaluate_density().
// Custom shaders must define: float evaluate_density(float x, float y, float z)
static const char* DENSITY_GLSL_MAIN = R"(
void main() {
uvec3 gid = gl_GlobalInvocationID;
int stride = grid_stride;
if (int(gid.x) >= stride || int(gid.y) >= stride || int(gid.z) >= stride)
return;
int idx = int(gid.z) * stride * stride + int(gid.y) * stride + int(gid.x);
float wx = origin_x + float(gid.x) * voxel_scale;
float wy = origin_y + float(gid.y) * voxel_scale;
float wz = origin_z + float(gid.z) * voxel_scale;
float base_d = evaluate_density(wx, wy, wz);
// BAKE mode: write the settled-edit delta (applied - base) into the brick.
// Uses this shader's own evaluate_density so base cancels exactly at sample
// time (no CPU/GPU noise-match dependency). No density/normal output.
if ((flags & FLAG_BAKE) != 0) {
float applied = base_d;
if (edit_count > 0u) applied = apply_gpu_edits(base_d, wx, wy, wz);
brick_delta[idx] = applied - base_d;
return;
}
float d = base_d;
if ((flags & FLAG_BRICK) != 0) d += apply_brick_delta(wx, wy, wz); // settled brick before HOT edits
if (edit_count > 0u) {
d = apply_gpu_edits(d, wx, wy, wz);
}
densities[idx] = d;
if (compute_normals != 0) {
float h = voxel_scale * 2.0;
float d0 = evaluate_density(wx + h, wy - h, wz - h);
float d1 = evaluate_density(wx - h, wy - h, wz + h);
float d2 = evaluate_density(wx - h, wy + h, wz - h);
float d3 = evaluate_density(wx + h, wy + h, wz + h);
if ((flags & FLAG_BRICK) != 0) {
d0 += apply_brick_delta(wx + h, wy - h, wz - h);
d1 += apply_brick_delta(wx - h, wy - h, wz + h);
d2 += apply_brick_delta(wx - h, wy + h, wz - h);
d3 += apply_brick_delta(wx + h, wy + h, wz + h);
}
if (edit_count > 0u) {
d0 = apply_gpu_edits(d0, wx + h, wy - h, wz - h);
d1 = apply_gpu_edits(d1, wx - h, wy - h, wz + h);
d2 = apply_gpu_edits(d2, wx - h, wy + h, wz - h);
d3 = apply_gpu_edits(d3, wx + h, wy + h, wz + h);
}
float nx = -(d0 - d1 - d2 + d3);
float ny = -(-d0 - d1 + d2 + d3);
float nz = -(-d0 + d1 - d2 + d3);
float len = sqrt(nx * nx + ny * ny + nz * nz);
if (len > 1e-10) { nx /= len; ny /= len; nz /= len; }
normal_x[idx] = nx;
normal_y[idx] = ny;
normal_z[idx] = nz;
}
}
)";
// BODY: built-in noise functions + evaluate_density for default terrain.
// This is the part that gets replaced by custom shaders.
static const char* DENSITY_GLSL_BODY = R"(
#define PERM_PRIMARY 0
#define PERM_TUNNEL_A 512
#define PERM_TUNNEL_B 1024
#define PERM_RIDGE 1536
#define PERM_WARP 2048
#define PERM_DETAIL 2560
int perm(int table_offset, int idx) {
return perm_tables[table_offset + (idx & 255)];
}
int fastfloor(float x) {
int xi = int(x);
return (x < float(xi)) ? xi - 1 : xi;
}
float grad3(int hash, float x, float y, float z) {
int h = hash & 15;
float u = (h < 8) ? x : y;
float v = (h < 4) ? y : ((h == 12 || h == 14) ? x : z);
return (((h & 1) != 0) ? -u : u) + (((h & 2) != 0) ? -v : v);
}
float contrib(int table_offset, int ii, int jj, int kk, float x, float y, float z) {
float t = 0.6 - x * x - y * y - z * z;
if (t < 0.0) return 0.0;
t *= t;
int gi = perm(table_offset, ii + perm(table_offset, jj + perm(table_offset, kk)));
return t * t * grad3(gi, x, y, z);
}
float noise3(int table_offset, float x, float y, float z) {
const float F3 = 1.0 / 3.0;
const float G3 = 1.0 / 6.0;
float s = (x + y + z) * F3;
int i = fastfloor(x + s);
int j = fastfloor(y + s);
int k = fastfloor(z + s);
float t = float(i + j + k) * G3;
float X0 = float(i) - t;
float Y0 = float(j) - t;
float Z0 = float(k) - t;
float x0 = x - X0;
float y0 = y - Y0;
float z0 = z - Z0;
int i1, j1, k1, i2, j2, k2;
if (x0 >= y0) {
if (y0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; }
else if (x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; }
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; }
} else {
if (y0 < z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; }
else if (x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; }
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; }
}
float x1 = x0 - float(i1) + G3;
float y1 = y0 - float(j1) + G3;
float z1 = z0 - float(k1) + G3;
float x2 = x0 - float(i2) + 2.0 * G3;
float y2 = y0 - float(j2) + 2.0 * G3;
float z2 = z0 - float(k2) + 2.0 * G3;
float x3 = x0 - 1.0 + 3.0 * G3;
float y3 = y0 - 1.0 + 3.0 * G3;
float z3 = z0 - 1.0 + 3.0 * G3;
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
float n0 = contrib(table_offset, ii, jj, kk, x0, y0, z0);
float n1 = contrib(table_offset, ii+i1, jj+j1, kk+k1, x1, y1, z1);
float n2 = contrib(table_offset, ii+i2, jj+j2, kk+k2, x2, y2, z2);
float n3 = contrib(table_offset, ii+1, jj+1, kk+1, x3, y3, z3);
return 32.0 * (n0 + n1 + n2 + n3);
}
float fbm3(int table_offset, float x, float y, float z, int octaves) {
float sum = 0.0;
float amp = 1.0;
float freq = 1.0;
float max_amp = 0.0;
for (int o = 0; o < octaves; o++) {
sum += noise3(table_offset, x * freq, y * freq, z * freq) * amp;
max_amp += amp;
amp *= 0.5;
freq *= 2.0;
}
return sum / max_amp;
}
float sample_noise(int table_offset, float freq, float px, float py, float pz, int oct) {
return fbm3(table_offset, px * freq, py * freq, pz * freq, oct);
}
float clampf(float v, float lo, float hi) {
return clamp(v, lo, hi);
}
float smoothstep_manual(float t) {
return t * t * (3.0 - 2.0 * t);
}
// Hash-based procedural Voronoi (infinite tiling)
uint hash2d(int ix, int iz, int seed_offset) {
uint h = uint(ix * 374761393 + iz * 668265263 + seed_offset);
h = (h ^ (h >> 13u)) * 1274126177u;
h = h ^ (h >> 16u);
return h;
}
float hash_float(int ix, int iz, int seed_offset) {
return float(hash2d(ix, iz, seed_offset) & 0xFFFFFFu) / 16777216.0;
}
float evaluate_density(float x, float y, float z) {
float wx = x + sample_noise(PERM_WARP, warp_frequency, x, y, z, warp_octaves) * warp_strength;
float wy = y + sample_noise(PERM_WARP, warp_frequency, x + 31.7, y + 47.3, z + 89.1, warp_octaves) * warp_strength;
float wz = z + sample_noise(PERM_WARP, warp_frequency, x + 73.1, y + 11.9, z + 59.7, warp_octaves) * warp_strength;
float d = -y * ground_bias;
d += sample_noise(PERM_PRIMARY, primary_frequency, wx, wy, wz, primary_octaves) * primary_strength;
float ta = sample_noise(PERM_TUNNEL_A, tunnel_frequency, wx, wy * 0.7, wz, tunnel_octaves);
float tb = sample_noise(PERM_TUNNEL_B, tunnel_frequency, wx + 200.0, wy * 0.7, wz + 200.0, tunnel_octaves);
float tube_dist_sq = ta * ta + tb * tb;
float radius_sq = tunnel_radius * tunnel_radius;
float tube_carve = max(1.0 - tube_dist_sq / radius_sq, 0.0);
d -= tube_carve * tunnel_strength;
float ridge_raw = sample_noise(PERM_RIDGE, ridge_frequency, wx, wy, wz, ridge_octaves);
// Smooth-abs ridge — IDENTICAL to CPU DCDensity::evaluate_internal and the
// mesh-stage noise_sdf.glsl. The old 1.0 - abs(ridge_raw) had a V-kink at
// ridge_raw=0 that diverged from the carved/meshed surface (Finding 3).
float ridge_eps = 0.08;
float ridged = 1.0 - sqrt(ridge_raw * ridge_raw + ridge_eps * ridge_eps);
ridged = max(ridged, 0.0);
ridged = ridged * ridged;
d += ridged * ridge_strength;
d += sample_noise(PERM_DETAIL, detail_frequency, wx, wy, wz, detail_octaves) * detail_strength;
if (floor_strength > 0.0) {
float cell_spacing = terrain_size / float(voronoi_cell_count);
int gx = int(floor(x / cell_spacing));
int gz = int(floor(z / cell_spacing));
int seed_base = seed + 100;
float nearest_dist_sq = 3.402823e+38;
float nearest_floor_h = 0.0;
float second_dist_sq = 3.402823e+38;
for (int dzi = -1; dzi <= 1; dzi++) {
for (int dxi = -1; dxi <= 1; dxi++) {
int cxi = gx + dxi;
int czi = gz + dzi;
float jx = hash_float(cxi, czi, seed_base);
float jz = hash_float(cxi, czi, seed_base + 7);
float cell_x = (float(cxi) + 0.2 + jx * 0.6) * cell_spacing;
float cell_z = (float(czi) + 0.2 + jz * 0.6) * cell_spacing;
float fh = hash_float(cxi, czi, seed_base + 13);
float flr_h = floor_height_min + fh * (floor_height_max - floor_height_min);
float ddx = x - cell_x;
float ddz = z - cell_z;
float vd = ddx * ddx + ddz * ddz;
if (vd < nearest_dist_sq) {
second_dist_sq = nearest_dist_sq;
nearest_dist_sq = vd;
nearest_floor_h = flr_h;
} else if (vd < second_dist_sq) {
second_dist_sq = vd;
}
}
}
float edge_ratio = 1.0 - nearest_dist_sq / (second_dist_sq + 0.001);
float cell_blend = clampf(edge_ratio * 2.0, 0.0, 1.0);
cell_blend = smoothstep_manual(cell_blend);
float floor_dist = nearest_floor_h - y;
float floor_effect = 0.0;
if (floor_dist > 0.0) {
floor_effect = clampf(floor_dist * 0.5, 0.0, 2.0);
} else if (floor_dist > -1.5) {
floor_effect = floor_dist * 1.5;
}
float ceiling_h = nearest_floor_h + room_height;
float ceil_dist = y - ceiling_h;
float ceil_effect = 0.0;
if (ceil_dist > 0.0 && ceil_dist < 3.0) {
ceil_effect = -clampf(ceil_dist * 0.5, 0.0, 2.0);
}
d += (floor_effect + ceil_effect) * cell_blend * floor_strength;
}
float floor_blend = max((-y - solid_floor_depth) * 0.1, 0.0);
d += floor_blend;
float sky_blend = max((y - sky_height) * 0.1, 0.0);
d -= sky_blend;
// Crater system: spawn crater + random craters across the island.
// Ported verbatim from CPU DCDensity::evaluate_internal. Crater #0
// (radius = flat_center_radius) SUBSUMES the old standalone flat-center
// spawn block — do NOT keep both (double-flatten).
float crater_cx[6];
float crater_cz[6];
float crater_r[6];
int num_craters = 0;
crater_cx[num_craters] = 0.0;
crater_cz[num_craters] = 0.0;
crater_r[num_craters] = flat_center_radius;
num_craters++;
for (int ci = 0; ci < 5; ci++) {
float angle = hash_float(ci, seed, 200) * 6.2831853;
float cdistr = 40.0 + hash_float(ci, seed, 201) * (island_radius * 0.5);
float cr = 10.0 + hash_float(ci, seed, 202) * 20.0;
crater_cx[num_craters] = cos(angle) * cdistr;
crater_cz[num_craters] = sin(angle) * cdistr;
crater_r[num_craters] = cr;
num_craters++;
}
for (int ci = 0; ci < num_craters; ci++) {
float cdx = x - crater_cx[ci];
float cdz = z - crater_cz[ci];
float cdist = sqrt(cdx * cdx + cdz * cdz);
float cr = crater_r[ci];
float ctransition = cr * 0.5;
if (cdist < cr + ctransition) {
float blend = 1.0 - clampf(
(cdist - cr * 0.3) / (cr * 0.7 + ctransition),
0.0, 1.0);
blend = smoothstep_manual(blend);
float flat_d = -y * 0.8;
d = d + (flat_d - d) * blend;
float rim_start = cr * 0.7;
float rim_end = cr + ctransition * 0.5;
if (cdist > rim_start && cdist < rim_end) {
float rim_t = (cdist - rim_start) / (rim_end - rim_start);
rim_t = sin(rim_t * 3.14159);
float rim_height = cr * 0.2;
float rim_noise = fbm3(PERM_DETAIL,
x * detail_frequency * 2.0,
y * detail_frequency,
z * detail_frequency * 2.0, 2) * rim_height * 0.4;
float rim_pull = (rim_height + rim_noise) * rim_t;
d += max(0.0, (rim_pull - y) * 0.3) * rim_t;
}
}
}
return d;
}
)";
// Full default shader = HEADER + BODY + MAIN (assembled once at init)
static std::string _build_default_shader() {
return std::string(DENSITY_GLSL_HEADER) + DENSITY_GLSL_BODY + DENSITY_GLSL_MAIN;
}
// Wrap custom evaluate_density() function body in compute shader template
// True when a generator body samples the noise volumes, i.e. when the density
// shader must declare bindings 15..22 and DCCompute must supply descriptors for
// them. Declaring bindings the body never reads risks them being optimised out
// of the SPIR-V, after which uniform_set_create is handed descriptors the shader
// does not reflect and fails -- so absence has to agree too, not just presence.
static bool _body_uses_noise_volumes(const char* custom_body) {
return custom_body != nullptr
&& std::string(custom_body).find("noiseVol") != std::string::npos;
}
static std::string _build_custom_shader(const char* custom_body) {
std::string header(DENSITY_GLSL_HEADER);
const std::string marker = "// GENERATOR_SAMPLER_BINDINGS";
size_t pos = header.find(marker);
if (pos != std::string::npos) {
std::string decls;
if (_body_uses_noise_volumes(custom_body)) {
for (int i = 0; i < 4; i++) {
decls += "layout(set = 0, binding = " + std::to_string(15 + i)
+ ") uniform sampler3D noiseVol" + std::to_string(i) + ";\n";
}
for (int i = 0; i < 4; i++) {
decls += "layout(set = 0, binding = " + std::to_string(19 + i)
+ ") uniform sampler3D packedNoiseVol" + std::to_string(i) + ";\n";
}
}
header.replace(pos, marker.length(), decls);
}
return header + "\n" + custom_body + "\n" + DENSITY_GLSL_MAIN;
}
#endif // DC_SHADER_SOURCE_H