extends SceneTree
## GATE — our undersea term inside the erosion / diffusion generator (§4 P3), on the
## REAL maps: their own DCTerrainGeneratorProcNodes, graphs and wrappers.
## E1 the undersea graph compiled (get_undersea_error() == "") and the combined
## density shader runs on the GPU — two graphs in one shader needs proc-nodes'
## guarded library globals (without them: 'pn_terrain_offset' : redefinition)
## E2 ABOVE the coast band (water_y - 10 m) the density is unchanged by it — the
## shoreline stays a single-valued heightfield
## E3 BELOW the band the term is present where there is sea; on a map with no sea
## (diffusion: water below its lowest terrain) it touches nothing
## Each map's GPU density is sampled with the graph, then with it removed
## (set_undersea_graph(null) recompiles the same generator), at the same points.
##
## ⚠ WINDOWED (needs a RenderingDevice). Run: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 DISPLAY=:0 "$HYPER_TPS_GODOT" --path . --script res://scenes/test/gate_undersea_erosion.gd
const MAPS := ["res://scenes/maps/map_dc_erosion.tscn", "res://scenes/maps/map_dc_diffusion.tscn"]
const CELLS := 6
var XZ: Array = [] # a 7x7 grid over ±40 km, filled in _init — enough to hit ocean on both maps
var ok := true
var checks := 0
var _frame := 0
var _i := 0
var _map: Node
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:
for i in 7:
for j in 7:
XZ.append(Vector2(-40000.0 + i * 13333.0, -40000.0 + j * 13333.0))
## Stages per map, the OFF samples frames later and TWICE (they must agree).
##
## ⛔ CORRECTED: this used to say a generator change reaches debug_gpu_density
## "asynchronously". It did not — DCGpuThread recompiled only when the shader
## source's POINTER changed, and a generator that rebuilt its GLSL string in place
## kept the same address, so the STALE shader answered (OFF == ON at points where the
## term is -35 m; later ON with the knob graphs). Fixed in dc-terrain (pointer OR
## text). E3 is what catches it: a stale baseline makes the term look absent.
const T_ON := 150
const T_OFF1 := 270
const T_OFF2 := 330
var _on: Dictionary = {}
var _off1: Dictionary = {}
func _process(_d: float) -> bool:
_frame += 1
if _frame == 1:
_load()
elif _frame == T_ON:
_stage_on()
elif _frame == T_OFF1:
_off1 = _sample()
elif _frame == T_OFF2:
_stage_off(_sample())
_map.queue_free()
_i += 1
if _i >= MAPS.size():
print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
quit(0 if ok else 1)
return true
_frame = 0
return false
func _load() -> void:
_map = (load(MAPS[_i]) as PackedScene).instantiate()
root.add_child(_map)
func _blocks(terrain: Node, y0: float, vox: float) -> Array:
var out: Array = []
for xz: Vector2 in XZ:
out.append(terrain.call("debug_gpu_density", Vector3(xz.x, y0, xz.y), vox, CELLS))
return out
func _sample() -> Dictionary:
var terrain: Node = _map.get_node("DCTerrain")
var water: float = float(_map.get("_gen").call("get_submarine_water_y"))
return {"shallow": _blocks(terrain, water - 10.0 - CELLS * 1.0, 1.0), # top at water-10
"deep": _blocks(terrain, water - 600.0, 100.0)}
func _name() -> String:
return String(MAPS[_i]).get_file()
func _stage_on() -> void:
var name := _name()
var gen: Object = _map.get("_gen")
_p(gen != null and _map.get_node_or_null("DCTerrain") != null, "%s: generator and terrain exist" % name)
if gen == null:
return
_p(String(gen.call("get_undersea_error")) == "", "E1 %s: the undersea graph compiled" % name,
String(gen.call("get_undersea_error")))
var glsl := String(gen.call("get_compiled_glsl"))
_p(glsl.contains("undersea_detail(vec3(x, y, z))"), "E1 %s: the map's density shader CALLS the undersea term" % name)
_on = _sample()
var ran := true
for b: PackedFloat32Array in (_on["shallow"] as Array) + (_on["deep"] as Array):
if b.size() != (CELLS + 1) * (CELLS + 1) * (CELLS + 1):
ran = false
_p(ran, "E1 %s: the combined density shader ran on the GPU (every block returned)" % name)
# THE BASELINE IS "NEITHER": the undersea graph REPLACES the submarine fBm, so
# removing the graph alone brings the fBm back — what E2 first measured (420 m):
# the old term, gated by COLUMN depth, not ours.
_map.set_meta("gate_graph", gen.call("get_undersea_graph"))
_map.set_meta("gate_amp", float(gen.call("get_submarine_amp")))
gen.call("set_undersea_graph", null)
gen.call("set_submarine_amp", 0.0)
func _maxdiff(a_list: Array, b_list: Array) -> float:
var m := 0.0
for i in a_list.size():
var a: PackedFloat32Array = a_list[i]
var b: PackedFloat32Array = b_list[i]
for j in mini(a.size(), b.size()):
m = maxf(m, absf(a[j] - b[j]))
return m
func _stage_off(off: Dictionary) -> void:
var name := _name()
var gen: Object = _map.get("_gen")
gen.call("set_submarine_amp", float(_map.get_meta("gate_amp")))
gen.call("set_undersea_graph", _map.get_meta("gate_graph"))
if _on.is_empty():
return
var settle := _maxdiff(_off1["deep"], off["deep"]) + _maxdiff(_off1["shallow"], off["shallow"])
_p(settle < 1e-3, "%s: the baseline shader has settled (two OFF samples agree)" % name, settle)
var water: float = float(gen.call("get_submarine_water_y"))
_p(_maxdiff(_on["shallow"], off["shallow"]) < 1e-3, "E2 %s: above the coast band the density is unchanged" % name,
_maxdiff(_on["shallow"], off["shallow"]))
var moved := 0
var water_pts := 0
var biggest := 0.0
var s := CELLS + 1
var deep_on: Array = _on["deep"]
var deep_off: Array = off["deep"]
for i in deep_on.size():
var a: PackedFloat32Array = deep_on[i]
var b: PackedFloat32Array = deep_off[i]
for z in s:
for y in s:
if water - 600.0 + float(y) * 100.0 > water - 50.0:
continue # only the full-strength part of the band
for x in s:
var j := z * s * s + y * s + x
if b[j] < 0.0:
water_pts += 1 # below the water line and not rock: ocean
var dd := absf(a[j] - b[j])
biggest = maxf(biggest, dd)
if dd > 0.5:
moved += 1
if water_pts == 0:
# NO SEA ON THIS MAP: map_dc_diffusion's WATER_Y (-5900) is below its lowest
# terrain (-5300) on purpose ("this map is about mountains"), so nothing is
# underwater and the term must touch nothing — the cap is 0 over land.
_p(moved == 0, "E3 %s: no sea here (water below the lowest terrain) — the term is inert, as it must be" % name, moved)
else:
_p(moved > 0, "E3 %s: below the band the undersea term is present (%d samples moved, max %.1f m; %d ocean samples)"
% [name, moved, biggest, water_pts])