OverviewFilesCommitsIssuesDocsReleasesPeers

terrain-lookdev commits / c7e41fb

gate_dc_vs_lookdev: read the map's parameters instead of keeping a copy
The gate rendered its lookdev half at erosion_render's OWN defaults — strength 0.22, erosion_scale 0.15 — while the Godot half ran the map's 0.0057 / 0.0014. Two different terrains, reported as MATCH max |godot - lookdev| = 904.639 m FAIL which reads as the engine and the lookdev disagreeing about the terrain. They did not. The gate was comparing parameters, not implementations. Fixed by PARSING the constants out of map_dc_diffusion.gd — floor, ceiling, terrain height, gully band, depth, K, cell scale, octaves, gain — and mirroring the map's own two derivations. A gate that keeps a private copy of the numbers it is checking drifts from them silently, which is precisely the failure being fixed, so it now cannot hold one. With both halves current and the depth formula corrected: MATCH max |godot - lookdev| = 0.002 m rms 0.001 m PASS CONTROL erosion off: max diff 16.8 m REJECTED Two millimetres over a 1.8 km window between DCTerrain::sample_density and a standalone Vulkan compute shader that shares no path with it beyond erosion_filter.inc and the wrapper. And the control now DISCRIMINATES, which it could not before: at the old 0.227 m amplitude, erosion-on and erosion-off were the same field inside the 2 m tolerance, so the gate accepted anything. That blindness was the third independent sighting of the depth bug, after the renderer at map settings and amplitude_law.py. ⚠ Re-run BOTH halves. Editing the map and re-running only the python half compares a fresh lookdev against a stale /tmp/dc_surface.txt; that misread cost a cycle here and shows up as a MATCH failure of about the depth.
c7e41fb61a21c263fac9ded977a7ab585f31a508
by plankatron <fleeky@gmail.com> · 2026-09-02T13:58:25.000Z

Changed files (1) +49 -3

gate_dc_vs_lookdev.py +49 -3
……@@ -14,7 +14,7 @@ would read as success.
1414
1515 python3 gate_dc_vs_lookdev.py [--bin /tmp/erosion_render4]
1616"""
17import argparse, os, subprocess, sys
17import argparse, os, re, subprocess, sys
1818import numpy as np
1919
2020DC = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "dc-terrain"))
……@@ -23,6 +23,49 @@ HBLK = os.path.abspath(os.path.join(os.path.dirname(__file__), "..",
2323 "diffusion_gully_30km.hblk"))
2424
2525
26MAP_GD = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "hyper-tps",
27 "scenes", "maps", "map_dc_diffusion.gd"))
28
29
30def map_params():
31 """The MAP's parameters, READ FROM THE MAP.
32
33 The first version of this gate rendered the lookdev half at erosion_render's
34 OWN defaults -- strength 0.22, erosion_scale 0.15 -- while the Godot half ran
35 the map's 0.0057 / 0.0014. Two different terrains, reported as a 904 m
36 disagreement between engine and lookdev. It was neither.
37
38 So these are PARSED from map_dc_diffusion.gd rather than copied. A gate that
39 keeps its own copy of the numbers it is checking drifts from them silently,
40 which is the whole failure being fixed here.
41 """
42 src = open(MAP_GD, encoding="utf-8").read()
43
44 def const(name):
45 m = re.search(r"^const\s+" + name + r"\s*:=\s*(-?[\d.]+)", src, re.M)
46 if not m:
47 raise SystemExit(f"cannot read {name} from {MAP_GD}")
48 return float(m.group(1))
49
50 floor_y, ceil_y = const("FLOOR_Y"), const("CEILING_Y")
51 th, cell = const("TERRAIN_HEIGHT"), const("EROSION_CELL_SCALE")
52 gully, depth = const("EROSION_GULLY_METRES"), const("EROSION_DEPTH_METRES")
53 k = const("EROSION_AMPLITUDE_K")
54 hs, gain = const("EROSION_HEIGHT_SCALE"), const("EROSION_GAIN")
55 oct_ = const("EROSION_OCTAVES")
56 # The map's own two derivations, mirrored (map :602 and the erosion_scale line).
57 strength = depth * th * cell / (k * gully)
58 escale = gully / (th * cell)
59 vals = [("floor_y", floor_y), ("ceiling_y", ceil_y), ("terrain_height", th),
60 ("en_height_scale", hs), ("en_strength", strength),
61 ("en_erosion_scale", escale), ("en_cell_scale", cell),
62 ("en_octaves", int(oct_)), ("en_gain", gain)]
63 out = []
64 for kk, vv in vals:
65 out += ["--set", kk, str(vv)]
66 return out, depth, strength
67
68
2669def render(binpath, ox, oz, n, mpp, out, extra=()):
2770 cmd = [binpath, "--heightmap", HBLK, "--ox", str(ox), "--oz", str(oz),
2871 "--n", str(n), "--mpp", str(mpp), "--out", out] + list(extra)
……@@ -54,14 +97,17 @@ def main():
5497 print(f"Godot surface: {n}x{len(zs)} columns from ({xs[0]:.0f}, {zs[0]:.0f}) "
5598 f"at {mpp:.0f} m spacing")
5699
57 look = render(a.bin, xs[0], zs[0], n, mpp, "/tmp/lk_surface.f32")
100 mp, depth, strength = map_params()
101 print(f"lookdev half at the MAP's parameters: depth {depth:g} m rms, "
102 f"strength {strength:.6g}")
103 look = render(a.bin, xs[0], zs[0], n, mpp, "/tmp/lk_surface.f32", mp)
58104 d = np.abs(godot - look)
59105 ok = d.max() <= a.tol
60106 print(f"\n MATCH max |godot - lookdev| = {d.max():8.3f} m "
61107 f"rms {np.sqrt((d**2).mean()):7.3f} m {'PASS' if ok else 'FAIL'}")
62108
63109 zero = render(a.bin, xs[0], zs[0], n, mpp, "/tmp/lk_zero.f32",
64 ["--set", "en_strength", "0"])
110 mp + ["--set", "en_strength", "0"])
65111 dz = np.abs(godot - zero)
66112 ctl = dz.max() > a.tol
67113 print(f" CONTROL erosion off: max diff {dz.max():8.1f} m "