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,
})