#!/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'{view[' f'
{view["name"]}
' for view in views) (OUT / "COMPARE.html").write_text( "L135 interior views\n" "\n" "

Planner and legacy interiors, seed 0

\n" + cards + "\n
\n", encoding="utf-8") print(f"[L135CaptureSummary] views={len(views)} status=PASS") return 0 if __name__ == "__main__": raise SystemExit(main())