OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / arch_field_gate.gd

144 lines · gd ref:
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)