core: cave planner holds for 16 seeds; mouth joints planned from the resolved mouth (L135, v236)
Mouth floor/profile carried into the first/last edges (fixes seeds 1 and 2); fixed a Q16/Q32 scale bug in the mouth clearance scorer; shortest grade-safe handles; bounded cave AABBs and hoisted edge-BVH normalisation. Seeds 0-15 pass all composed audits and negative controls; fast tier, Clang/GCC W1/W8/W12 green. Near-ready still +3.9..+13.7% vs assembler/world (Surface World +3.9..+8.2% with no caves). Not for merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
a3c8e3687d
commit
2d89023933
25 files changed
+2516
-235
No files matched your search
@@ -0,0 +1,171 @@
|
||||
#!/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'<figure><a href="{Path(str(view["image"])).name}">'
|
||||
f'<img src="{Path(str(view["image"])).name}" alt="{view["name"]}"></a>'
|
||||
f'<figcaption>{view["name"]}</figcaption></figure>' for view in views)
|
||||
(OUT / "COMPARE.html").write_text(
|
||||
"<!doctype html><meta charset=\"utf-8\"><title>L135 interior views</title>\n"
|
||||
"<style>body{font:16px sans-serif;background:#202329;color:#eee;margin:24px}"
|
||||
"h1{font-size:22px}section{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:18px}"
|
||||
"figure{margin:0}img{width:100%;height:auto;display:block}figcaption{padding:6px 0}</style>\n"
|
||||
"<h1>Planner and legacy interiors, seed 0</h1><section>\n"
|
||||
+ cards + "\n</section>\n", encoding="utf-8")
|
||||
print(f"[L135CaptureSummary] views={len(views)} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user