extends SceneTree
## GATE — our Geiss density graph, as REBUILT 2026-10-06 (undersea_geiss_graph.gd header:
## masks -> pre-warp -> ground + hard floor -> shelf -> continuous terraces -> ridges ->
## eight octaves). The previous checks tested the replaced design (coast mask, a 10-100 m
## random floor staircase, `warp` / `floor_strength` knobs) and no longer parsed.
## U1 the shipped graph (Position) compiles to one GLSL proc_density
## U2 the bridged scene's graph compiles to proc_density and labels its G term
## U3 CONTINUOUS: along y the field never jumps (the old sawtooth floors did, and
## meshed as floating razor sheets) — |Δd| over 1 cm steps stays small
## U4 air far above the ground plane, solid far below; every value finite
## U5 G == d + p_rel.y exactly (the field without its flat ground plane)
## U6 prewarp = 0 -> wp == p_rel (no warp); octave_gain = 0 -> d loses exactly oct
## U7 detail_of() cuts exactly G (vs the builder's DETAIL mode); the scene's term
## compiles
## U8 the KNOBS are labelled nodes that survive a save, and a tuned one changes d
## U9 install_detail() gives a real DCTerrainGeneratorProcNodes the scene's term
## Sampled on the CPU through evaluate_socket on a graph built by the SAME builder with
## a constant Vector in place of Position.
##
## Run: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_undersea_geiss_graph.gd
var ok := true
var checks := 0
func _p(c: bool, m: String, got: Variant = "") -> void:
checks += 1
print(("ok " if c else "FAIL: ") + m + ("" if str(got) == "" else " (" + str(got) + ")"))
if not c:
ok = false
func _init() -> void:
_run()
print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
quit(0 if ok else 1)
## A sampler: the builder with a constant Vector; move it with at().
class Sampler:
var b := UnderseaGeissGraph.new()
var ids: Dictionary
func _init(knobs: Dictionary = {}, mode: String = UnderseaGeissGraph.FULL) -> void:
ids = b.build(ProcNodeGraph.new(), Vector3.ZERO, knobs, mode)
func at(p: Vector3, what: String = "out", sock: int = 0) -> float:
b.g.get_node(ids["p"]).set("value", p)
return float(b.g.evaluate_socket(ids[what], sock))
func _run() -> void:
# ---- U1 ----
var shipped := UnderseaGeissGraph.make()
var glsl: String = shipped.compile_to_glsl_function("proc_density", false, PackedStringArray())
_p(glsl.contains("proc_density") and glsl.length() > 500, "U1 the graph compiles to proc_density", glsl.length())
# ---- U2 ----
var saved := UnderseaGeissGraph.load_graph()
_p(saved != null, "U2 %s holds a graph at %s" % [UnderseaGeissGraph.SCENE, UnderseaGeissGraph.HOLDER])
if saved != null:
var g2: String = saved.compile_to_glsl_function("proc_density", false, PackedStringArray())
_p(g2.contains("float proc_density") and g2.length() > 500, "U2 the scene's graph compiles to proc_density", g2.length())
var has_detail := false
for i in saved.get_node_count():
var n: ProcNode = saved.get_node(i)
if n != null and String(n.get("label")) == UnderseaGeissGraph.DETAIL_LABEL:
has_detail = true
_p(has_detail, "U2 the scene's graph labels its Geiss term '%s'" % UnderseaGeissGraph.DETAIL_LABEL)
var s := Sampler.new()
var rng := RandomNumberGenerator.new()
rng.seed = 7
# ---- U3 ----
var jump := 0.0
var jump_at := Vector3.ZERO
for c in 6:
var x := rng.randf_range(-400, 400)
var z := rng.randf_range(-400, 400)
var prev := s.at(Vector3(x, -60.0, z))
var y := -60.0
while y < 60.0:
y += 0.01
var v := s.at(Vector3(x, y, z))
if absf(v - prev) > jump:
jump = absf(v - prev)
jump_at = Vector3(x, y, z)
prev = v
_p(jump < 0.5, "U3 continuous along y: largest change over 1 cm %.3f (a terrace step is >= terrace_str 3)" % jump, jump_at)
# ---- U4 ----
var air := 0
var solid := 0
var nonfinite := 0
for i in 60:
var x2 := rng.randf_range(-900, 900)
var z2 := rng.randf_range(-900, 900)
var up := s.at(Vector3(x2, 300.0, z2))
var dn := s.at(Vector3(x2, -300.0, z2))
if is_nan(up) or is_inf(up) or is_nan(dn) or is_inf(dn):
nonfinite += 1
if up < 0.0:
air += 1
if dn > 0.0:
solid += 1
_p(nonfinite == 0 and air == 60 and solid == 60, "U4 air 300 m above / solid 300 m below the ground plane, all finite",
"air %d solid %d nonfinite %d" % [air, solid, nonfinite])
# ---- U5 ----
var gworst := 0.0
for i in 40:
var p := Vector3(rng.randf_range(-900, 900), rng.randf_range(-80.0, 80.0), rng.randf_range(-900, 900))
gworst = maxf(gworst, absf(s.at(p, "G") - (s.at(p) + p.y - float(UnderseaGeissGraph.KNOBS["ground_y"]))))
_p(gworst < 1e-3, "U5 G == d + p_rel.y", gworst)
# ---- U6 ----
var nowarp := Sampler.new({"prewarp": 0.0})
var wworst := 0.0
for i in 20:
var p := Vector3(rng.randf_range(-900, 900), rng.randf_range(-80.0, 80.0), rng.randf_range(-900, 900))
nowarp.b.g.get_node(nowarp.ids["p"]).set("value", p)
var wp: Vector3 = nowarp.b.g.evaluate_socket(nowarp.ids["wp"], 0)
wworst = maxf(wworst, wp.distance_to(p)) # ground_y 0: p_rel == p
_p(wworst < 1e-3, "U6 prewarp = 0: the warped coordinate is p itself", wworst)
var nooct := Sampler.new({"octave_gain": 0.0})
var oworst := 0.0
for i in 20:
var p := Vector3(rng.randf_range(-900, 900), rng.randf_range(-80.0, 80.0), rng.randf_range(-900, 900))
oworst = maxf(oworst, absf(nooct.at(p) - (s.at(p) - s.at(p, "oct"))))
_p(oworst < 1e-3, "U6 octave_gain = 0 removes exactly the octave term", oworst)
# ---- U7 ----
var dg := UnderseaGeissGraph.make(null, {}, UnderseaGeissGraph.DETAIL)
var dglsl: String = dg.compile_to_glsl_function("undersea_detail", false, PackedStringArray())
_p(dglsl.contains("float undersea_detail"), "U7 the detail graph compiles", dglsl.length())
var sdet := UnderseaGeissGraph.detail_of(saved)
_p(sdet != null and sdet.compile_to_glsl_function("undersea_detail", false, PackedStringArray()).contains("float undersea_detail"),
"U7 detail_of(the scene's graph) compiles to undersea_detail")
var det := Sampler.new({}, UnderseaGeissGraph.DETAIL)
# detail_of on a SAMPLER's full graph: the copy keeps node indices (the bridge JSON
# round trip is exact), so the constant Vector is still ids["p"].
var cut := UnderseaGeissGraph.detail_of(s.b.g)
var cworst := 0.0
for i in 40:
var p := Vector3(rng.randf_range(-900, 900), rng.randf_range(-80.0, 80.0), rng.randf_range(-900, 900))
cut.get_node(s.ids["p"]).set("value", p)
det.b.g.get_node(det.ids["p"]).set("value", p)
cworst = maxf(cworst, absf(float(cut.evaluate_socket(_output_source(cut), 0)) - det.at(p)))
_p(cut != null and cworst < 1e-4, "U7 detail_of cuts exactly G (vs the builder's DETAIL graph)", cworst)
# ---- U8 ----
var shipped_knobs := UnderseaGeissGraph.knobs_from(saved)
var missing: Array = []
for key: String in UnderseaGeissGraph.KNOBS:
if not shipped_knobs.has(key):
missing.append(key)
_p(missing.is_empty(), "U8 the scene's graph carries every knob as a labelled node (%d)" % shipped_knobs.size(), missing)
var tuned := UnderseaGeissGraph.make(null, {"shelf_strength": 3.0})
var tmp := "user://gate_undersea_knobs.tres"
ResourceSaver.save(tuned, tmp)
var back := UnderseaGeissGraph.knobs_for(load(tmp) as ProcNodeGraph)
_p(is_equal_approx(float(back["shelf_strength"]), 3.0) and is_equal_approx(float(back["prewarp"]), float(UnderseaGeissGraph.KNOBS["prewarp"])),
"U8 a tuned knob saved and read back is the tuned value; the rest are unchanged", back["shelf_strength"])
var ts := Sampler.new({"shelf_strength": 3.0})
var diff := 0
for i in 20:
var p := Vector3(rng.randf_range(-900, 900), rng.randf_range(-6.0, 4.0), rng.randf_range(-900, 900))
if absf(ts.at(p) - s.at(p)) > 0.01:
diff += 1
_p(diff > 2, "U8 the tuned knob changes the field in the shelf band (%d / 20 samples)" % diff)
DirAccess.remove_absolute(ProjectSettings.globalize_path(tmp))
# ---- U9 ----
var gen: Object = ClassDB.instantiate("DCTerrainGeneratorProcNodes")
_p(gen != null, "U9 DCTerrainGeneratorProcNodes is registered")
if gen != null:
# A main graph first, as the maps do: the generator compiles the undersea term
# only as part of its own shader, and only once that shader compiled. Any
# GPU-compilable graph serves (graphs/terrain.tres is NOT one: SlopeGradient
# has no GPU path), so the scene's own graph stands in.
gen.call("set_graph", saved)
var why := UnderseaGeissGraph.install_detail(gen)
_p(why == "" and gen.call("get_undersea_graph") != null, "U9 install_detail gives the generator the scene's term", why)
## The node feeding a graph's Group Output (socket 0) — what a density evaluates.
func _output_source(g: ProcNodeGraph) -> int:
var go := -1
for i in g.get_node_count():
if g.get_node(i) is GroupOutputNode:
go = i
var c := g.get_connections_flat()
for i in range(0, c.size(), 4):
if c[i + 2] == go and c[i + 3] == 0:
return c[i]
return -1