extends SceneTree
## DOES THE TERRAIN FIELD ACTUALLY READ AIR IN THE BORE? — hyper-games #25.
##
## "$HYPER_TPS_GODOT" --headless --script scenes/test/arch_field_gate.gd
##
## Every gate so far has stopped at the SHELL: does the clearance reach it, does
## it grow by the right volume, is it sealed. All of that passes, and the report
## from the game is "I only see the arch clipping into the terrain, no empty hole
## inside". So the question was never answered — it was assumed.
##
## The shell is an INPUT to the terrain field. This asks the field.
##
## THE CONTROL IS THE POINT. A room inside START.MAP must read AIR — that proves
## the mesh SDF, the stamp, and the whole subtract path work at all. If the room
## is air and the bore is solid, the failure is specific to the arch's island and
## not to the mechanism.
##
## DCTerrain outside the tree keeps its edit list and evaluates get_distance on
## the CPU (_eval_with_edits), so no display, no meshing and no GPU are involved.
## Convention: positive = INSIDE SOLID, negative = air.
const MAP_SCENE := "res://scenes/maps/map_dc_world.tscn"
const OP := preload("res://scenes/maps/operators/create_box_operator.gd")
var _map: Node = null
var _t0 := 0
var _done := false
var _failures: Array[String] = []
func _check(label: String, ok: bool, detail: String) -> void:
if not ok:
_failures.append("%s (%s)" % [label, detail])
print(" %-46s %s %s" % [label, "OK " if ok else "FAIL", detail])
func _initialize() -> void:
_t0 = Time.get_ticks_msec()
_map = (load(MAP_SCENE) as PackedScene).instantiate()
root.add_child(_map)
func _process(_d: float) -> bool:
if _done:
return true
var lvl: Node = _map.get("level")
if lvl == null or not is_instance_valid(lvl) or lvl.get_brush_count() == 0:
if Time.get_ticks_msec() - _t0 > 90000:
print("FAIL — level never imported"); quit(1); return true
return false
_done = true
print("arch field gate")
# Our own terrain: map_dc_world frees its own when headless.
var terrain: Node = ClassDB.instantiate("DCTerrain")
root.add_child(terrain)
DCWorldSetup.configure_terrain(terrain)
# Place the arch where the report put it, outside the level's AABB.
var op := OP.new()
op.map = _map
op.level = lvl
_map.add_child(op)
op._shape = 2
op._size_steps = 19 # 152 units at grid 8, as reported
op._normal = Vector3.UP
op._axis = 1
op._view = Vector3(0, 0, 1)
op._material = 0
op._level_id = 0
op._origin = Vector3(-47.936, 4.928, 81.312)
op._valid = true
var c: Array = op._corners()
var lo: Vector3 = c[0]
var hi: Vector3 = c[1]
# MID-BORE, not just inside the surface. A point 0.3 m in has mesh_d ~0.3, and
# the subtract is `min(base, -mesh_d)` — so on a base that is already airier
# than -0.3 the edit applies and changes NOTHING, which is indistinguishable
# from being culled. The centre of the clearance is where the edit has
# something to say.
var bore := Vector3((lo.x + hi.x) * 0.5, lo.y + (hi.y - lo.y) * 0.35,
(lo.z + hi.z) * 0.5)
print(" arch bounds %s .. %s" % [str(lo), str(hi)])
print(" bore probe %s" % str(bore))
var room_before: float = terrain.get_distance(room_probe())
# What is the ground doing there BEFORE anything is subtracted?
var before: float = terrain.get_distance(bore)
print(" terrain at the bore, no edits: %.3f (%s)"
% [before, "SOLID" if before > 0.0 else "air"])
print(" room before: %.3f" % room_before)
_check("confirm() creates the arch", op.confirm(), "")
# Stamp exactly as map_dc_world does.
var shell: Dictionary = lvl.build_shell()
_check("the shell is sealed", bool(shell.get("sealed", false)),
"%d tris" % int(shell.get("triangles", 0)))
terrain.stamp_mesh_arrays(shell["vertices"], shell["indices"],
shell["center"], 1.0, -1.0, false, 0, true)
print(" stamped %d tris, centre %s" % [int(shell.get("triangles", 0)), str(shell["center"])])
# THE BUILT-IN DIAGNOSTIC prints dist-to-stamp, radius, in_aabb and mesh_d —
# every number the CPU path branches on.
print(" --- debug_sdf_at(room) ---")
terrain.debug_sdf_at(room_probe())
print(" --- debug_sdf_at(bore) ---")
terrain.debug_sdf_at(bore)
# ---- THE CONTROL: a room inside the level must be air ----------------
# info_player_start is standable, so the field must not be solid there.
var room := room_probe()
var d_room: float = terrain.get_distance(room)
_check("A ROOM IN THE LEVEL GOT AIRIER", d_room > room_before + 0.5,
"%.3f -> %.3f" % [room_before, d_room])
# ---- THE QUESTION -----------------------------------------------------
var d_bore: float = terrain.get_distance(bore)
# THE BUG, as one number: this was IDENTICAL to `before` because the mesh edit
# was rejected by a cull sphere centred on the mesh centroid and sized as if it
# were centred in the bounds. dist 94.57 against radius 82.52, with mesh_d
# +0.300 saying the point is genuinely inside the shell.
_check("AND SO DID THE BORE", d_bore > before + 0.5,
"%.3f -> %.3f (identical means the edit was culled)" % [before, d_bore])
# Where does it become air, walking out along the tunnel axis? Locates the
# subtract if it is landing somewhere other than where we think.
var line := ""
for i in range(-3, 8):
var p := bore + Vector3(0, 0, float(i))
line += "%+d:%s " % [i, "S" if terrain.get_distance(p) > 0.0 else "a"]
print(" along Z (S=solid a=air): %s" % line)
print("\nRESULT: %s" % ("PASS" if _failures.is_empty() else "FAIL"))
for f: String in _failures:
print(" - %s" % f)
quit(0 if _failures.is_empty() else 1)
return true
static func room_probe() -> Vector3:
return Vector3(-5.376, 4.36, 39.648)