proto: liminal looks - pool rooms, backrooms grid, staircase to nowhere, terraced hills (L161)
Stamp vocabulary v7 (opt-in fillBox, carveBox, fillSteps; carveArch reused); flat-field liminal stamp fixtures; capsule walk audit with doorway-width gate (1.2 m control fails as expected). Clang/GCC W1/W8/W12 identical; owner canonical hashes unchanged; fast tier 273/273. Terraced wide view not yet legible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
No files matched your search
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build four standalone liminal geometry fixtures and their capsule-walk specs."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OWNER = Path(os.environ.get("VOXELFORGE_OWNER_DATA",
|
||||
ROOT / "Standalone/data/owner_world.json"))
|
||||
OUT_DEFAULT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l161"))
|
||||
FLAT_FIELD = {"version": 1, "kind": "flatField", "minimum_arena_clearance_m": 0.0}
|
||||
|
||||
|
||||
def stamp(stamps: list[dict], stable_id: str, kind: str, x: float, y: float,
|
||||
z: float, half: tuple[float, float, float], *, yaw: int = 0,
|
||||
steps: int | None = None) -> None:
|
||||
row = {
|
||||
"stable_id": stable_id,
|
||||
"kind": kind,
|
||||
"anchor_xy_m": [x, y],
|
||||
"z_offset_m": z,
|
||||
"half_extents_m": list(half),
|
||||
"yaw_quarter_turns": yaw,
|
||||
}
|
||||
if steps is not None:
|
||||
row["step_count"] = steps
|
||||
stamps.append(row)
|
||||
|
||||
|
||||
def fixture(name: str, stamps: list[dict], walk_points: list[tuple[float, float, float]],
|
||||
doors: list[dict], *, center: tuple[float, float], wide_camera: tuple[float, float, float],
|
||||
eye_camera: tuple[float, float, float], eye_target_offset: tuple[float, float, float],
|
||||
wide_target_offset: tuple[float, float, float],
|
||||
eye_clip: int, wide_clip: int) -> tuple[str, dict, dict]:
|
||||
root = json.loads(OWNER.read_text(encoding="utf-8"))
|
||||
root["surface_profile_override"] = dict(FLAT_FIELD)
|
||||
root["liminal_stamp_fixture_override"] = {
|
||||
"version": 1,
|
||||
"strate_index": 0,
|
||||
"stamps": stamps,
|
||||
}
|
||||
walk = {
|
||||
"version": 1,
|
||||
"path_points_offset_m": [list(point) for point in walk_points],
|
||||
"door_prefixes": doors,
|
||||
}
|
||||
views = {
|
||||
"center_xy_m": list(center),
|
||||
"eye_camera_offset_m": list(eye_camera),
|
||||
"wide_camera_offset_m": list(wide_camera),
|
||||
"eye_target_offset_m": list(eye_target_offset),
|
||||
"wide_target_offset_m": list(wide_target_offset),
|
||||
"eye_clip_radius": eye_clip,
|
||||
"wide_clip_radius": wide_clip,
|
||||
}
|
||||
return name, root, {"walk": walk, "views": views}
|
||||
|
||||
|
||||
def pool_rooms() -> tuple[str, dict, dict]:
|
||||
stamps: list[dict] = []
|
||||
cx, cy, cell, cells = 520.0, 440.0, 20.0, 7
|
||||
half = cell * cells * 0.5
|
||||
wall_height, wall_half_thickness = 9.0, 1.2
|
||||
for line in range(cells + 1):
|
||||
offset = (line - cells * 0.5) * cell
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/00-wall-x/{line:02d}", "fillBox",
|
||||
cx + offset, cy, wall_height * 0.5,
|
||||
(wall_half_thickness, half, wall_height * 0.5))
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/00-wall-y/{line:02d}", "fillBox",
|
||||
cx, cy + offset, wall_height * 0.5,
|
||||
(half, wall_half_thickness, wall_height * 0.5))
|
||||
for row in range(cells):
|
||||
y = cy + (row - (cells - 1) * 0.5) * cell
|
||||
for line in range(cells + 1):
|
||||
x = cx + (line - cells * 0.5) * cell
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/01-doorway-x/{row:02d}-{line:02d}",
|
||||
"carveArch", x, y + 7.0, 4.0, (3.6, 1.55, 4.0), yaw=1)
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/01-doorway-y/{line:02d}-{row:02d}",
|
||||
"carveArch", cx + (row - (cells - 1) * 0.5) * cell,
|
||||
cy + (line - cells * 0.5) * cell, 4.0,
|
||||
(3.6, 1.55, 4.0))
|
||||
for row in range(cells):
|
||||
y = cy + (row - (cells - 1) * 0.5) * cell
|
||||
for column in range(cells):
|
||||
x = cx + (column - (cells - 1) * 0.5) * cell
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/02-roof/{row:02d}-{column:02d}",
|
||||
"fillBox", x, y, 9.4, (10.0, 10.0, 0.5))
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/03-skylight/{row:02d}-{column:02d}",
|
||||
"carveBox", x, y, 9.4, (5.5, 5.5, 1.0))
|
||||
stamp(stamps, f"liminal/pool-rooms-v1/04-basin/{row:02d}-{column:02d}",
|
||||
"carveBox", x, y, -1.0, (5.5, 5.5, 1.5))
|
||||
path_y = cy + 7.0
|
||||
walk = [(-half - 2.0 + cx, path_y, 0.88), (half + 2.0 + cx, path_y, 0.88)]
|
||||
doors = [
|
||||
{"prefix": "liminal/pool-rooms-v1/01-doorway-x/", "width_axis": 0,
|
||||
"minimum_width_m": 1.5},
|
||||
{"prefix": "liminal/pool-rooms-v1/01-doorway-y/", "width_axis": 0,
|
||||
"minimum_width_m": 1.5},
|
||||
]
|
||||
return fixture("pool-rooms", stamps, walk, doors, center=(cx, cy),
|
||||
wide_camera=(-60.0, 7.0, 3.0), eye_camera=(-40.0, 7.0, 1.6),
|
||||
eye_target_offset=(60.0, 7.0, 0.8),
|
||||
wide_target_offset=(65.0, 7.0, 1.2), eye_clip=4, wide_clip=7)
|
||||
|
||||
|
||||
def backrooms(width: float = 4.0, name: str = "backrooms-grid") -> tuple[str, dict, dict]:
|
||||
stamps: list[dict] = []
|
||||
cx, cy, cell, cells = 820.0, 420.0, 18.0, 7
|
||||
half, wall_half = cell * cells * 0.5, 1.2
|
||||
wall_height = 5.5
|
||||
for line in range(cells + 1):
|
||||
offset = (line - cells * 0.5) * cell
|
||||
stamp(stamps, f"liminal/backrooms-grid-v1/00-wall-x/{line:02d}", "fillBox",
|
||||
cx + offset, cy, wall_height * 0.5,
|
||||
(wall_half, half, wall_height * 0.5))
|
||||
stamp(stamps, f"liminal/backrooms-grid-v1/00-wall-y/{line:02d}", "fillBox",
|
||||
cx, cy + offset, wall_height * 0.5,
|
||||
(half, wall_half, wall_height * 0.5))
|
||||
door_half = width * 0.5
|
||||
for row in range(cells):
|
||||
y = cy + (row - (cells - 1) * 0.5) * cell
|
||||
for line in range(cells + 1):
|
||||
x = cx + (line - cells * 0.5) * cell
|
||||
stamp(stamps, f"liminal/backrooms-grid-v1/01-door-x/{row:02d}-{line:02d}",
|
||||
"carveBox", x, y, 1.25, (1.6, door_half, 1.25))
|
||||
stamp(stamps, f"liminal/backrooms-grid-v1/01-door-y/{line:02d}-{row:02d}",
|
||||
"carveBox", cx + (row - (cells - 1) * 0.5) * cell,
|
||||
cy + (line - cells * 0.5) * cell, 1.25,
|
||||
(door_half, 1.6, 1.25))
|
||||
stamp(stamps, "liminal/backrooms-grid-v1/02-ceiling", "fillBox",
|
||||
cx, cy, 5.9, (half, half, 0.5))
|
||||
walk = [(cx - half - 2.0, cy, 0.88), (cx + half + 2.0, cy, 0.88)]
|
||||
doors = [
|
||||
{"prefix": "liminal/backrooms-grid-v1/01-door-x/", "width_axis": 1,
|
||||
"minimum_width_m": 1.5},
|
||||
{"prefix": "liminal/backrooms-grid-v1/01-door-y/", "width_axis": 0,
|
||||
"minimum_width_m": 1.5},
|
||||
]
|
||||
return fixture(name, stamps, walk, doors, center=(cx, cy),
|
||||
wide_camera=(-42.0, -6.0, 1.6), eye_camera=(-54.0, 0.0, 1.6),
|
||||
eye_target_offset=(102.0, 0.0, 1.6),
|
||||
wide_target_offset=(80.0, 20.0, 0.0), eye_clip=6, wide_clip=6)
|
||||
|
||||
|
||||
def nowhere_stairs() -> tuple[str, dict, dict]:
|
||||
stamps: list[dict] = []
|
||||
cx, cy = 300.0, -760.0
|
||||
stamp(stamps, "liminal/staircase-nowhere-v1/00-stair", "fillSteps",
|
||||
cx, cy, 6.0, (32.0, 4.0, 6.0), steps=64)
|
||||
stamp(stamps, "liminal/staircase-nowhere-v1/04-landing", "fillBox",
|
||||
cx + 40.5, cy, 11.5, (9.0, 4.5, 0.5))
|
||||
stamp(stamps, "liminal/staircase-nowhere-v1/02-arch-wall", "fillBox",
|
||||
cx + 38.0, cy, 7.5, (1.2, 6.5, 7.5))
|
||||
stamp(stamps, "liminal/staircase-nowhere-v1/03-doorway-arch", "carveArch",
|
||||
cx + 38.0, cy, 7.0, (5.0, 5.0, 7.0), yaw=1)
|
||||
# Begin high enough to clear the first riser with the audit capsule, then
|
||||
# walk the same ascending staircase through the arch and onto the landing.
|
||||
points = [(cx - 32.5, cy, 0.88 + 12.0 / 64.0 + 0.05)]
|
||||
tread, rise = 64.0 / 64, 12.0 / 64
|
||||
for index in range(64):
|
||||
points.append((cx - 32.0 + (index + 0.5) * tread,
|
||||
cy, 0.88 + (index + 1) * rise + 0.05))
|
||||
points.extend([(cx + 34.0, cy, 12.93), (cx + 38.0, cy, 12.93),
|
||||
(cx + 44.0, cy, 12.93)])
|
||||
doors = [{"prefix": "liminal/staircase-nowhere-v1/03-doorway-arch", "width_axis": 0,
|
||||
"minimum_width_m": 1.5}]
|
||||
return fixture("staircase-to-nowhere", stamps, points, doors, center=(cx, cy),
|
||||
wide_camera=(-8.0, -32.0, 36.0), eye_camera=(10.0, 0.0, 9.7),
|
||||
eye_target_offset=(38.0, 0.0, 13.6),
|
||||
wide_target_offset=(42.0, 0.0, 11.0), eye_clip=4, wide_clip=6)
|
||||
|
||||
|
||||
def terraced_hills() -> tuple[str, dict, dict]:
|
||||
stamps: list[dict] = []
|
||||
cx, cy, rows, spacing = -520.0, -500.0, 9, 96.0
|
||||
half_run, half_width, half_rise, steps = 48.0, 40.0, 8.0, 80
|
||||
for row in range(rows):
|
||||
y = cy + (row - (rows - 1) * 0.5) * spacing
|
||||
stamp(stamps, f"liminal/terraced-hills-v1/ridge/{row:02d}/rise-west",
|
||||
"fillSteps", cx - half_run, y, half_rise,
|
||||
(half_run, half_width, half_rise), steps=steps)
|
||||
stamp(stamps, f"liminal/terraced-hills-v1/ridge/{row:02d}/rise-east",
|
||||
"fillSteps", cx + half_run, y, half_rise,
|
||||
(half_run, half_width, half_rise), yaw=2, steps=steps)
|
||||
points = [(cx - 100.0, cy, 0.93)]
|
||||
tread, rise = (2.0 * half_run) / steps, (2.0 * half_rise) / steps
|
||||
for index in range(steps):
|
||||
points.append((cx - 2.0 * half_run + (index + 0.5) * tread,
|
||||
cy, 0.93 + (index + 1) * rise))
|
||||
for index in reversed(range(steps)):
|
||||
points.append((cx + 2.0 * half_run - (index + 0.5) * tread,
|
||||
cy, 0.93 + (index + 1) * rise))
|
||||
points.append((cx + 100.0, cy, 0.93))
|
||||
return fixture("terraced-hills", stamps, points, [], center=(cx, cy),
|
||||
wide_camera=(-40.0, 0.0, 20.0), eye_camera=(-60.0, 0.0, 7.8),
|
||||
eye_target_offset=(0.0, 0.0, 14.5),
|
||||
wide_target_offset=(100.0, 0.0, -10.0), eye_clip=4, wide_clip=3)
|
||||
|
||||
|
||||
def write(path: Path, value: dict) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(value, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--out", type=Path, default=OUT_DEFAULT / "fixtures")
|
||||
args = parser.parse_args()
|
||||
args.out.mkdir(parents=True, exist_ok=True)
|
||||
specs = args.out / "walk-specs"
|
||||
fixtures = [pool_rooms(), backrooms(), nowhere_stairs(), terraced_hills()]
|
||||
fixtures.append(backrooms(width=1.2, name="backrooms-narrow-control"))
|
||||
for name, world, metadata in fixtures:
|
||||
write(args.out / f"liminal-{name}.json", world)
|
||||
write(specs / f"{name}.json", metadata["walk"])
|
||||
write(args.out / "views" / f"{name}.json", metadata["views"])
|
||||
print(f"[L161Fixture] name={name} stamps="
|
||||
f"{len(world['liminal_stamp_fixture_override']['stamps'])} status=PASS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run L161 stamp-fixture determinism, walk audits and two-view captures."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT_DEFAULT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l161"))
|
||||
CAPTURE_DEFAULT = Path(__file__).resolve().parent / "captures"
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
Q_PER_METRE = 1024.0
|
||||
FOG_COLOR = "0.78,0.84,0.87"
|
||||
|
||||
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
|
||||
from runner import current_stamp, first_stamp
|
||||
|
||||
|
||||
def fmt(values) -> str:
|
||||
return ",".join(f"{value:.6f}" for value in values)
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
for block in iter(lambda: stream.read(1024 * 1024), b""):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def run_step(name: str, command: list[object], log: Path, *, env: dict[str, str],
|
||||
stamped: bool = False, expected_exit: int = 0,
|
||||
timeout_seconds: int = 1740) -> str:
|
||||
expected_stamp = current_stamp() if stamped else None
|
||||
if stamped:
|
||||
probe = subprocess.run([str(command[0]), "--print-source-stamp"], cwd=ROOT,
|
||||
capture_output=True, text=True, timeout=15, check=False)
|
||||
if probe.returncode != 0 or first_stamp(probe.stdout) != expected_stamp:
|
||||
raise RuntimeError(f"{name} refused a stale executable: {command[0]}")
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run([*LIMIT, "timeout", f"{timeout_seconds}s",
|
||||
*map(str, command)], cwd=ROOT, env=env, stdout=stream,
|
||||
stderr=subprocess.STDOUT, timeout=timeout_seconds + 5, check=False)
|
||||
elapsed = time.monotonic() - started
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
print(f"[L161Run] step={name} elapsed_s={elapsed:.3f} exit={result.returncode} log={log}",
|
||||
flush=True)
|
||||
if stamped and (first_stamp(output) != expected_stamp or current_stamp() != expected_stamp):
|
||||
raise RuntimeError(f"{name} source stamp mismatch; see {log}")
|
||||
if result.returncode != expected_exit:
|
||||
raise RuntimeError(f"{name} exit={result.returncode}, expected={expected_exit}; see {log}")
|
||||
return output
|
||||
|
||||
|
||||
def camera_for(view: dict, floor: float, name: str, kind: str) -> tuple[float, float, float]:
|
||||
center_x, center_y = view["center_xy_m"]
|
||||
offset = view[f"{kind}_camera_offset_m"]
|
||||
z = floor + offset[2]
|
||||
if name == "backrooms-grid" or name == "backrooms-narrow-control":
|
||||
z = floor + offset[2]
|
||||
return center_x + offset[0], center_y + offset[1], z
|
||||
|
||||
|
||||
def target_for(view: dict, floor: float, name: str, kind: str) -> tuple[float, float, float]:
|
||||
center_x, center_y = view["center_xy_m"]
|
||||
offset = view[f"{kind}_target_offset_m"]
|
||||
return center_x + offset[0], center_y + offset[1], floor + offset[2]
|
||||
|
||||
|
||||
def headless_command(binary: Path, fixture: Path, camera, mesh: Path,
|
||||
manifest: Path, layout: Path, workers: int, clip_radius: int,
|
||||
*, max_level: int = 0, hash_tiles: bool = False,
|
||||
audit_path: Path | None = None) -> list[object]:
|
||||
command: list[object] = [binary, fixture, f"--workers={workers}", "--seed=0",
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--force-generate", f"--clip-radius={clip_radius}",
|
||||
f"--max-clip-level={max_level}", f"--max-level={max_level}",
|
||||
"--render-distance-chunks=0",
|
||||
f"--camera={fmt((*camera, 0.0, 0.0))}", f"--mesh-export={mesh}",
|
||||
f"--world-assembler-manifest-output={manifest}",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
if hash_tiles:
|
||||
command.append("--stream-hash-tiles")
|
||||
if audit_path is not None:
|
||||
command.append(f"--audit-liminal-stamp-walk={audit_path}")
|
||||
return command
|
||||
|
||||
|
||||
def digest(output: str, manifest_path: Path, layout_path: Path) -> tuple[str, str, str]:
|
||||
tile_hashes = sorted(line for line in output.splitlines()
|
||||
if line.startswith("[PipelineTileHash] "))
|
||||
if not manifest_path.is_file() or not layout_path.is_file() or not tile_hashes:
|
||||
raise RuntimeError("determinism run omitted manifest, resolved layout or tile hashes")
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
layout = json.loads(layout_path.read_text(encoding="utf-8"))
|
||||
manifest_digest = "|".join((manifest["content_hash"],
|
||||
manifest["recipe"]["content_hash"], manifest["plan"]["content_hash"]))
|
||||
return manifest_digest, layout["stable_identity"], "\n".join(tile_hashes)
|
||||
|
||||
|
||||
def write_walk_audit(spec_path: Path, manifest_path: Path, audit_path: Path) -> None:
|
||||
spec = json.loads(spec_path.read_text(encoding="utf-8"))
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
surface = manifest["recipe"]["surface_profile"]
|
||||
floor = surface["h0_q"] / Q_PER_METRE
|
||||
lines = ["# P records are world metres; doorway width axes use the resolved stamp's local extents."]
|
||||
for gate in spec["door_prefixes"]:
|
||||
lines.append(f"D {gate['prefix']} {gate['width_axis']} {gate['minimum_width_m']:.6f}")
|
||||
for x, y, z_offset in spec["path_points_offset_m"]:
|
||||
lines.append(f"P {x:.6f} {y:.6f} {floor + z_offset:.6f}")
|
||||
audit_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
audit_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def camera_angles(camera, target) -> tuple[float, float]:
|
||||
dx, dy, dz = (target[index] - camera[index] for index in range(3))
|
||||
return math.degrees(math.atan2(dx, dy)), math.degrees(math.atan2(dz, math.hypot(dx, dy)))
|
||||
|
||||
|
||||
def build_plan(binary: Path, name: str, fixture: Path, view: dict,
|
||||
out: Path, env: dict[str, str]) -> tuple[Path, Path, dict, dict]:
|
||||
run_dir = out / "plan-probes" / name
|
||||
run_dir.mkdir(parents=True, exist_ok=True)
|
||||
manifest, layout = run_dir / "manifest.json", run_dir / "layout.json"
|
||||
mesh = run_dir / "probe.vfmesh"
|
||||
camera = (view["center_xy_m"][0], view["center_xy_m"][1], -500.0)
|
||||
output = run_step(f"plan-{name}", headless_command(binary, fixture, camera,
|
||||
mesh, manifest, layout, 12, 1), out / "logs" / f"plan-{name}.log",
|
||||
env=env, stamped=True)
|
||||
if "[LiminalStampFixtureResolved] vocabulary=7" not in output:
|
||||
raise RuntimeError(f"{name} did not resolve with stamp vocabulary 7")
|
||||
if not manifest.is_file() or not layout.is_file():
|
||||
raise RuntimeError(f"{name} plan probe omitted its outputs")
|
||||
manifest_value = json.loads(manifest.read_text(encoding="utf-8"))
|
||||
layout_value = json.loads(layout.read_text(encoding="utf-8"))
|
||||
return manifest, layout, manifest_value, layout_value
|
||||
|
||||
|
||||
def run_determinism(name: str, fixture: Path, view: dict, floor: float,
|
||||
binaries: dict[str, Path], out: Path, env: dict[str, str]) -> None:
|
||||
camera = camera_for(view, floor, name, "eye")
|
||||
baseline = None
|
||||
for compiler in ("clang", "gcc"):
|
||||
for workers in (1, 8, 12):
|
||||
tag = f"{name}-{compiler}-w{workers}"
|
||||
run_dir = out / "determinism" / tag
|
||||
run_dir.mkdir(parents=True, exist_ok=True)
|
||||
mesh, manifest, layout = (run_dir / "fixture.vfmesh",
|
||||
run_dir / "manifest.json", run_dir / "layout.json")
|
||||
output = run_step(tag, headless_command(binaries[compiler], fixture,
|
||||
camera, mesh, manifest, layout, workers, 1,
|
||||
hash_tiles=True), out / "logs" / f"determinism-{tag}.log",
|
||||
env=env, stamped=True)
|
||||
result = digest(output, manifest, layout)
|
||||
if baseline is None:
|
||||
baseline = result
|
||||
elif result != baseline:
|
||||
raise RuntimeError(f"{name} diverged at {compiler} W{workers}; see determinism logs")
|
||||
print(f"[L161Determinism] fixture={name} compiler_worker_cells=6 identical=yes status=PASS",
|
||||
flush=True)
|
||||
|
||||
|
||||
def run_walk_audit(name: str, fixture: Path, view: dict, floor: float,
|
||||
spec: Path, binary: Path, out: Path, env: dict[str, str],
|
||||
*, expected_exit: int = 0) -> str:
|
||||
camera = camera_for(view, floor, name, "eye")
|
||||
run_dir = out / "walk-audits" / name
|
||||
run_dir.mkdir(parents=True, exist_ok=True)
|
||||
manifest = run_dir / "manifest.json"
|
||||
layout = run_dir / "layout.json"
|
||||
mesh = run_dir / "audit.vfmesh"
|
||||
audit_path = run_dir / "walk.path"
|
||||
# Use a fixture-specific manifest, so the path uses the exact authored floor height.
|
||||
plan = out / "plan-probes" / name / "manifest.json"
|
||||
if not plan.is_file():
|
||||
raise RuntimeError(f"{name} has no resolved manifest for its walk audit")
|
||||
write_walk_audit(spec, plan, audit_path)
|
||||
output = run_step(f"walk-{name}", headless_command(binary, fixture, camera,
|
||||
mesh, manifest, layout, 12, 1, audit_path=audit_path),
|
||||
out / "logs" / f"walk-{name}.log", env=env, stamped=True,
|
||||
expected_exit=expected_exit)
|
||||
expected_status = "FAIL" if expected_exit else "PASS"
|
||||
if not re.search(rf"\[LiminalStampWalkAudit\].*status={expected_status}\b", output):
|
||||
raise RuntimeError(f"{name} walk audit did not report expected {expected_status}")
|
||||
return output
|
||||
|
||||
|
||||
def capture(name: str, fixture: Path, view: dict, floor: float, *, kind: str,
|
||||
binary: Path, renderer: Path, out: Path, artifact_dir: Path,
|
||||
env: dict[str, str]) -> None:
|
||||
camera = camera_for(view, floor, name, kind)
|
||||
target = target_for(view, floor, name, kind)
|
||||
clip_radius = int(view[f"{kind}_clip_radius"])
|
||||
max_level = 1 if name == "terraced-hills" and kind == "wide" else 0
|
||||
stem = f"{name}-{kind}"
|
||||
run_dir = out / "captures" / stem
|
||||
run_dir.mkdir(parents=True, exist_ok=True)
|
||||
mesh, manifest, layout = (run_dir / "capture.vfmesh",
|
||||
run_dir / "manifest.json", run_dir / "layout.json")
|
||||
png = run_dir / "capture.png"
|
||||
image = artifact_dir / f"{stem}.jpg"
|
||||
output = run_step(f"capture-export-{stem}", headless_command(binary,
|
||||
fixture, camera, mesh, manifest, layout, 12, clip_radius,
|
||||
max_level=max_level), out / "logs" / f"capture-export-{stem}.log",
|
||||
env=env, stamped=True)
|
||||
if not mesh.is_file() or mesh.stat().st_size == 0:
|
||||
raise RuntimeError(f"{stem} produced an empty mesh")
|
||||
yaw, pitch = camera_angles(camera, target)
|
||||
if name == "backrooms-grid":
|
||||
fog_start, fog_end, fog_color = 90, 500, "0.82,0.83,0.76"
|
||||
elif name == "pool-rooms":
|
||||
fog_start, fog_end, fog_color = 90, 480, "0.76,0.84,0.87"
|
||||
elif name == "staircase-to-nowhere":
|
||||
fog_start, fog_end, fog_color = 110, 700, "0.78,0.84,0.87"
|
||||
else:
|
||||
fog_start, fog_end, fog_color = 160, 1050, "0.78,0.84,0.89"
|
||||
renderer_command = [renderer, mesh, png, "--size=1600x900", "--fov-deg=72",
|
||||
f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
|
||||
f"--pitch-deg={pitch:.6f}", "--tile-skirts=on", "--shade=readable",
|
||||
"--headlamp=off", "--height-tint=on",
|
||||
f"--height-center-m={floor + 3.0:.6f}", "--height-range-m=30.0",
|
||||
"--light-direction=-0.42,-0.58,0.70", "--fog=on",
|
||||
f"--fog-start-m={fog_start}", f"--fog-end-m={fog_end}",
|
||||
f"--fog-color={fog_color}"]
|
||||
run_step(f"capture-render-{stem}", renderer_command,
|
||||
out / "logs" / f"capture-render-{stem}.log", env=env, stamped=True)
|
||||
run_step(f"capture-jpeg-{stem}", ["ffmpeg", "-nostdin", "-hide_banner",
|
||||
"-loglevel", "error", "-y", "-i", png, "-frames:v", "1", "-q:v", "4", image],
|
||||
out / "logs" / f"capture-jpeg-{stem}.log", env=env)
|
||||
quality = run_step(f"capture-quality-{stem}", [sys.executable,
|
||||
ROOT / "Standalone/Tools/verify/capture_quality.py", image],
|
||||
out / "logs" / f"capture-quality-{stem}.log", env=env)
|
||||
if "[CaptureQuality]" not in quality or not image.is_file() or image.stat().st_size > 300_000:
|
||||
raise RuntimeError(f"{stem} failed image quality or size checks")
|
||||
print(f"[L161Capture] fixture={name} view={kind} camera_m=({fmt(camera)}) "
|
||||
f"target_m=({fmt(target)}) bytes={image.stat().st_size} "
|
||||
f"sha256={sha256(image)} status=PASS", flush=True)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--out", type=Path, default=OUT_DEFAULT)
|
||||
parser.add_argument("--artifact-dir", type=Path, default=CAPTURE_DEFAULT)
|
||||
parser.add_argument("--headless-clang", type=Path, required=True)
|
||||
parser.add_argument("--headless-gcc", type=Path, required=True)
|
||||
parser.add_argument("--renderer", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
out, artifact_dir = args.out.resolve(), args.artifact_dir.resolve()
|
||||
fixture_dir = out / "fixtures"
|
||||
for directory in (out, fixture_dir, artifact_dir):
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
env = os.environ.copy()
|
||||
env["VOXELFORGE_VERIFY_OUT"] = str(out)
|
||||
env["VOXELFORGE_OWNER_DATA"] = str(ROOT / "Standalone/data/owner_world.json")
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(out / "xdg-cache")
|
||||
run_step("build-fixtures", [sys.executable,
|
||||
Path(__file__).resolve().parent / "BuildL161Fixtures.py",
|
||||
f"--out={fixture_dir}"], out / "logs" / "build-fixtures.log", env=env)
|
||||
|
||||
binaries = {"clang": args.headless_clang.resolve(), "gcc": args.headless_gcc.resolve()}
|
||||
fixtures = {name: fixture_dir / f"liminal-{name}.json" for name in
|
||||
("pool-rooms", "backrooms-grid", "staircase-to-nowhere", "terraced-hills")}
|
||||
views = {}
|
||||
floors = {}
|
||||
for name, fixture_path in fixtures.items():
|
||||
view = json.loads((fixture_dir / "views" / f"{name}.json").read_text(encoding="utf-8"))
|
||||
manifest_path, _, manifest, layout = build_plan(binaries["clang"], name,
|
||||
fixture_path, view, out, env)
|
||||
view["center_xy_m"] = view["center_xy_m"]
|
||||
floors[name] = manifest["recipe"]["surface_profile"]["h0_q"] / Q_PER_METRE
|
||||
if layout["stamp_vocabulary_version"] != 7 or not layout["stamps"]:
|
||||
raise RuntimeError(f"{name} plan lacks resolved vocabulary-7 stamps")
|
||||
write_walk_audit(fixture_dir / "walk-specs" / f"{name}.json",
|
||||
manifest_path, out / "walk-audits" / name / "walk.path")
|
||||
views[name] = view
|
||||
print(f"[L161Plan] fixture={name} stamps={len(layout['stamps'])} "
|
||||
f"floor_m={floors[name]:.3f} layout={layout['stable_identity']} status=PASS")
|
||||
|
||||
for name, fixture_path in fixtures.items():
|
||||
run_walk_audit(name, fixture_path, views[name], floors[name],
|
||||
fixture_dir / "walk-specs" / f"{name}.json", binaries["clang"], out, env)
|
||||
run_determinism(name, fixture_path, views[name], floors[name], binaries, out, env)
|
||||
|
||||
narrow_name = "backrooms-narrow-control"
|
||||
narrow_fixture = fixture_dir / f"liminal-{narrow_name}.json"
|
||||
narrow_view = json.loads((fixture_dir / "views" / f"{narrow_name}.json").read_text(encoding="utf-8"))
|
||||
narrow_manifest, _, narrow_manifest_value, _ = build_plan(binaries["clang"],
|
||||
narrow_name, narrow_fixture, narrow_view, out, env)
|
||||
narrow_floor = narrow_manifest_value["recipe"]["surface_profile"]["h0_q"] / Q_PER_METRE
|
||||
run_walk_audit(narrow_name, narrow_fixture, narrow_view, narrow_floor,
|
||||
fixture_dir / "walk-specs" / f"{narrow_name}.json", binaries["clang"],
|
||||
out, env, expected_exit=14)
|
||||
print("[L161DoorwayNegativeControl] width_m=1.200 minimum_m=1.500 expected=FAIL status=PASS")
|
||||
|
||||
for name, fixture_path in fixtures.items():
|
||||
for kind in ("eye", "wide"):
|
||||
capture(name, fixture_path, views[name], floors[name], kind=kind,
|
||||
binary=binaries["clang"], renderer=args.renderer.resolve(), out=out,
|
||||
artifact_dir=artifact_dir, env=env)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as error:
|
||||
print(f"[L161Capture] status=FAIL error={error}", file=sys.stderr, flush=True)
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,31 @@
|
||||
# L161 — More liminal looks
|
||||
|
||||
| Look | Eye height | Wide |
|
||||
|---|---|---|
|
||||
| Pool rooms |  |  |
|
||||
| Backrooms grid |  |  |
|
||||
| Staircase to nowhere |  |  |
|
||||
| Terraced hills |  |  |
|
||||
|
||||
## Result
|
||||
|
||||
Four standalone SurfaceWorld fixtures resolve from the shared stamp vocabulary. They use repeated rooms and basins, a doorway grid, a freestanding stair and arch, and nine stepped ridges. This round authored no inter-strate passages or owner content.
|
||||
|
||||
## Changes
|
||||
|
||||
- Stamp vocabulary v7 adds `fillBox`, `carveBox`, and `fillSteps`; the existing `carveArch` is reused. The new kinds are opt-in.
|
||||
- Added a flat-field liminal stamp-fixture override and a capsule walk audit with doorway-width gates. The audit checks support, collision, grade, and authored opening widths.
|
||||
- Fixtures contain 275 pool-room stamps, 129 backrooms stamps, 4 staircase/arch stamps, and 18 terrace stamps.
|
||||
|
||||
## Verification
|
||||
|
||||
- Clang 19 and GCC 14 builds passed with FP contraction off. Both binaries carry source stamp `8e9e114c1357`.
|
||||
- Each fixture matched across Clang/GCC at W1, W8, and W12: manifest, resolved-layout identity, and tile hashes were identical in all six cells.
|
||||
- Positive capsule audits passed. Pool doorways measured 7.2 m, backrooms doorways 4.0 m, and the arch opening 10.0 m. The narrow 1.2 m backrooms control failed the 1.5 m minimum as expected. The terrace walk had zero blocked samples, unsupported samples, or grade failures.
|
||||
- All eight 1600×900 captures passed image quality checks; files are 25.3–58.4 KB. Capture and audit logs are under `Saved/Linux/l161/logs/`.
|
||||
- Fast tier passed: 273 checks in 346.6 s. Both owner generators matched their pinned L130/v227 canonical baselines exactly (12/12 rows), and W1/W12 signatures matched. Summary: `Saved/Linux/l161/fast-tier/fast-summary.json`.
|
||||
- `git diff --check` passed. No Unreal `Source/` files changed; no Unreal build or medium/full verification tier was run.
|
||||
|
||||
## Remaining risk
|
||||
|
||||
The captures show untextured geometry with simple fog. The terraced wide capture uses clip radius 3 and max LOD 1 to stay within the standalone shape-build bound, so the long-range horizon read still needs an art review.
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 57 KiB |