// GENERATED by hyper-tps/tools/gen_geiss_dc_glsl.py -- DO NOT EDIT.
// Mirror of shaders/generators/geiss_sdf.glsl, embedded because
// DCGenerator::gpu_shader_source() returns a const char* and the generator
// has no shader-directory resolution of its own.
#ifndef GEISS_SDF_GLSL_H
#define GEISS_SDF_GLSL_H
static const char* GEISS_SDF_GLSL = R"GEISSGLSL(
// GENERATED by hyper-tps/tools/gen_geiss_dc_glsl.py from
// hyper-tps/shaders/geiss_density.gdshaderinc -- DO NOT EDIT.
//
// GPU Gems 3 ch.1 (Geiss) density, as a dc-terrain generator body.
//
// Provides: float evaluate_density(float x, float y, float z)
//
// Requires the host shader to declare the eight noise volumes by NAME
// (noiseVol0..3, packedNoiseVol0..3). The binding NUMBERS differ between the
// two shaders this is injected into -- 14..21 in edge_intersect.glsl, 15..22 in
// the density header, where binding 14 is already custom_data_tex -- which is
// safe precisely because this body names them and never numbers them.
//
// Sign convention matches: Geiss's "positive values are inside the terrain" is
// dc-terrain's "positive = solid".
// GPU Gems 3 ch.1 (Geiss), ported verbatim from
// gamedev/gpu-gems-3/GPU Gems 3 code/content/01/demo/shaders/{density,sampleNoise}.h
// sampling the demo's own 16^3 noise volumes.
//
// SHARED so the volume BAKE and anything that evaluates the field directly
// cannot drift apart -- the article's own pipeline builds a density volume
// once (1_build_density_vol.psh) and everything downstream samples that.
const float NOISE_LATTICE_SIZE = 16.0;
const float INV_LATTICE_SIZE = 1.0 / 16.0;
const float prewarp_str = 25.0;
// The terrain cannot leave this vertical slab; everything outside is sky.
// ── sampleNoise.h ───────────────────────────────────────────────────────────
vec4 NLQu(vec3 uvw, sampler3D t) { return textureLod(t, uvw, 0.0); }
vec4 NLQs(vec3 uvw, sampler3D t) { return NLQu(uvw, t) * 2.0 - 1.0; }
vec3 nmq_warp(vec3 uvw) {
vec3 t = fract(uvw * NOISE_LATTICE_SIZE + 0.5);
vec3 t2 = (3.0 - 2.0 * t) * t * t;
return uvw + (t2 - t) * INV_LATTICE_SIZE;
}
vec4 NMQu(vec3 uvw, sampler3D t) { return NLQu(nmq_warp(uvw), t); }
vec4 NMQs(vec3 uvw, sampler3D t) { return NLQs(nmq_warp(uvw), t); }
// Two-sample manual trilinear against the PACKED volumes, per the article:
// .yzw carry the +Y, +Z and +YZ neighbours of .x.
float NHQu(vec3 uvw, sampler3D tex, float smooth_it) {
vec3 uvw2 = floor(uvw * NOISE_LATTICE_SIZE) * INV_LATTICE_SIZE;
vec3 t = (uvw - uvw2) * NOISE_LATTICE_SIZE;
t = mix(t, t * t * (3.0 - 2.0 * t), smooth_it);
vec2 d = vec2(INV_LATTICE_SIZE, 0.0);
// ⚠ .xzyw, NOT the .zxyw that sampleNoise.h line 86 writes.
//
// _about_the_noise_volumes.txt and a channel-by-channel check of the shipped
// files both confirm packed = (R base, G +Y, B +Z, A +YZ). The manual
// trilinear below interpolates X, then Y, then Z, so it needs the tuple
// ordered (base, +Z, +Y, +YZ) -- which is .xzyw. Verified numerically
// against the 8-tap reference in the #if 0 block of the original: .xzyw
// reproduces it EXACTLY (max err 0.000000 over 20k points) while .zxyw
// gives max err 0.907 / mean 0.083.
//
// NHQs feeds the whole 25-unit prewarp and the amplitude-32 octave, so this
// was corrupting the two largest contributions to the terrain.
vec4 f1 = textureLod(tex, uvw2, 0.0).xzyw;
vec4 f2 = textureLod(tex, uvw2 + d.xyy, 0.0).xzyw;
vec4 f3 = mix(f1, f2, t.xxxx);
vec2 f4 = mix(f3.xy, f3.zw, t.yy);
return mix(f4.x, f4.y, t.z);
}
float NHQs(vec3 uvw, sampler3D tex, float smooth_it) { return NHQu(uvw, tex, smooth_it) * 2.0 - 1.0; }
float smooth_snap(float t, float m) {
float c = (t > 0.5) ? 1.0 : 0.0;
float s = 1.0 - c * 2.0;
return c + s * pow((c + s * t) * 2.0, m) * 0.5;
}
// octaveMat0..7 are uniforms. main.nma ships them as identity, but that is only
// a placeholder: TerrainDemo::Randomize() (TerrainDemo.cpp:2588) overwrites every
// one with a random Euler-XYZ rotation in [-pi, pi], and Init.cpp:364 calls it
// during startup -- after the constructor has already done srand(timeGetTime())
// (TerrainDemo.cpp:670).
//
// So the demo draws a DIFFERENT terrain on every launch, and there is no single
// canonical Geiss terrain to match; the article's figures are one draw. Any
// fixed rotation set is therefore exactly as faithful as any other. These two
// are one such draw, generated the way Randomize() does it.
//
// Only c6 and c7 are consumed by DENSITY, so only these two matter. rot() uses
// the upper-left 3x3 only, so Randomize()'s translation is deliberately absent.
const mat3 OCTAVE_MAT6 = mat3(vec3( 0.534965, -0.143815, 0.832544),
vec3( 0.228474, 0.973317, 0.021323),
vec3(-0.813395, 0.178808, 0.553548));
const mat3 OCTAVE_MAT7 = mat3(vec3(-0.361212, 0.871481, 0.331733),
vec3( 0.932478, 0.338770, 0.125376),
vec3(-0.003119, 0.354621, -0.935005));
vec3 rot6(vec3 p) { return OCTAVE_MAT6 * p; }
vec3 rot7(vec3 p) { return OCTAVE_MAT7 * p; }
// ── density.h ───────────────────────────────────────────────────────────────
// The article's own EVAL_CHEAP path: it drops the highest-frequency octaves
// ("skipped for long-range ambo"), which is exactly the right trade for the
// MARCH -- they cannot move the crossing by more than their amplitude. The
// full function is still used for the final normal.
float DENSITY_impl(vec3 ws, bool cheap) {
vec3 ws_orig = ws;
float density = 0.0;
vec4 uulf_rand = clamp(NMQu(ws * 0.000718, noiseVol0) * 2.0 - 0.5, 0.0, 1.0);
vec4 uulf_rand2 = NMQu(ws * 0.000632, noiseVol1);
vec4 uulf_rand3 = NMQu(ws * 0.000695, noiseVol2);
// PRE-WARP. 25 units of domain warping -- the single biggest source of the
// overhangs in the article's figures, and the thing our own attempt had
// none of.
vec3 ulf_rand;
ulf_rand.x = NHQs(ws * 0.0041 * 0.971, packedNoiseVol2, 1.0) * 0.64
+ NHQs(ws * 0.0041 * 0.461, packedNoiseVol3, 1.0) * 0.32;
ulf_rand.y = NHQs(ws * 0.0041 * 0.997, packedNoiseVol1, 1.0) * 0.64
+ NHQs(ws * 0.0041 * 0.453, packedNoiseVol0, 1.0) * 0.32;
ulf_rand.z = NHQs(ws * 0.0041 * 1.032, packedNoiseVol3, 1.0) * 0.64
+ NHQs(ws * 0.0041 * 0.511, packedNoiseVol2, 1.0) * 0.32;
ws += ulf_rand * prewarp_str * clamp(uulf_rand3.x * 1.4 - 0.3, 0.0, 1.0);
vec3 c6 = rot6(ws);
vec3 c7 = rot7(ws);
// MAIN SHAPE: general ground plane, gradient 1.0 per unit.
density = -ws.y;
density += clamp((-4.0 - ws_orig.y * 0.3) * 3.0, 0.0, 1.0) * 40.0 * uulf_rand2.z;
// CRUSTY SHELF -- smooth tops, crumbly eroded undersides.
float shelf_thickness_y = 2.5;
float shelf_pos_y = -1.0;
float shelf_strength = 9.5;
density = mix(density, shelf_strength,
0.83 * clamp(shelf_thickness_y - abs(ws.y - shelf_pos_y), 0.0, 1.0)
* clamp(uulf_rand.y * 1.5 - 0.5, 0.0, 1.0));
// FLAT TERRACES -- and the overhang term, explicitly.
{
float terraces_can_warp = 0.5 * uulf_rand2.y;
float terrace_freq_y = 0.13;
float terrace_str = 3.0 * clamp(uulf_rand.z * 2.0 - 1.0, 0.0, 1.0);
float overhang_str = 1.0 * clamp(uulf_rand.z * 2.0 - 1.0, 0.0, 1.0);
float fy = -mix(ws_orig.y, ws.y, terraces_can_warp) * terrace_freq_y;
float orig_t = fract(fy);
float t = smooth_snap(orig_t, 16.0);
fy = floor(fy) + t;
density += fy * terrace_str;
density += (t - orig_t) * overhang_str;
}
// Repeating ridges on the warped Y coord -- note the 27x anisotropy.
density += NLQs(ws * vec3(2.0, 27.0, 2.0) * 0.0037, noiseVol0).x * 2.0
* clamp(uulf_rand2.w * 2.0 - 1.0, 0.0, 1.0);
// Nine octaves. Amplitude * frequency is ~0.05 for every one of them, so
// they sum to ~0.41 against a base gradient of 1.0 -- the noise ALONE
// cannot fold the surface. The folds come from the warp and the terms above.
float HFM = cheap ? 0.0 : 1.0;
density +=
NLQs(ws * 0.1600 * 1.021, noiseVol1).x * 0.32 * 1.16 * HFM
+ NLQs(ws * 0.0800 * 0.985, noiseVol2).x * 0.64 * 1.12 * HFM
+ NLQs(ws * 0.0400 * 1.051, noiseVol0).x * 1.28 * 1.08 * HFM
+ NLQs(ws * 0.0200 * 1.020, noiseVol1).x * 2.56 * 1.04
+ NLQs(ws * 0.0100 * 0.968, noiseVol3).x * 5.0
+ NMQs(ws * 0.0050 * 0.994, noiseVol0).x * 10.0
+ NMQs(c6 * 0.0025 * 1.045, noiseVol2).x * 20.0 * 0.9
+ NHQs(c7 * 0.0012 * 0.972, packedNoiseVol3, 1.0) * 40.0 * 0.8;
// density.h ends with `density -= wsChunkSize.x*0.009`, an LOD bias that
// shrinks coarse chunks so finer ones enclose them. There are no chunks
// here, so it is deliberately omitted.
return density;
}
// Godot's shading language has no forward declarations, so these live after the
// definition rather than before it.
float DENSITY(vec3 ws) { return DENSITY_impl(ws, false); }
float DENSITY_CHEAP(vec3 ws) { return DENSITY_impl(ws, true); }
// dc-terrain calls the field with loose floats.
float evaluate_density(float x, float y, float z) {
return DENSITY(vec3(x, y, z));
}
)GEISSGLSL";
#endif // GEISS_SDF_GLSL_H