OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / oracle_dump.gd

115 lines · gd ref:
extends SceneTree

## Headless BRUSH DUMP for the offline DC oracles (restores the asset described in
## hyper-games issue #7, which was never committed).
##
## Runs the REAL QuakeMapImporter over START.MAP against a recording stub, so the
## dump is byte-identical to what the game stamps into the terrain — same recenter,
## same world_scale, same per-face plane transform, same material + projection ids.
## Offline C++ harnesses then replay the mesher against this exact brush set instead
## of a synthetic cube.
##
## Run:
##   cd /storage/projects/code/godot/hyper-tps
##   "$HYPER_TPS_GODOT" --headless --script res://scenes/test/oracle_dump.gd
## (same invocation documented at the top of rtlight_dump.gd)
##
## Output: /tmp/startmap_brushes.bin
##   magic  "DCOR"                        4 bytes
##   u32    version = 1
##   f32    world_scale
##   f32    carve_half x,y,z              (the subtract box applied BEFORE the brushes)
##   f32    carve_center x,y,z
##   u32    brush_count
##   per brush:
##     f32  center x,y,z
##     f32  radius
##     f32  strength
##     u32  material_id
##     u32  plane_count
##     per plane: f32 nx,ny,nz,d ; u32 proj_index

const MAP_PATH := "/storage/projects/gamedev/quake/quakemash/START.MAP"
const OUT_PATH := "/tmp/startmap_brushes.bin"
## Sky-inclusive variant, written instead when run with `-- --sky`.
##
## A SEPARATE FILE ON PURPOSE. test_csg4_polycount and the other published oracles
## are baselined against the SOLID-ONLY set (1164 brushes / 12747 tris), so
## changing what the default dump contains would silently move numbers that exist
## to catch regressions in the csg4 port.
##
## The interior oracle needs this one. A Quake sky brush is solid — it blocks the
## player — and only its rendering is special, so with sky dropped the map has 19
## HOLES in its boundary. A flood fill from outside pours through them, marks the
## rooms as exterior, and the interior set comes back EMPTY: a decomposition that
## subtracts nothing, arrived at without erroring. Same boundary set that answers
## §5a.3, load-bearing for §5a.2 as well.
const OUT_PATH_SKY := "/tmp/startmap_brushes_sky.bin"
const WORLD_SCALE := 0.028          # map_dc_terrain.gd:294
const ORIGIN := Vector3(0, 20, 0)   # map_dc_terrain.gd:294

func _init() -> void:
	var want_sky := "--sky" in OS.get_cmdline_user_args()
	var out_path: String = OUT_PATH_SKY if want_sky else OUT_PATH
	var importer := QuakeMapImporter.new()
	var rec := RecordingTerrain.new()
	# carve=true matches the game (map_dc_terrain.gd:294) so the recorded brush set
	# and the carve box are exactly what the terrain receives.
	var n := importer.import_file(rec, MAP_PATH, WORLD_SCALE, ORIGIN, 1.0, null, true, {}, want_sky)
	print("[oracle_dump] include_sky=%s -> %s" % [str(want_sky), out_path])

	var f := FileAccess.open(out_path, FileAccess.WRITE)
	if f == null:
		push_error("cannot open %s" % out_path)
		quit(1)
		return
	f.store_buffer("DCOR".to_ascii_buffer())
	f.store_32(1)
	f.store_float(WORLD_SCALE)
	f.store_float(rec.carve_half.x); f.store_float(rec.carve_half.y); f.store_float(rec.carve_half.z)
	f.store_float(rec.carve_center.x); f.store_float(rec.carve_center.y); f.store_float(rec.carve_center.z)
	f.store_32(rec.brushes.size())

	var total_planes := 0
	for b in rec.brushes:
		var c: Vector3 = b["center"]
		f.store_float(c.x); f.store_float(c.y); f.store_float(c.z)
		f.store_float(b["radius"])
		f.store_float(b["strength"])
		f.store_32(b["material"])
		var planes: PackedFloat32Array = b["planes"]
		var proj: PackedInt32Array = b["proj"]
		var pc := planes.size() / 4
		f.store_32(pc)
		total_planes += pc
		for i in pc:
			f.store_float(planes[i * 4 + 0])
			f.store_float(planes[i * 4 + 1])
			f.store_float(planes[i * 4 + 2])
			f.store_float(planes[i * 4 + 3])
			f.store_32(proj[i] if i < proj.size() else 0)
	f.close()

	print("[oracle_dump] importer stamped=%d  recorded=%d brushes  %d planes  -> %s"
		% [n, rec.brushes.size(), total_planes, out_path])
	print("[oracle_dump] carve center=%s half=%s" % [str(rec.carve_center), str(rec.carve_half)])
	print("[oracle_dump] materials=%d  face-projections=%d"
		% [importer.material_names.size(), importer.projections.size() / 12])
	quit()

## Records instead of discarding — otherwise identical to rtlight_dump.gd's StubTerrain.
class RecordingTerrain extends Node:
	var brushes: Array = []
	var carve_center := Vector3.ZERO
	var carve_half := Vector3.ZERO

	func edit_terrain_box(c, h, _s, _i, _m) -> void:
		carve_center = c
		carve_half = h

	func edit_terrain_convex(planes, c, r, s, _i, m, fp) -> void:
		brushes.append({
			"planes": planes, "center": c, "radius": r,
			"strength": s, "material": m, "proj": fp,
		})