proto: first liminal fixtures - flat field + monolith, periodic hills, raised road in fog (L141)
Standalone only: fixtures, liminal_road_override (fillDais), --audit-liminal-road-walk, capture-only distance fog. Road 65 m x 6 m, 22 m up, capsule walk 127/127. Owner hashes unchanged (v227), fast tier, Clang/GCC green. Hills repeat every 80 m but the capture reaches 56 m, so only one rise shows. Also R48 (who owns the tunnel mouth): recommends a bounded joint plan of passage ports and layer landing candidates. 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
eb47d72258
commit
9c6d261d01
15 files changed
+823
-12
No files matched your search
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build three standalone liminal SurfaceWorld fixtures from the owner input."""
|
||||
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/l141"))
|
||||
|
||||
FLAT_FIELD = {
|
||||
"version": 1,
|
||||
"kind": "flatField",
|
||||
"minimum_arena_clearance_m": 0.0,
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
base = json.loads(OWNER.read_text(encoding="utf-8"))
|
||||
|
||||
flat = dict(base)
|
||||
flat["surface_profile_override"] = dict(FLAT_FIELD)
|
||||
flat["motif_family_override"] = {
|
||||
"version": 1,
|
||||
"strate_index": 0,
|
||||
"placement_policy": "seed,strate,stable_id",
|
||||
"motifs": [
|
||||
{
|
||||
"stable_id": "liminal/oversized-familiar-monolith-v1",
|
||||
"kind": "cappedCylinder",
|
||||
"radius_m": 1.6,
|
||||
"height_m": 26.0,
|
||||
"route_side_offset_m": 42.0,
|
||||
"placement_radius_m": 0.0,
|
||||
"fillet_radius_m": 0.6,
|
||||
"blend_width_m": 1.2,
|
||||
"minimum_spacing_m": 0.0,
|
||||
"yaw_quarter_turns": 0,
|
||||
"attachment": "seated_floor",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
hills = dict(base)
|
||||
hills["surface_profile_override"] = {
|
||||
"version": 1,
|
||||
"kind": "periodicHills",
|
||||
"amplitude_x_m": 2.2,
|
||||
"amplitude_y_m": 2.2,
|
||||
"wavelength_x_m": 80.0,
|
||||
"wavelength_y_m": 80.0,
|
||||
"minimum_arena_clearance_m": 0.0,
|
||||
}
|
||||
|
||||
road = dict(base)
|
||||
road["surface_profile_override"] = dict(FLAT_FIELD)
|
||||
road["liminal_road_override"] = {
|
||||
"stable_id": "liminal/raised-road-v1",
|
||||
"strate_index": 0,
|
||||
"position_xy_m": [240.0, 180.0],
|
||||
"half_extents_m": [32.5, 3.0, 0.75],
|
||||
"deck_height_above_field_m": 22.0,
|
||||
"yaw_quarter_turns": 0,
|
||||
}
|
||||
|
||||
for name, fixture in (("flatField-monolith", flat),
|
||||
("periodicHills", hills),
|
||||
("raised-road", road)):
|
||||
path = args.out / f"liminal-{name}.json"
|
||||
path.write_text(json.dumps(fixture, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L141Fixture] name={name} file={path} status=PASS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,389 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run L141 prototype determinism checks and create eye-level/wide 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/l141"))
|
||||
CAPTURE_DEFAULT = Path(__file__).resolve().parent / "captures"
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
Q_PER_METRE = 1024.0
|
||||
FOG_COLOR = "0.66,0.73,0.77"
|
||||
|
||||
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
|
||||
from runner import bounded_run, 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, timeout_seconds: int = 1740) -> str:
|
||||
expected_stamp = current_stamp() if stamped else None
|
||||
if stamped:
|
||||
binary = Path(str(command[0]))
|
||||
probe = bounded_run([str(binary), "--print-source-stamp"], timeout=15)
|
||||
if probe is None or probe.returncode != 0 or first_stamp(probe.stdout or "") != expected_stamp:
|
||||
raise RuntimeError(f"{name} refused a stale executable: {binary}")
|
||||
started = time.monotonic()
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run([*LIMIT, f"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"[L141Run] 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 != 0:
|
||||
raise RuntimeError(f"{name} failed; see {log}")
|
||||
return output
|
||||
|
||||
|
||||
def route_frame(manifest_path: Path) -> tuple[tuple[float, float, float],
|
||||
tuple[float, float, float]]:
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
strata = [entry for entry in manifest["plan"]["strates"]
|
||||
if entry.get("route") and len(entry["route"]) >= 2]
|
||||
if not strata:
|
||||
raise RuntimeError(f"no planned SurfaceWorld route in {manifest_path}")
|
||||
route = strata[0]["route"]
|
||||
points = [tuple(float(value) / Q_PER_METRE for value in item["position_q"])
|
||||
for item in route]
|
||||
lengths = [math.dist(a, b) for a, b in zip(points, points[1:])]
|
||||
total = sum(lengths)
|
||||
if total <= 1.0e-6:
|
||||
raise RuntimeError(f"degenerate planned route in {manifest_path}")
|
||||
|
||||
def at(distance: float) -> tuple[float, float, float]:
|
||||
remaining = max(0.0, min(total, distance))
|
||||
for index, (a, b, length) in enumerate(zip(points, points[1:], lengths)):
|
||||
if remaining <= length or index == len(lengths) - 1:
|
||||
if length <= 1.0e-9:
|
||||
return b
|
||||
t = remaining / length
|
||||
return tuple(a[axis] + (b[axis] - a[axis]) * t for axis in range(3))
|
||||
remaining -= length
|
||||
return points[-1]
|
||||
|
||||
center = at(total * 0.5)
|
||||
before, after = at(total * 0.5 - 1.0), at(total * 0.5 + 1.0)
|
||||
dx, dy = after[0] - before[0], after[1] - before[1]
|
||||
length = math.hypot(dx, dy)
|
||||
if length <= 1.0e-9:
|
||||
raise RuntimeError(f"degenerate route tangent in {manifest_path}")
|
||||
return center, (dx / length, dy / length, 0.0)
|
||||
|
||||
|
||||
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 point_toward(camera, target) -> tuple[float, ...]:
|
||||
yaw, pitch = camera_angles(camera, target)
|
||||
return (*camera, yaw, pitch)
|
||||
|
||||
|
||||
def stamp(layout: dict, stable_id: str) -> dict:
|
||||
for record in layout.get("stamps", []):
|
||||
if record.get("stable_id") == stable_id:
|
||||
return record
|
||||
raise RuntimeError(f"missing resolved stamp {stable_id}")
|
||||
|
||||
|
||||
def make_views(name: str, manifest_path: Path, layout_path: Path):
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
layout = json.loads(layout_path.read_text(encoding="utf-8"))
|
||||
center, tangent = route_frame(manifest_path)
|
||||
tx, ty, _ = tangent
|
||||
nx, ny = -ty, tx
|
||||
base_height = manifest["recipe"]["surface_profile"]["h0_q"] / Q_PER_METRE
|
||||
if name == "flatField-monolith":
|
||||
monolith = stamp(layout, "liminal/oversized-familiar-monolith-v1")
|
||||
sx, sy, _ = (value / 1.0e6 for value in monolith["position_um"])
|
||||
floor = monolith["floor_um"] / 1.0e6
|
||||
eye_camera = (sx - tx * 36.0, sy - ty * 36.0, floor + 1.6)
|
||||
eye_target = (sx, sy, floor + 10.5)
|
||||
wide_camera = (sx - tx * 42.0 - nx * 18.0,
|
||||
sy - ty * 42.0 - ny * 18.0, floor + 42.0)
|
||||
wide_target = (sx, sy, floor + 8.0)
|
||||
return {
|
||||
"eye": (eye_camera, eye_target, 20),
|
||||
"wide": (wide_camera, wide_target, 24),
|
||||
"height_center": floor,
|
||||
"height_range": 38.0,
|
||||
}
|
||||
if name == "periodicHills":
|
||||
# Route points are capsule centers: +0.72 m places the view at 1.60 m above ground.
|
||||
eye_camera = (center[0], center[1], center[2] + 0.72)
|
||||
eye_target = (center[0] - tx * 45.0, center[1] - ty * 45.0,
|
||||
center[2] + 0.72)
|
||||
wide_camera = (center[0] - tx * 20.0 - nx * 20.0,
|
||||
center[1] - ty * 20.0 - ny * 20.0, center[2] + 34.0)
|
||||
wide_target = (center[0] - tx * 45.0, center[1] - ty * 45.0,
|
||||
center[2] + 0.72)
|
||||
return {
|
||||
"eye": (eye_camera, eye_target, 20),
|
||||
"wide": (wide_camera, wide_target, 28),
|
||||
"height_center": base_height,
|
||||
"height_range": 5.0,
|
||||
}
|
||||
road = stamp(layout, "liminal/raised-road-v1")
|
||||
rx, ry, rz = (value / 1.0e6 for value in road["position_um"])
|
||||
half_length, half_width, half_thickness = (
|
||||
value / 1.0e6 for value in road["half_extents_um"])
|
||||
deck = rz + half_thickness
|
||||
eye_camera = (rx - half_length + 14.0, ry, deck + 1.6)
|
||||
eye_target = (rx + half_length, ry, base_height)
|
||||
wide_camera = (rx, ry - 28.0, deck + 24.0)
|
||||
wide_target = (rx + 4.0, ry, base_height + 4.0)
|
||||
return {
|
||||
"eye": (eye_camera, eye_target, 24),
|
||||
"wide": (wide_camera, wide_target, 26),
|
||||
"height_center": base_height + 11.0,
|
||||
"height_range": 32.0,
|
||||
"road_deck": deck,
|
||||
"road_width": 2.0 * half_width,
|
||||
}
|
||||
|
||||
|
||||
def headless_command(binary: Path, fixture: Path, camera, mesh: Path,
|
||||
manifest: Path, layout: Path, workers: int,
|
||||
clip_radius: int, *, hash_tiles: bool = False,
|
||||
audit_road: bool = False, max_level: int = 0) -> 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(point_toward(camera, (camera[0], camera[1] + 1.0, camera[2])))}",
|
||||
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_road:
|
||||
command.append("--audit-liminal-road-walk")
|
||||
return command
|
||||
|
||||
|
||||
def determinism_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, layout or per-tile hashes")
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
layout = json.loads(layout_path.read_text(encoding="utf-8"))
|
||||
manifest_digest = (manifest["content_hash"],manifest["recipe"]["content_hash"],
|
||||
manifest["plan"]["content_hash"])
|
||||
return ("|".join(manifest_digest),layout["stable_identity"],"\n".join(tile_hashes))
|
||||
|
||||
|
||||
def run_determinism(name: str, fixture: Path, view, *, binaries: dict[str, Path],
|
||||
out: Path, env: dict[str, str]) -> None:
|
||||
camera, _, _ = view["eye"]
|
||||
clip_radius = 1
|
||||
max_clip = 0
|
||||
baseline = None
|
||||
cells = 0
|
||||
for compiler, workers in ((compiler, workers)
|
||||
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 = run_dir / "feature.vfmesh"
|
||||
manifest = run_dir / "manifest.json"
|
||||
layout = run_dir / "layout.json"
|
||||
command = headless_command(binaries[compiler], fixture,camera,mesh,manifest,
|
||||
layout,workers,max_clip,hash_tiles=True)
|
||||
output = run_step(tag,command,out/"logs"/f"determinism-{tag}.log",
|
||||
env=env,stamped=True)
|
||||
digest = determinism_digest(output,manifest,layout)
|
||||
if baseline is None:
|
||||
baseline = digest
|
||||
elif digest != baseline:
|
||||
raise RuntimeError(f"{name} diverged at {compiler} W{workers}; see determinism logs")
|
||||
cells += 1
|
||||
print(f"[L141Determinism] fixture={name} compiler_worker_cells={cells} identical=yes status=PASS",
|
||||
flush=True)
|
||||
|
||||
|
||||
def export_and_capture(name: str, fixture: Path, views: dict, *,
|
||||
headless: Path, renderer: Path, out: Path,
|
||||
artifact_dir: Path, env: dict[str, str]) -> None:
|
||||
for view_name in ("eye", "wide"):
|
||||
camera, target, clip_radius = views[view_name]
|
||||
clip_radius = min(clip_radius,7)
|
||||
stem = f"{name}-{view_name}"
|
||||
mesh = out / "capture-meshes" / f"{stem}.vfmesh"
|
||||
manifest = out / "capture-manifests" / f"{stem}.json"
|
||||
layout = out / "capture-layouts" / f"{stem}.json"
|
||||
png = out / "capture-png" / f"{stem}.png"
|
||||
image = artifact_dir / f"{stem}.jpg"
|
||||
for path in (mesh,manifest,layout,png,image):
|
||||
path.parent.mkdir(parents=True,exist_ok=True)
|
||||
command = headless_command(headless,fixture,camera,mesh,manifest,layout,12,
|
||||
clip_radius,max_level=0)
|
||||
output = run_step(f"capture-{stem}-export",command,
|
||||
out/"logs"/f"capture-{stem}-export.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)
|
||||
height_center = views["height_center"]
|
||||
height_range = views["height_range"]
|
||||
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=off","--shade=readable",
|
||||
"--headlamp=off","--height-tint=on",f"--height-center-m={height_center:.6f}",
|
||||
f"--height-range-m={height_range:.6f}","--light-direction=-0.42,-0.58,0.70",
|
||||
"--fog=on","--fog-start-m=30","--fog-end-m=105",f"--fog-color={FOG_COLOR}"]
|
||||
run_step(f"capture-{stem}-render",renderer_command,
|
||||
out/"logs"/f"capture-{stem}-render.log",env=env,stamped=True)
|
||||
run_step(f"capture-{stem}-jpeg",["ffmpeg","-nostdin","-hide_banner","-loglevel",
|
||||
"error","-y","-i",png,"-frames:v","1","-q:v","4",image],
|
||||
out/"logs"/f"capture-{stem}-jpeg.log",env=env)
|
||||
quality = run_step(f"capture-{stem}-quality",
|
||||
[sys.executable,ROOT/"Standalone/Tools/verify/capture_quality.py",image],
|
||||
out/"logs"/f"capture-{stem}-quality.log",env=env)
|
||||
if "[CaptureQuality]" not in quality or not image.is_file() \
|
||||
or image.stat().st_size > 300_000:
|
||||
raise RuntimeError(f"{stem} capture failed quality or size checks")
|
||||
eye_height = "1.600m" if view_name == "eye" else "wide"
|
||||
print(f"[L141Capture] fixture={name} view={view_name} eye_height={eye_height} "
|
||||
f"camera_m=({fmt(camera)}) target_m=({fmt(target)}) bytes={image.stat().st_size} "
|
||||
f"sha256={sha256(image)} status=PASS",flush=True)
|
||||
|
||||
|
||||
def run_road_walk_audit(fixture: Path, view, *, headless: Path,
|
||||
out: Path, env: dict[str, str]) -> None:
|
||||
camera = view["eye"][0]
|
||||
run_dir = out / "road-walk-audit"
|
||||
run_dir.mkdir(parents=True,exist_ok=True)
|
||||
output = run_step("road-walk-audit",headless_command(headless,fixture,camera,
|
||||
run_dir/"audit.vfmesh",run_dir/"manifest.json",run_dir/"layout.json",12,1,
|
||||
audit_road=True),out/"logs"/"road-walk-audit.log",env=env,stamped=True)
|
||||
if not re.search(r"\[LiminalRoadWalkAudit\].*status=PASS",output):
|
||||
raise RuntimeError("raised road failed the Core capsule walk audit")
|
||||
|
||||
|
||||
def measure_near_ready(fixture: Path, views: dict, *, headless: Path,
|
||||
out: Path, env: dict[str, str]) -> None:
|
||||
uptime = subprocess.run(["uptime"],cwd=ROOT,capture_output=True,text=True,check=True).stdout.strip()
|
||||
load_match = re.search(r"load average:\s*([0-9.]+)",uptime)
|
||||
if not load_match or float(load_match.group(1)) >= 1.5:
|
||||
raise RuntimeError(f"near-ready timing requires a quiet machine: {uptime}")
|
||||
print(f"[L141TimingEnvironment] window=one W12 run after one warmup uptime='{uptime}' "
|
||||
f"status=PASS",flush=True)
|
||||
camera = views["eye"][0]
|
||||
warm_dir = out / "near-ready" / "warmup"
|
||||
timed_dir = out / "near-ready" / "measured"
|
||||
warm_dir.mkdir(parents=True,exist_ok=True)
|
||||
timed_dir.mkdir(parents=True,exist_ok=True)
|
||||
|
||||
def command(directory: Path) -> list[object]:
|
||||
return [headless,fixture,"--workers=12","--world-assembler-prototype",
|
||||
"--seed=0","--stream-benchmark","--stream-pipeline","--force-generate",
|
||||
"--clip-radius=1","--max-clip-level=2","--max-level=2",
|
||||
"--render-distance-chunks=0",
|
||||
f"--camera={fmt(point_toward(camera,(camera[0],camera[1]+1.0,camera[2])))}",
|
||||
f"--world-assembler-manifest-output={directory/'manifest.json'}",
|
||||
f"--resolved-layout-output={directory/'layout.json'}"]
|
||||
|
||||
run_step("near-ready-warmup",command(warm_dir),
|
||||
out/"logs"/"near-ready-warmup.log",env=env,stamped=True)
|
||||
output = run_step("near-ready-measured",command(timed_dir),
|
||||
out/"logs"/"near-ready-measured.log",env=env,stamped=True)
|
||||
match = re.search(r"^\[WorldAssemblerNearLODPipelineReady\] lods=0-2 "
|
||||
r"tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",output,re.M)
|
||||
if not match or match.group(1)!=match.group(2):
|
||||
raise RuntimeError("near-ready run did not complete the full LOD 0-2 set")
|
||||
print(f"[L141NearReady] window=one W12 run after one warmup clip_radius=1 "
|
||||
f"tiles={match.group(1)}/{match.group(2)} cold_ready_s={match.group(3)} 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 = args.out.resolve()
|
||||
artifact_dir = 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,
|
||||
ROOT/"Standalone/Research/WorldAssemblerM3/Linux141/BuildL141Fixtures.py",
|
||||
f"--out={fixture_dir}"],out/"logs"/"build-fixtures.log",env=env)
|
||||
|
||||
binaries = {"clang":args.headless_clang.resolve(),"gcc":args.headless_gcc.resolve()}
|
||||
fixtures = {
|
||||
"flatField-monolith":fixture_dir/"liminal-flatField-monolith.json",
|
||||
"periodicHills":fixture_dir/"liminal-periodicHills.json",
|
||||
"raised-road":fixture_dir/"liminal-raised-road.json",
|
||||
}
|
||||
probed = {}
|
||||
for name,fixture in fixtures.items():
|
||||
probe_dir=out/"plan-probes"/name
|
||||
probe_dir.mkdir(parents=True,exist_ok=True)
|
||||
camera=(0.0,0.0,-500.0)
|
||||
output=run_step(f"plan-probe-{name}",headless_command(binaries["clang"],fixture,
|
||||
camera,probe_dir/"probe.vfmesh",probe_dir/"manifest.json",
|
||||
probe_dir/"layout.json",12,1),out/"logs"/f"plan-probe-{name}.log",
|
||||
env=env,stamped=True)
|
||||
if "[WorldAssemblerFullManifest]" not in output:
|
||||
raise RuntimeError(f"{name} probe omitted its full plan manifest")
|
||||
probed[name]=make_views(name,probe_dir/"manifest.json",probe_dir/"layout.json")
|
||||
|
||||
for name,fixture in fixtures.items():
|
||||
run_determinism(name,fixture,probed[name],binaries=binaries,out=out,env=env)
|
||||
run_road_walk_audit(fixtures["raised-road"],probed["raised-road"],
|
||||
headless=binaries["clang"],out=out,env=env)
|
||||
for name,fixture in fixtures.items():
|
||||
export_and_capture(name,fixture,probed[name],headless=binaries["clang"],
|
||||
renderer=args.renderer.resolve(),out=out,artifact_dir=artifact_dir,env=env)
|
||||
measure_near_ready(fixtures["raised-road"],probed["raised-road"],
|
||||
headless=binaries["clang"],out=out,env=env)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as error:
|
||||
print(f"[L141Capture] status=FAIL error={error}",file=sys.stderr,flush=True)
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,40 @@
|
||||
# L141 — first liminal layer
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
The standalone fixtures and six captures are ready for review. The flat field has one oversized capped-cylinder monolith. The road is a 65 m `fillDais` platform, 6 m wide, with its deck 22 m above a flat field. Fog supplies the road's cloud-sea backdrop in these standalone images.
|
||||
|
||||
The `periodicHills` fixture uses the validated 80 m periods and 2.2 m amplitudes on both axes. Its field repeats exactly, but these captures show one broad rise rather than hills repeating to the horizon. The validated period is longer than the 56 m eye-level capture radius; the wider full-detail capture exceeded Core's 160-million-node interval bound. Smaller periods fail the existing profile validator. A capture extent that trims vertical tiles independently from horizontal tiles is the next useful step; this round leaves the Core profile limits unchanged.
|
||||
|
||||
## Changes
|
||||
|
||||
- Reused Core's `flatField`, `periodicHills`, `fillMonolith`, and `fillDais` support. Added standalone fixture inputs and runners in `BuildL141Fixtures.py` and `CaptureL141.py`.
|
||||
- Added `liminal_road_override` as a standalone adapter to the existing free-placement `fillDais` stamp, plus `--audit-liminal-road-walk`. The walk audit samples the Core field with the existing capsule sampler and checks width and grade.
|
||||
- Added optional distance fog to `LinuxOffscreenCapture` for the screenshots. The fog is capture-only; Unreal cloud rendering remains Unreal-side.
|
||||
- No evaluator change or Unreal `Source/` change. Owner output hashes match their pinned L130/v227 canonical rows in the fast tier.
|
||||
|
||||
## Measurements
|
||||
|
||||
- Determinism: each fixture matched across Clang and GCC at W1, W8, and W12 (six cells per fixture). The matrix used LOD0, clip radius 1; each cell compares recipe/plan hashes, resolved-layout identity, and per-tile hashes. Individual logs are in `Saved/Linux/l141/logs/determinism-*.log`.
|
||||
- Raised-road capsule walk: 127 stations, 8,001 capsule samples, zero blocked samples, zero unsupported stations, 6.000 m width against a 0.680 m capsule diameter, and 0.000° grade. See `Saved/Linux/l141/logs/road-walk-audit.log`.
|
||||
- Near-ready: one W12 LOD0–2 sample after one warm-up, clip radius 1, 139/139 tiles. The measured `cold_ready_s` was **0.173886 s**. The window was one run after one warm-up; `uptime` showed a 1-minute load average of 1.31 and no other build was running. See `Saved/Linux/l141/logs/near-ready-measured.log`.
|
||||
- Each capture is 1600×900 and passed `capture_quality.py`. Eye views place the camera 1.6 m above the field or road deck. Camera coordinates and image hashes are recorded in the capture runner output; export and render logs are under `Saved/Linux/l141/logs/`.
|
||||
|
||||
## Checks
|
||||
|
||||
- Clang 19 and GCC standalone builds passed with FP contraction off, four jobs, and source stamp `0a2828c398aa62870690667901d44f2daf5b1a5547b9640da6dbb01df721af8b`.
|
||||
- `python3 Standalone/Tools/verify/verify.py fast --out Saved/Linux/l141 --overall-timeout 1740` passed in 285.7 s. Owner SurfaceWorld and FloatingIslands W1/W12 canonical hashes matched the L130/v227 baselines.
|
||||
- Fixture determinism, dedicated road walk audit, six standalone captures, and all six image-quality checks passed.
|
||||
- Python bytecode compilation and `git diff --check` passed.
|
||||
- An exploratory broad `--audit-resolved-layout-passages` invocation also ran unrelated passage ripple checks and reported 2/3 passage ripple records passing, with `passage/1-2/0` failing. The focused liminal road audit passes; no legacy threshold was changed.
|
||||
- No Unreal build was run because no Unreal `Source/` file changed. No timing matrix was requested or run.
|
||||
|
||||
## Remaining risk
|
||||
|
||||
The hill screenshots do not yet show multiple full repeats to the horizon. The profile itself is exactly periodic; the standalone capture volume is too short at the current full-detail safety bound. The raised road is geometric and walk-audited, while its clouds are represented by fog in the capture renderer.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Reference in new issue
Block a user