#!/usr/bin/env python3
"""Capture eye-height planner interiors and legacy interiors at legacy coordinates."""
from __future__ import annotations
import json
import math
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux135/captures"
SAVED = ROOT / "Saved/Linux/l135"
DATA = ROOT / "Standalone/data/owner_world.json"
LAYOUT = SAVED / "capture-default-layout.json"
HEADLESS = SAVED / "bin/VoxelForgeCoreHeadless-clang-l135"
RENDERER = SAVED / "bin/VoxelForgeOffscreenCapture-clang-l135"
STAMP = "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5"
EYE_METRES = 1.6
def run(command: list[str], log_path: Path) -> None:
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", *command]
with log_path.open("w", encoding="utf-8") as log:
result = subprocess.run(wrapped, cwd=ROOT, stdout=log,
stderr=subprocess.STDOUT, timeout=1740,
check=False)
if result.returncode:
raise SystemExit(f"command failed ({result.returncode}): {log_path}")
def xyz(value: list[int] | list[float]) -> tuple[float, float, float]:
return tuple(component / 1_000_000.0 for component in value) # type: ignore[return-value]
def yaw_to(dx: float, dy: float) -> float:
# The renderer's yaw zero points along +Y (Forward = sin(yaw), cos(yaw), 0).
return math.degrees(math.atan2(dx, dy))
def camera_arg(camera: tuple[float, float, float], yaw: float) -> str:
return ",".join(f"{part:.9f}" for part in (*camera, yaw, 0.0))
def make_view(name: str, recipe: str, camera: tuple[float, float, float],
yaw: float) -> dict[str, object]:
OUT.mkdir(parents=True, exist_ok=True)
mesh = SAVED / f"capture-{name}.vfmesh"
image = OUT / f"{name}.png"
mesh_log = SAVED / f"capture-{name}-mesh.log"
render_log = SAVED / f"capture-{name}-render.log"
command = [str(HEADLESS), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=8",
"--clip-radius=1", "--max-clip-level=0", "--max-level=0",
f"--camera={camera_arg(camera, yaw)}", f"--mesh-export={mesh}",
"--capture-skip-walkability", "--force-generate",
"--render-distance-chunks=0"]
# The checked-in fixture is already seed 0. Keeping its Unreal-resolved owner
# plan avoids rebuilding the broad SurfaceWorld spawn search for each camera.
if recipe == "legacy-tunnel-network":
command.append("--legacy-tunnel-network")
else:
command.append("--recipe=surface-world")
run(command, mesh_log)
render_command = [str(RENDERER), str(mesh), str(image), "--size=1600x900",
"--shade=readable", "--height-tint=on", "--headlamp=off"]
run(render_command, render_log)
mesh_output = mesh_log.read_text(encoding="utf-8", errors="replace")
render_output = render_log.read_text(encoding="utf-8", errors="replace")
if STAMP not in mesh_output or STAMP not in render_output:
raise SystemExit(f"source stamp mismatch for {name}")
if "status=PASS" not in render_output:
raise SystemExit(f"render failed for {name}")
return {"name": name, "recipe": recipe, "camera_m": list(camera),
"yaw_deg": yaw,
"camera_height_basis": "floor + player eye offset" if recipe == "surface-world"
else ("legacy room floor + player eye offset" if name.endswith("room")
else "near the legacy tunnel centerline"),
"eye_offset_m": EYE_METRES
if recipe == "surface-world" or name.endswith("room") else None,
"mesh": str(mesh.relative_to(ROOT)), "image": str(image.relative_to(ROOT)),
"mesh_log": str(mesh_log.relative_to(ROOT)),
"render_log": str(render_log.relative_to(ROOT))}
def main() -> int:
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
rooms = {room["stable_id"]: room for room in layout["cavern_rooms"]}
edges = {edge["stable_id"]: edge for edge in layout["cavern_edges"]}
views: list[dict[str, object]] = []
arrival = rooms["tunnel-network/strate-2/room-0/primitive-0"]
room1 = rooms["tunnel-network/strate-2/room-1/primitive-0"]
arrival_center = xyz(arrival["center_um"])
arrival_floor = arrival["floor_um"] / 1_000_000.0
views.append(make_view("planner-arrival-room", "surface-world",
(arrival_center[0], arrival_center[1], arrival_floor + EYE_METRES),
yaw_to(room1["center_um"][0] - arrival["center_um"][0],
room1["center_um"][1] - arrival["center_um"][1])))
tunnel = edges["tunnel-network/strate-2/route-edge-1"]["polyline_um"]
tunnel_start, tunnel_end = xyz(tunnel[0]), xyz(tunnel[-1])
tunnel_floor = tunnel[1][2] / 1_000_000.0
views.append(make_view("planner-tunnel", "surface-world",
((tunnel_start[0] + tunnel_end[0]) * 0.5,
(tunnel_start[1] + tunnel_end[1]) * 0.5,
tunnel_floor + EYE_METRES),
yaw_to(tunnel_end[0] - tunnel_start[0], tunnel_end[1] - tunnel_start[1])))
compound = rooms["tunnel-network/strate-2/room-19/primitive-0"]
compound_center = xyz(compound["center_um"])
compound_next = rooms["tunnel-network/strate-2/room-20/primitive-0"]
views.append(make_view("planner-compound-room", "surface-world",
(compound_center[0], compound_center[1],
compound["floor_um"] / 1_000_000.0 + EYE_METRES),
yaw_to(compound_next["center_um"][0] - compound["center_um"][0],
compound_next["center_um"][1] - compound["center_um"][1])))
destination = rooms["tunnel-network/strate-2/room-33/primitive-0"]
destination_center = xyz(destination["center_um"])
upstream = rooms["tunnel-network/strate-2/room-32/primitive-0"]
views.append(make_view("planner-destination-chamber", "surface-world",
(destination_center[0], destination_center[1],
destination["floor_um"] / 1_000_000.0 + EYE_METRES),
yaw_to(upstream["center_um"][0] - destination["center_um"][0],
upstream["center_um"][1] - destination["center_um"][1])))
# The compiled legacy network has a deterministic origin chamber at (0, 0).
# Its room floor is hash-rolled from the owner fixture; these are legacy world
# coordinates, independent of the native planner's passage route.
legacy_origin_floor = -819.9169921875
legacy_origin_eye = legacy_origin_floor + EYE_METRES
legacy_neighbor = (39.327854, 5.115777, -797.724731)
views.append(make_view("legacy-origin-room", "legacy-tunnel-network",
(0.0, 0.0, legacy_origin_eye), yaw_to(legacy_neighbor[0], legacy_neighbor[1])))
# Hash-selected (cell 1,0) is the origin graph's nearest connected room. Its
# cut-plane floor estimate is -801.0 m before the authored local relief.
legacy_neighbor_floor = -800.9986572265625
legacy_neighbor_eye = legacy_neighbor_floor + EYE_METRES
views.append(make_view("legacy-connected-room", "legacy-tunnel-network",
(legacy_neighbor[0], legacy_neighbor[1], legacy_neighbor_eye),
yaw_to(-legacy_neighbor[0], -legacy_neighbor[1])))
legacy_tunnel_mid = (legacy_neighbor[0] * 0.5, legacy_neighbor[1] * 0.5,
(-812.0 + legacy_neighbor[2]) * 0.5)
views.append(make_view("legacy-origin-tunnel", "legacy-tunnel-network",
legacy_tunnel_mid, yaw_to(legacy_neighbor[0], legacy_neighbor[1])))
manifest = {"source_stamp": STAMP, "seed": 0,
"renderer": "readable, height tint on, headlamp off; 1600x900",
"views": views}
(OUT / "capture-index.json").write_text(
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
cards = "\n".join(
f''
f'