extends SceneTree
## GATE — the terrain's COLLISION never vanishes under the player while the camera is
## still (user 2026-10-06: "the character can too easily fall inside of the terrain").
##
## A map loads with the camera 24 m above (0, 16) looking down (inside collision_distance,
## 100 m); after 10 s of streaming, physics rays are cast straight down at 100 fixed
## columns every 0.5 s for 30 s. A column whose ray hit ground and then hits NOTHING is a
## collision gap: a player standing there falls into the terrain.
##
## Measured before the fix: map_geiss 59 gaps (51 columns), map_geiss_world 45 (43) —
## dc-terrain freed a split parent's collision as soon as its children were VISIBLE, not
## when their collision existed (dc_octree_clipmap.cpp Stage 2), and Geiss terrain keeps
## re-splitting. The smooth terraces map had 0 and is the CONTROL: if it gaps, the
## measurement is broken.
##
## ⚠ WINDOWED (collision needs the GPU mesher). Unset CI or the window is 2x2:
## env -u CI HYPERTPS_DAEMON_TCP=127.0.0.1:1 DISPLAY=:0 "$HYPER_TPS_GODOT" --path . \
## --script res://scenes/test/gate_collision_continuity.gd
## One map per run (a fresh DCTerrain each): pass the map name as a user arg, or none
## for map_geiss.
const SETTLE_FRAMES := 600
const EVERY := 30
const SAMPLES := 60
## A window hidden behind a fullscreen one (i3 + Guake) presents at ~1 Hz (the NVIDIA
## driver sleeps in present): the terrain barely streams, nothing re-splits, and 0 gaps
## would be a FALSE PASS. Measured 2026-10-06: 1 fps, 896 chunks in 120 s hidden.
const MIN_FPS := 15
const ProbeWindow := preload("res://scenes/test/probe_window.gd")
var _ws := ""
var _fps_min := 1 << 30
var _map: Node
var _f := 0
var _cols: Array = []
var _prev: Array = []
var _samples := 0
var _gaps := 0
var _gap_cols := {}
var _first_hits := 0
var _name := ""
func _initialize() -> void:
var a := OS.get_cmdline_user_args()
_name = a[0] if a.size() > 0 else "map_geiss"
_ws = ProbeWindow.free_workspace()
OS.set_environment("GEISS_CAM", "0,40,16,0,-60")
_map = (load("res://scenes/maps/%s.tscn" % _name) as PackedScene).instantiate()
root.add_child(_map)
for i in 10:
for k in 10:
_cols.append(Vector2(-45.0 + i * 10.0, 16.0 - 45.0 + k * 10.0))
_prev.resize(_cols.size())
_prev.fill(NAN)
func _physics_process(_d: float) -> bool:
_f += 1
if _f % 60 == 0 and not ProbeWindow.drawing(root):
ProbeWindow.move_to(_ws, Vector2i(1600, 900))
if _f < SETTLE_FRAMES or _f % EVERY != 0:
return false
_fps_min = mini(_fps_min, int(Engine.get_frames_per_second()))
var space := root.get_world_3d().direct_space_state
var hits := 0
for i in _cols.size():
var c: Vector2 = _cols[i]
var r := space.intersect_ray(PhysicsRayQueryParameters3D.create(
Vector3(c.x, 600.0, c.y), Vector3(c.x, -400.0, c.y)))
var hit := not r.is_empty()
if hit:
hits += 1
if _prev[i] == 1.0 and not hit:
_gaps += 1
_gap_cols[i] = true
_prev[i] = 1.0 if hit else 0.0
_samples += 1
if _samples == 1:
_first_hits = hits
if _samples < SAMPLES:
return false
var ok := _first_hits >= 90 and _gaps == 0 and _fps_min >= MIN_FPS
print(" %s: ground under %d/100 columns at the start; over %d samples (30 s) the ground VANISHED %d times under %d columns (min fps %d)"
% [_name, _first_hits, _samples, _gaps, _gap_cols.size(), _fps_min])
if _first_hits < 90:
print("FAIL too few columns had collision at all — the rig proves nothing")
if _fps_min < MIN_FPS:
print("FAIL min fps %d < %d — window hidden (workspace '%s')? the terrain did not stream; proves nothing" % [_fps_min, MIN_FPS, _ws])
print("PASS" if ok else "FAIL")
quit(0 if ok else 1)
return true