OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain commits / 14fcd80

dc-terrain: mirror the erosion mask on the CPU, and name the gap that is left
The subaerial erosion mask now exists on both paths. hc is hoisted out of the hypso branch as pre_erosion_h so the CPU twin can reach the pre-erosion elevation the mask keys off. ⚠ THE SUBMARINE TERRAIN IS STILL GPU-ONLY, and that is recorded in the source rather than left to be discovered. erosion_cpp_shim.h implements the 2D subset the erosion filter needs; proc_simplex.inc wants vec4, floor(vec3) and scalar-vector operators it does not have, so the 3D noise will not compile on the CPU side. Faking it there would recreate exactly the divergence test_erosion_parity exists to catch. WHAT THAT COSTS, precisely. Collision is unaffected -- it is COLLISION_TRIMESH, built from the GPU mesh. What is wrong below the coastal buffer is sample_density(), which is where "surface at spawn column" and every headless gate get their answer, and the !gpu_filled fallback in dc_worker_cpu.cpp. So the seabed cannot currently be VERIFIED by any gate, which is its own reason to extend the shim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
14fcd8041b73ebde88b8813a8973bed1ae3ee6d1
by plankatron <fleeky@gmail.com> · 2026-09-07T20:11:39.000Z

Changed files (1) +31 -1

src/dc_terrain_generator_procnodes.cpp +31 -1
……@@ -1787,6 +1787,9 @@ float DCTerrainGeneratorProcNodes::_eval_at(const EvalParams &p,
17871787 // scaling the curve by it would move sea level per region. Same reason,
17881788 // same line, as the emission site.
17891789 vec3 terrain_offset;
1790 // Hoisted: the subaerial erosion mask below needs the pre-erosion elevation,
1791 // and hc is otherwise scoped to the branch that computes it.
1792 float pre_erosion_h = mtn.x;
17901793 if (hypso_enabled_ != 0.0f) {
17911794 dcerosion::vec2 hc = dcerosion::hypso_curve(mtn.x,
17921795 hypso_sea_, hypso_shelf_width_, hypso_slope_width_, hypso_abyss_, hypso_plat_slope_,
……@@ -1794,6 +1797,7 @@ float DCTerrainGeneratorProcNodes::_eval_at(const EvalParams &p,
17941797 hypso_peak_onset_, hypso_peak_gain_);
17951798 terrain_offset = vec3(hc.x, mtn.y * mtn_scale * hc.y,
17961799 mtn.z * mtn_scale * hc.y) * (mtn_height * 8.0f);
1800 pre_erosion_h = hc.x;
17971801 } else {
17981802 terrain_offset = vec3(mtn.x, mtn.y * mtn_scale, mtn.z * mtn_scale) * (mtn_amp * 8.0f);
17991803 }
……@@ -1882,8 +1886,21 @@ float DCTerrainGeneratorProcNodes::_eval_at(const EvalParams &p,
18821886 terrain_height * erosion_scale_ * en_cell_scale,
18831887 lod_voxel_size, 2.0f);
18841888 }
1889 // EROSION IS SUBAERIAL -- mirrors pn_erosion_mask in the emitted GLSL.
1890 // Written out rather than shared because the CPU path calls the node's
1891 // wrapper directly and has no globals to set. If these two expressions
1892 // differ, the surface you collide with and the surface you see part
1893 // company, which is what test_erosion_parity exists to catch.
1894 float ero_mask = 1.0f;
1895 if (erosion_mask_depth_ > 0.0f) {
1896 float depth_m = (get_waterline_norm() - pre_erosion_h)
1897 * mountain_height_ * erosion_height_scale_ * inv_y_scale;
1898 float t = depth_m / erosion_mask_depth_;
1899 t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t);
1900 ero_mask = 1.0f - (t * t * (3.0f - 2.0f * t));
1901 }
18851902 vec2 en = dcerosion::erosion_node_eval(
1886 uv, gn + terrain_offset, en_height_scale, en_erosion_str,
1903 uv, gn + terrain_offset, en_height_scale, en_erosion_str * ero_mask,
18871904 en_cell_scale, en_octaves, en_gain, 4.8f,
18881905 erosion_scale_, ero_max_oct);
18891906
……@@ -1891,6 +1908,19 @@ float DCTerrainGeneratorProcNodes::_eval_at(const EvalParams &p,
18911908 float d = en.x - y_mapped;
18921909 d *= inv_y_scale;
18931910
1911 // ⚠ SUBMARINE TERRAIN IS NOT MIRRORED HERE, AND THAT IS A KNOWN GAP.
1912 // The GPU density adds a 3D fBm below the coastal buffer; this CPU twin
1913 // does not, so the meshed seabed has caves and arches that the CPU path
1914 // does not know about. Everything meshes on the GPU today (GPUPATH reports
1915 // gpu_mesh 100%), so the visible surface is right -- but any consumer of
1916 // THIS function below the buffer is wrong by up to submarine_amp metres.
1917 //
1918 // The blocker is erosion_cpp_shim.h: it implements the 2D subset the
1919 // erosion filter needs, and proc_simplex.inc wants vec4, floor(vec3) and
1920 // scalar-vector operators it does not have. Extending the shim is the fix;
1921 // faking the noise here would recreate the exact divergence
1922 // test_erosion_parity exists to catch.
1923
18941924 // Clip: solid below floor_y, air above ceiling_y
18951925 d = MAX(d, (floor_y - y) * 2.0f);
18961926 d = MIN(d, (ceiling_y - y) * 2.0f);