From 9c6d261d017ea4c2afa8373c814d29f8124a4dc7 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Sun, 1 Nov 2026 08:43:24 +0100 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1 --- .../Linux141/BuildL141Fixtures.py | 84 ++++ .../WorldAssemblerM3/Linux141/CaptureL141.py | 389 ++++++++++++++++++ .../WorldAssemblerM3/Linux141/REPORT.md | 40 ++ .../captures/flatField-monolith-eye.jpg | Bin 0 -> 26770 bytes .../captures/flatField-monolith-wide.jpg | Bin 0 -> 24016 bytes .../Linux141/captures/periodicHills-eye.jpg | Bin 0 -> 23693 bytes .../Linux141/captures/periodicHills-wide.jpg | Bin 0 -> 24629 bytes .../Linux141/captures/raised-road-eye.jpg | Bin 0 -> 24503 bytes .../Linux141/captures/raised-road-wide.jpg | Bin 0 -> 33966 bytes .../WorldAssemblerM3/Research48/PROMPT.md | 36 ++ .../WorldAssemblerM3/Research48/REPORT.md | 43 ++ Standalone/Source/LinuxOffscreenCapture.cpp | 76 +++- Standalone/Source/StreamBenchmarkHeadless.cpp | 32 +- Standalone/Source/StreamBenchmarkPipeline.h | 72 +++- Standalone/Source/WorldAssemblyPrototype.h | 63 +++ 15 files changed, 823 insertions(+), 12 deletions(-) create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/BuildL141Fixtures.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/CaptureL141.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/captures/flatField-monolith-eye.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/captures/flatField-monolith-wide.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/captures/periodicHills-eye.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/captures/periodicHills-wide.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/captures/raised-road-eye.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux141/captures/raised-road-wide.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Research48/PROMPT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Research48/REPORT.md diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/BuildL141Fixtures.py b/Standalone/Research/WorldAssemblerM3/Linux141/BuildL141Fixtures.py new file mode 100644 index 0000000..8274c45 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux141/BuildL141Fixtures.py @@ -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() diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/CaptureL141.py b/Standalone/Research/WorldAssemblerM3/Linux141/CaptureL141.py new file mode 100644 index 0000000..5d295a6 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux141/CaptureL141.py @@ -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) diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux141/REPORT.md new file mode 100644 index 0000000..0f608c8 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux141/REPORT.md @@ -0,0 +1,40 @@ +# L141 — first liminal layer + +![flatField monolith, eye level](captures/flatField-monolith-eye.jpg) +![periodicHills, eye level](captures/periodicHills-eye.jpg) +![raised road, eye level](captures/raised-road-eye.jpg) + +![flatField monolith, wide](captures/flatField-monolith-wide.jpg) +![periodicHills, wide](captures/periodicHills-wide.jpg) +![raised road, wide](captures/raised-road-wide.jpg) + +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. diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/captures/flatField-monolith-eye.jpg b/Standalone/Research/WorldAssemblerM3/Linux141/captures/flatField-monolith-eye.jpg new file mode 100644 index 0000000000000000000000000000000000000000..547cc0f60f3ce67648e140676136bcaf7ff4a571 GIT binary patch literal 26770 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 ScwoZ=8y@)2^?*2``o91bh?GJA literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/captures/flatField-monolith-wide.jpg b/Standalone/Research/WorldAssemblerM3/Linux141/captures/flatField-monolith-wide.jpg new file mode 100644 index 0000000000000000000000000000000000000000..43b92bc28587b54a2329ca7d5fb986d4aec11dbd GIT binary patch literal 24016 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/captures/periodicHills-eye.jpg b/Standalone/Research/WorldAssemblerM3/Linux141/captures/periodicHills-eye.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e76c43327ebcb925325eab50e0706f66120ada66 GIT binary patch literal 23693 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 M`tmiOf2s9<0hlOPvH$=8 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux141/captures/periodicHills-wide.jpg b/Standalone/Research/WorldAssemblerM3/Linux141/captures/periodicHills-wide.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3c8097adb590ffc777af3a860017904505940bdf GIT binary patch literal 24629 zcmeI43!GHdmF`b9Rc(h6tuj?cS`AF+-btqr9bWMj_0-%hB!n?lrV6Be#gIlCe2W6c zM~jn$TTuxKp!mSHd6fvEL$+3&o=X3pRmuvo4$09H}uA*yY;y@e(Z>E z7P>8)^$#cCbIJ5+)%*Ik4gT4^b(srVYAb4!HLd#VSuA!fG`WxFlE8G!FDi6fJT)`w z|3ys0FMjE&;jJ&tICs{C3;Vq}bWHDocfYvg#fIY6dxtFAFiRg3=2j1^AG78+vywYz z{5Z5=YtKTlDp}jLiTA`w#fi;cMU&%A(X)5YWU``}|5}=9)y}9c*FoG_iBZk31kKwI z{k3h)cyQ)>FFp3~woR9=+3>)jey`ni&f0~2?j3veo-HR|wC3{Lm!ERsmajM7z4hM5 zufFD<_qI3w^rH8!x#s;X0~fW;`_bXoKe24dnB~JqEIsyuS;Mk33Oy@YCMCUOt%2T@ z^lF<7?-nqt@Ct<{hcGR8^Ku*QpS1*`JpNyM+>B{Q0r$71F@O}N?c=CdeSGPSpe(9e2YxkW$|HUWIIsfI~zWq{<2YNT$_uAd- zfA;=SwS_99cS}+RDKvR4wb-Jn1vjXzsOVa#nb<;9>xn&@nt`&aII*S4PN*$RYVGvb zQ^)nHat`Aa3tcBQ+lQpey}c%{*L5d;RMlniomUl(ShMjVZo$}Vj z|6}|3Lsx!x*Eu&|zx}4Yd$#O6;?N&ndTPtr$E`f_tGoBCyy}L1{mwXT!b2~ z?fPWGMK`|IcEcIn4&8nIjuZbp;%C#xFMfa1wWBug-G0%5>DS(Q>4L_rpYw9zd^#F@ zWxRCQBVwjQnOwq4guG0s&ht{)d`P>Zxojd6jd<+#ytwa$GU2R+*Hm3Doy$eiPsn-BeN&d@&;iu+I5`0$h;%vwAkxg`Fd&}v8RE?zp} z!<~BuF1~Zm`encW(>+)1IQjX$@6SBx{;^jrUp{X9!RdPsK77(eSFbs6=hEc7ugtpf zzH1-(@Qn%kR<+%5%SF>adGGWq@49Qt&R=4vJqI>jJN3eCJ4VfzF>_iblnME1&-Wt< zFYaNnL|nM1%zPOy>S3oWMsc$Ra6t6aS+4h z)m&#}ypr9#Qj^kmW$U;hEw`;4yzrl=Py6a!UGLwJEbe~poJSsu9NXUYf8ec=Z{+%| z_~VBMXANI9?wxtZ{{G23zV*q9tq1PK>`(=XUH@5Cbq z9=i67n?LjB%~arU%YqO%;(l@+yC&eW zqC7VXITgyjDtIfZC`HhA(Xs*}6+aZ3yovU0xt!Z1sjzf|v&oSswkU2TYZab)dL9Rf zQY{r;ZLy-3_QBi5Ef>K^PyXUt>0pqA*A&&=i>O|#PPu3nR!jNPhQ3q2u$ zOVMqUC6BFpZ2i+xo9tQB)LcX6E+ZF;Ey?Dd?!zkB8y%b?}@yZzw?jJH3AJz1y$b-+lY=XUCkf`-#16*AJW5{PkJ>)&L` z+-zRj?E2L~6U~X7Q)SND(xL*!dgseY9F84#pxU*yXlhllYqDmN4)$#=)U;OUpeuK< z%l-8%+7)FmTz8STp=SsG)>!1@RZW4s@Vmp?G~9g@nzUrm6nZkPN$So!F%#N+`i6&> z9sH#27wN+uKK;X$V|ommQqy|oKV90ral(xs)#q;d`cF<@Jo$n@?fX;1oU@m`HTi{y zAD=vOXzl)f-Jh?&u-;o|-Td2p^!di~)jNKgJ^9+mnr)ZAKJ(Ap@4IEwHQUeMf9JAQ zXKoiEKK@p>JtwR>y5F6<7B5*kr}rtJ?74c(gx&jD{>|7l$E^e8n2AKh98HeLk5D>T zBG@`XALhNzhfAM+$gB1(VQu%bQZglR2$^@WHql$kk;j2!`A{O8jimjYE@R%Dk%!~y zv1}q;mrZofSIhM?xrUT^eIlA`w6p1;x-K|rwsNQ(O|tR0q=80S<7g37YAdm_y>0*= zL$a)|G!uTsL-FvxFt$NuJNboA=fC{MAjgEUhu$lm zdGA}RuD^QyCnrtK+;rGIXMOGCN9scF{Gi|MOMdNL_J7j;N9~?9eEHKAD;Do~r^m(X zp5Ob&W7nQ_Qr(>F<_``X9C!H@tNyOxx*NX!)VOEIzyI#O0V_W3e(5Po@G^>87S)P5<=w_TJW0(bS+FY@3J7<)IwH8x$(9kEfY(ju)Mq{?+Jox=Wi8v@N~JB zE3Baf<)T4Ph4kPThvm-Aw`NL2i7<}i(9r^iV^aI4ixZ#!3=tC&(6m$3 zOL%$0RyZ#bWkecY(M;~JLA%$UcJYt*zF9mnck_1-T=c+|TUS>-GVAyk+P*VyZFtzm z8~^BzS3T0ach&2+kmI_;%fhL7F9|D~IkOrHP3=DnBP_lK>+#`St}`|yC`3ZR-uDoU&+$XW=Dvq+8R zqWGdBF`D2<{POz6Hd5^RqEI+|tACiN{$afFb9i;ekE|gG%N_{k8?uSH`M{BL5lbl~ zJy}aTuZRjjD|V- zndm->PHqj(EB(kcPR%sNQs=hZHe_R_ae94h?(oL!?1p^eDB=vX7}R=4-zi*O7i=v5 zH@jg@dT`s|!tG0n4-LYWHbo!DS z`hM8K%}h)xVE{rC@9F->`FUuZ1ICeZ6sN;p0A28|J*R z@1BO~5B%e6ckbW0bwpDzhm6* z2cIx@{MT20JQTfgC>yIi(tUH`V;U4|@b-cZZmy8gHK?>5xhY^%C%!BH}5+e-FkcLfEOR$GDmCIz3X21SEM=pzA^B$?Oyfx zxAva+$H~X9yyYMM=BqQ#`TNbEoc81ut&=vaIQT~IQy!UrYqmytT9smj##>dA_^M=8 zkQoN)90ABh7loKwq7akj&(`2c5<|_fSQRYzN}(nx9D*OU3Us83J-wPH&gxoP5OVPV zD`8|~Av_VI)V8A8#YKWt`(N>Kxj_(Pw23okt0M#J$x-H{2i8YeusEu96p_+)Yg_Lm z#7GVp4wH7BfZ!Cm4b6Ukz;LWT0MobJc3Izzm9LJ3Eo1tm=8jHR&RFpLtxxpo-Q}{O zBmAuc?p`-wI6LN!fm@r8t$bxc|89$++mOxmO)wR1o$z?**U$9!uXuFz(^u#S)Xh{c zC>?&Kq1f%wHh=30MaNEhYiLFZE2m7qH9M_xer?O+!_(=}iwcEJBU{)m78ef9X6qMh z%nh9}vVUQHKTWj#uN)CzFMkL-03d$!g&epyq9DghTVCl!To_O!^%IfnoRnQNdeNH3 z_wL+(D=hW*?jeamgffUI{fkY<x+)JlEXXvD;^6zx?ERy#;DuBtxvJrV^YVVBQnQ|lp6;8 zqf3Rp{8s;`8=8j1k3zwd^UtMN;xVYcj?Mwu3q@*Xz#s48@5e7@Z{<9Q>k}melp^@Ti*S2@=>)X zpYhx8p8vtyZ<02KIoIREOZL>~))G|wxTaf%yiC4u^)m)>=wjwU7|f+YL+ zWq;EAMQwxc7&^Rh;QSMo1Oe2|MX@pY&F315#jS@g>2+D(=B-uDTX8>{^<86>Mhl^2 zTb~EdUodpUpoN=z^_e|%mlM!|2xy*Hmam@eoD2))Cc55vZy{i~Oaj zr{@YS6)lUp%8KhtF<9eeMOanVJR)@0D$nE*653X01_o3_8&SrdT?S1o5W2;=TIw?l zCfN+{YAc$WU?e--Kt(=CKT#4{(cHA2hFwjY#X|N>v=xMgwF&6g70NMt&FHQ$xv=%{ z)?Syvf;OnD_`B_ryVrfQ4dzb&nknxS-A5g3%Nz+}(>6LoOb0FOyZYIoGtz0)G0lGW zUw0xnF&csHyC&0k6y}qkDb>6k%3&{aNL<@IRzGSev}1Jl_6U5&LK$V!BbUuPX3WjU zUwrzB@1OFu6$f`;Ff4Ik!utN)^7kf|q^hd6LQi<`BBpv?PY(;B7>;2mfrL$qf7#Pi zOlNb{vM=$gCnF(7VoRfu!l2?B1A{ZB$a@xU{2~ZV6npuez?A^^9z7aoGJlhC28|JXw zP>Q#to8ue#ngkE^D=yrOw&d?A6gJed?P7Z6j7u!Hd$klHA3T5LqGG@hNz1~%w1;M- zb)u$4RbAU@FiKVV3(>P7-8eOeR87eniPDX+Y(AE)A6TExHL#UZzBm2W*{aY51F&_hI^-M+nsH|jsXj|QMDB2SqIAUS~zOa z=ToS#b*J3};10YBG^%Dq9FF9GF?^hx^1P!N)hQb7etBcmi;!i~i#r<4a+@HOk493V zjELYT!eT(AF-)pj=cwW3b&xSdQW5Hm@J8m&q_A&}`@ve>8jUER#OlS46znjQ&hgV3 zmk*07Q7V!Von?Ph*_F*;e78Eb_S6GXG0Il_sq@@GfvCS}k-AYcAhff`|>GCjv3uP9rVktU;?M&Pg|IYMB1x#_9RQY^@81!wI_0W*WOo z6^G7f5GPpvurFI*m&?}?CliU;X;DFXY3K}e8C!QoUpXc5^pEm8qrR7kp%C#f8{tff zJL+{5Ln0w=w}+@vcc6)Jf?MsONQ4Js!V$5@^F$eJjXQ5$5$({z)-v87&%@6E1+u`MGsOV zi(7RWRU|8?I7P|3(!F`1^q*1}Cpx5Qk*mL*+$cz|x=xGdu@uRMi2$NuDMhm_14``> zht^)-#+Z~GAj)k+7OU7wNP$cy2=xuqqy}bYNda5ar_>t*QnSTu$Ag?M<#><{=j(>u z{i98P;PHArM3dq_{2gD{H!{GFuouPwg&B+h>OdpP#>dai0+IoVxiEnMFh$UKfF_IL z(yAeqH&tMZ$9S>>LBPxb6C$a&j4b7l7U0bhKRcV>(t%Ja?kpS1#o`(V>Jg5>)&%Fo z64Eg!fhj~0l)#xdj~>y?Jib(Hg(L2+1&;_>$O%@&fgSBBAq2+b-e@KU1PO|T>C!K8 zBNsMzmw>3M7Obe}>KPW3A8y#OnhZ&t+>qr_JW)Gcmx1|WHNz08A`hqd&MX_+3iAZr_O)@zvX}fEVOrAAp zJj2%$QDN-rZ0by`+qSmb3WLt(>0B(rmK~DPb5j?`ibvVD+d7mSF)t>C-xS?DnHDR_ zZ!sU9D;D6^*-)O}Wy{Jfe2UBjC5gIhYwAucP{eo6or#2Ec@D_uN+tznfTzlzmYpF} z2n$0B`8_&--@2x_x9)n0&ajS0ju;N_cr>HOh*a|Bc39y2CjF&~hvWogjC*3+D!akPxxBeT09nBAf@@fwMS335K1(Y!NPEmd~z zY6g~*ntGSN@@!3+X5?wODE0Ae`~KNk5zCo1ong&qzhF>ylnrWk)PtCgj5Az0Gdn_l z#>5KS={|~&HitdDS+)$NqaI$%e+mV@oYkaR&?CD1h{nMs6t`Gzjv6>7KI`MBW@a)B z;&0%QSOGY3Ug@qGhw!cNj%72d!JECze53?&M8bqUJI3&+S?O+~ZG~x027{&P9@9i^ zH(fAN2=C!xAt%Ox#l--fE(Z==82`0Q~3@vwio`B$H;V~A#%vTp?*C^K}jTj^&coDB3`mr!HnbqiBv}hrC>lp`o5J`KOoF zUFZnf!7Za{PHKN40dyX)ekCB2(jJuDL6kD`Rm;uw{4|{KN8?*g_P1+5V`+^nGy-=o3iBJMX z&GDa%Lm~u*fiw~`oiJv2b{JhUwTx%IFn4UQF7oHa35LLwW}Y$<*y(r5Maghnn(1fj zFJq4ax%d^B2HIm=Jun%{g+o~kAlqRVlQkJR zt`pscOf4r7`s~7&_+XD`QP9&PKfc|fRKldhS~K;s)?2&Fd6}|OW&_*Av5KNdEH&G= z?b&HuM?V$q^f^TZm|<&sJaN{4aRDlDp}+hC>tGHAHu#(f+=t&>3d{nxA$wIb=~fV! z6ro5;MYdSlPw<5e0N>LldpMhiw0+woCnWRdV|2`Wke|fDjfB; zkkc99x-5)Ng`IC;gD_qh7y&bs<4xHx7K2hUx&kksh#pRw;~t;(`Z6B8!emYeF&5dC zkHv7S2r`JtoPm)&?(5+U3U<>Feo0{f*jtP&w|Nb~XYVXuQdu$c1+Xr)c048KvjfFfpP zmB4khTRg0z0V@>p&`=s}G99J??TPnRp3AaF%1 ze;#46=HikT5a^j1W!jBPZvNvZ-w#-?gf3cg0nZoe3|Bk!Wk;NsKt?kOdAHdntSI5* zB+yBYL0eYnaqLC7bRMEa4RCo7J0#z5VI-@x-SD@NfriEDA&gXUGI;?{-KV)c@@1|O zjU(Awuwi!WFRO@_JfGAFaUw)25zn$D>0khCRk9WvVtrkP@c=t%UnE18BH)}Y@uCR>6e z1b@kSqNUWUWCl5t8j*Wxk>(mQ$v6v0>%%-I5(!RpjXPTu2Dp|cksM0y>Rx+M?TY8p!nzG^u%R4_(|}i zY$CiS;`#b1zwz+~pW*~{3!m7*xP+>{v?}%BKRz5vMTr+eS7s2%gbiV@U)YCD_*54U zS!4;CpmptbU|6UH&(ir$pd`yiG{cxF5+x!mFw%q<^sNJqW`}6Pfc6~vCC^dJ!8);& z7R^kW9{kQW z^yuf2XKLcSi`(s%A=4q7!Za>4C`|a!*7GUxu!eM5lD@(?NIaH;qu`75xRs>+P?bQ- zN?W(cKA06j!U`5K6m5vwj5vjl_^^w-OyGhV;>E>FNNY|KewbKdy-Z+`DI$0*pImBN zIxN!Dx{S7KTCy5o2kwv|4Kn_yh<-r+_BKqPNTf;+qa|pwp&$xX)AXaf3T~6k8p#D2 zQ9{^LYC%!zY`Gs#`9UV@n=1f1u|6zEF~3pX8cGXfY9cH;Ig02iZX5XV66e8KAz6{x zO)-G2i~^#pbwVfBOZx-(ji2qTN5xa%Kom~rSUUIwj8H6>A;98Vo=@>0m7-kflNej1 z4z@U?1<4rA`57#X(3dPhWIuIkzA`Z!XTz%tx}q8iOFFFVU~8tbXoL$5=f?6 zMuxVK7}H`HTO5HuqN zHo+2_zHurh6J$fU0h}|^*ZyhZ%D7IR{~NxjQ7SrZ^yTv3@5bRvvvz-Ug%OI3tW zgeCE2?ZYdWZLmcM)g0OBq9kL>$sa-)S{nrH4=5fbZ=rt_L&T1NyiYGOUjT9*Urpe? zaZ;Oj#8oJ5i%h|H`_QH+$)&7EF75(>I-yr&ivGw(F|3B*2E;+s?t>}&cfR$s!?gpr z9k|_DVK_i?>HF4J@Om+q=`gb}Njm_COr}+*Kst0Qh)jprJj6GY zfa;(OgD{Ky=>%@crLs&2GAj`h9J~}*Q8-6*9V$)6NoP8z{F{7MGcFmVlu*hZ?a;&B zFECw(R&ZtHDa1qkCX8npjf0SXz3n!9C7kqT4usC*sMd^NOgI%CoC(T;#9f=L{^5d_ zcrxq<*#LLHqnx)#TNo^kbBU5wG9hvm`=vn2A-tW0S-i!zNKA}=!Cc8VPy#Zk4UlG7 zlKFZ=eu{ol8m*N{GPT|$+rwg<&Z$ZssBT7u6EK)g8ms`RnM*08(;Xcci%B%4{$;qe z&Wh+Cy4%S{W3Xfy%j(F;mv#)NAFYrb;=DPPnd9JPGOT5)1;pQMrjrtDQ4(LkAJK&AeVmKY= zh9Oexm)T7W!hXa*Acet9H-&^yZr1%`EMibAw5Ud^q6*$hQA!NPjiCd{4pT|fF{h+W z3?&i#V1BFTyw6QskGMsb9k6L)3}Z_`9d zpIBfE4AXfa^_AXH4nnNMEGW4aFIp3Uq#zbC9g*A|V(B=NqoM%kaK7_$r6?vPW)9$` zvAAoiVjAM=3e3XKg+8ocn#TN1pxr5*@$4)hI@j@>jo`AJWf#;yN|=l21fop1$IP=g z&U&jUPI3pvbso<*pwb!sPjmbLL;y0t*#ZUV8Q(g8Mr%J}a9hb6oOi(m*7^mqP+(spvm+=~b zpw4d4SVLf>gA&?;h4?6Irv(i2z?LOcq(JD79Kk}M?WZpXBnL}@?~5B;i{9GR#ZdRz z(~h-sq-^5Q#+%MPb8N^@F)E%o(kEt4dr=?&J>(*Q+H^%K$_8^lqoV+=WW=kZ+o;uv zB?k(N*eXoE$9GnF3EEPJ*bo^FRp7-G%#NZfT$jK=C{qh4uBhgz)JEqPR_qoPKedM| zyGb~d$hWPE-XZVyT{A96KP4EHu?6BG_#jDuqA^-(Adr|-RiVbixxgU z5b=wpj8pU)~P6_zl>^r>HI9Nm3$$ z92k*91_kIM9{;Bk*kSMh0aGPkq*otQg%&@V8-SJ)KQirNpd%|pUPf1ADNXzUIT$UtNuiko z6sL^eDKMIu{0?WaZQ!c6@`gC-EWvz|`v=WSToFt8>LGw|c zP;Q2BX7N>Xka%+m@31#lZJhsBT^MSnEmB4)>u?!z2?iVnTvHdQhvzd<8=^jeg~F|< zE>V!Q2yie(pJW1GDC20ie^f87_qbd&7*D_ z{Nz|^41)m#CJ)~|;EQol2Rr_*O$H0uX#nz>uw+IvR}fSlh&sp&#^?6rBq}X8S8(zo zF#=RbPO+FHW+ey*)aswreUwjnv+2itG0au090VMsne2|1rP4s5v$P961|9@WVBv|S z`jyJ7kbQ>u!zcw?o={;>M<XscRUEdMf+s~{43B~W!P#BJ z&ZDV%Fud8JOY%Q2(Zz&z)X+8}3*d~qm4*G?I+wHD%|kL(SB=nq-3!JBxI{b=mjWG{ zebae|)(K4jaZL6o5=P;ZCM}N)a|Gn=D7$%Dz@n+Nn%PU|eQc&Om5hh?Lg65ZQKqOQ z1#d7nAo;^Ibgi=xXL$+#442Du6VC?|f=Hd_?yE4Pmo4S_7H+&Cjcm+i{Qz!vz~vDP zScV`swH#H*@`IfWU%Nai%hfg2$rh)E4~c0&~XFn8CbXM{MQFRe3qQ9Nr=1 zL_QV4rFhwg^M2tLKpoL&(z?ksoGbHO2%=t#zbyd9>v|hqmPaCnQ z1Z4+(fIpUGOOq}IO(H)qFab_BtbKUQVZH>h6}Kd7j8FAHs}kst0jqJevsyiKXHP+r zGmO)QFeJC$?38whZ7a|+MjaFyP?{chL*}bW%AwhFIEu*n2CaVKc?lNsjEX{2NVAmF z0~}G(G8Bg~^$1m-e5S1&1j@@(9h2Fc#t+aEEy#6@{|A)5b&H2?v<&jOZh$iB9-F+3 zuG+Abkh6+(M_9C*Kziy@bpsl45V&+N#8fb3nZ}2mB_=!UVWeqChf0ZE(7G-|1)ORA zUw|ZnAgz1c1WF6Y78-r}pl2@eIjC*m4s;tqE6q`B`OYafe(ieYOog2^dzbiJo)fBW+Rl2 z=%pF((q#A{arN0{P0BW~T*M^Z_IKp5oyl;0>zYrlRa)q%aoGo4k1~rj(l?Usu1DU1 e>`KkYx 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
-na6?O89 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 z zEDdJA!_h`4y+MMu!_COX@$h%dg7Ba+GnSdF+14P_STx)?8}#8<_r)@=(hMk5jH}w0 zUQLFd2XoMmzGvb^;jGxcUW?;T4~l;3x2e0xPK(`h*M%USK}aZ(1kTPeyrcfLQtk6t zUhL+2a!0&sI;leeOt|fEt@5u2uO|CmCOxq5<85&og zD8?d9sB5W{&ujog@ilcI7u}1Ez<=aU+kt%vz#)*@a;NEFZvq0>Mh#Wy`8K>fgze8) z4mrk$at_Qs0`M#&Bq;sJWtpnethdqo@q0ga)Y0`|B^Iqb$g#4U(ASKKoE zrv;V@71f*y67IN?7i*6XQt!H?Q**%bH^(zlsb`Up;uQ40O-a$AOFlXFYRE0%a9|7w zB|_k_sYbmyI`EtBp?)y8gECPp@0y56R3e&YRhqm6_yxbaM>m26_z3AF5F6e;=X>(GBc=QkmDH0pWgU0;D+b_GAC^k!u z8{x}6B;*|~wq9?an&QtK3;yknU%{S&@sO_?Dzs5$e9kci!&7stKk*@FcCJ(5&whC! zk=C7?n5rCQ89~^NjDl$jKJ`AmopOa;E1b#FUn?s|u)5n966ZERsCTZZ@=9`b*`7<- znlL(Yt*Nf1{`n;G&XvbWtI8pY&Pt>6P|DSI*(;yAG*_GM$cADWC1faU^J~AK`o6O3 zbH_ZaMTC8;{07%S!_Rl%;pfyOtDfNBZVPEUvGEld3bdv@6k%10vGUVR*~#bngQ3x9 z!&e{8!HdE~6?~rk);5NHB^c%d@B5Fa;Il93T3qqHG^8-;qBqzZA(hCV@d+7zZjdA9 zgNb%oAF3uL2RviBqt9$zmF80~RU_3vvgZdRmCAWH35X7)Wr%)3a)iD{3+=*M!(;HF zWAFm$q5!Ipz`KPkZvg2X6s{n? z0HUn$OPCzZE$Y3#A-eAEpFS90@Yd#F z@&^zsKW$+h)XM+5&uCLcedds3mFk1&(IAz(e#OOh8J&^&;`Mv2wjq{@m0Is1cgBdOWrlswJmeeRt?w!xXs(|^2Xj`#zoyjYxNbLyHJg8!^C|gq z4>b8AFZE7L+-9?qYax~f*PY$IBy|T-vo6$+n%=T{4v%v-?)!F}^QrfGGL**4wYSML z%Ir4ov>2`gUaO&1E#kFSo1J}ISsfM~d)3yaE}nACa3m(H<2zbHSpQi)mU6U^w4d9> zelVk?F5uYE>JYTjOOpek5eNpw|2VhaIU6~#I=a2q&YXX_(8MX&UfUxMxV|Q z0+ZcrkDlq*uc@zjmzl2ue$48UvyUXB(?&~OT}6>zBH6luwK-~Q)zh5rY%-WSC#A-n zWeN!#vT)~}0!;uVkOi_bs0JV^0;CO`L8KlvL8vZ)!$4K|)qrF{qwr`0*k%nZjfhOb zYwW~4+qp$iG3F_Ws0QS=LfVO(0HrXG8M&AmJiGz*1sQ|@9a{?fv}#BNpbJZvB6`Es zhHn%E?hH}`D}^0`>w*p-&2gaQIf9&-4_b&|Kt-f1!ablc+y;6IsS64KNaq5IqKOpw zW{tQGP;VFh%zZvcBk-L;k4KHCQ50-ms9~T%UISu4P?AGIFw-E90m-y1fbpE%3f2xM zJ^@_HgCh6kQn**(=HV?5tl$Vz2*wV%e;gki+(Sj-8n7tC6`^_40&t(vub4n%x_})9 zeLz0gf&Xla)dt&H6`|YZ-ZD6a;HS292^5Aw3*vvNp@N2-hF)@qpC?y{`Hpj&fiDKv zMIQ=W7-|m_SFj7!hZg7uNWp?(j3Ek+z{g>0Hy2S*YbodnD8SWzq%^Acz1hk9U zF|dnSF(93P2WD7wxKfK^3_NP^K`sK;fH)9*z&;06&e{KHhv%tq9~qY(U`by;SITDt z%>f2n7$RkeKHxrZ$wO_HG2}`%RP|fpPy@A5E(TJEERYDpd$R=?NFywyTxfwHLLm#X z@K6FIl4=|_KyZa=04>T9+2a_bSR-Hy+TmeiNC2Rde6ZLf!oSdZ4J=r&aA9oDix+4G zeFCRB50!&HXcHB44!^*^q=FS#K)z?0Nt97wIp>;4P?PKAA^HMac`n+>9tj zM&p)wrFjko&kagf3gOQz^Q@kfL0J?;dura{0Q?P*z-^eg#5R-b7g!k>=^JohH?&nFM#_JEXDxJ z4Z#8Ti3V{fgVzA5Q5S$(i1AO`r@G}sMI2EAdeCp7T)533A zb5K;9@&N-G4F>{3bF%g*f-u1V4qJ<9m5&?cF7v76bF4& z`VrVs!(&(+IVax`j4l!2(j z;*2!HE6ga!-~j;bMz4Wld>mW?Hmis~f}Wgz16>LGsLfvlKFUEHY8nKR0toZKKfVYG zpd5q57Yl+7xiLVzk?TNL1*R0^@;Q|9_Xq_F6;Z{4>>=8JjsyV9iAw95C7AOB8$#%= z;RWCewrG!VkSzuca?bLpgFqHu(BBq8$p82f$W$UHxNG$H5&rgX7h%5}1pA9>YTSn4 zJp}JlOCb5?NH+p6{6myHbb`#MUw<4GFhD-A$H;{l_RD!h8au;lE|KfQQ8~;jC~2b> zgTu$X)(=Q2yZ~%51eZWPM34d00TM;n2^dM;iVH*SSAB0SEGsb%L+_ zXDg@>`az0ec+Z9!#(52r+eF+J1Y|^@1)!?4#F+e`Xqy z;dCBwu^ZxH1~v3)@j#0cI0X5GaOWTv1^bZ0hDszhLXP~T2cQCkXtAcj7Mc=xniD(1 zDDj;Ak9T=c=7WX#I6shEY(e>!OB$#U16UE^Lf#gEoIubiM5G|sPebkaX*EpB=MV)N zP=aSPA0xwV2NY5wU08$m1P-9#{rLqJ1j~@PB9RF)RcasdKo&k6M6UB9s)m$6 zf+Mu}w;PUshcYI|HN&hU@R`Bwe_N4r1k0QuZrg%L^Dr}vBJ6U*vVp-2g*dRJB0gPGFs1?NMr; z^Yb-OL-T!5rGYxn6Qy8xf!q_ahQ%7}!o0zSnM)WDEn|-gJk41s1F4_I6iZQIpJnur z{0NE(a6AxG2sA|v>jy`6xenPpd;ocApayblkPSnK7HWi-W>IVbEZDLHz0y3uqNt)J zfy_f6tlnR2k;2%0U_szF81n$kbI%Pn68IxQkx|^A0?Esx93usF zkc%TKmC!+WnHNVmRDezcX+(-Gyq+DWA(F&NbMk&w;|Y)FZw>}o4gD!~pgcUw{0Kv+ z%@tV4W0+&f)__r6F2~LeD|o#HJ+9-A={A5HHC?=J6(D9AbJ1fuS9 zOg_X5U_s~>-krPRgbJ870vz_2AGl4_zdFtH3-)sxhpZmdE(bNV0OB_Qe&vuY%%Y$$ zU=h??Kz%Y)fT5%uY{eh}=K5m`N-*3*+&l(~NBI}P6_!Gd?sHg_=iVUjp=Gwva0)<> zVtfJJ=_$^czt}+vW5TyEu!#C{p+cGkPYvwwfC?!+B;+7K{%cJCiWDG66%i(`kb+qZ z-}q5bU%2`|n@1PMasdIw{4GeT|2B#U8oX-+S1?xwA_y*P@)SV?^=~0MAJ3vJfXOH{ zT*4c7@rDerhX5di1%x+{;)A3KL73Ml@#s5beK`95EtCF>0uw2MFg|z(CQp~aEXgQY^`P2`AoBK1R1~C7bsX^>FC^UP) zZ2jz`3x4C~yRiW1n@}GM(HCrka{`6-8!3eqbhe>iz#=VVkGM`MkTy=jMGZIw08s?Q zxo*)Clt@utgy2&922qGN(FNhiZ*if$B;`9e@pWLr9cah*?~+A7$1ZWqh<#P+P4z=-@y~fwPHZR7yXMB0xbsW zw-1r0`u`G5NS0^2OgQwXVi0t|wK+q<;HrO5Dp5#*+Xy%Emnb4i{*CVQ+$G8me;qaP z)WLSaEkeni*$RuD9+sfnn%0OzKFug+`NVOaxS5^rdauqPTR@iHY06d3t{ zmj9I;{uNCw#0K2k|66-j)W2Xi2i zdxzK(Tjt}yzd#Vx;Xjj6to6UO1%jmko2CVodk#KWtB`#0ax7E~2%s8r9x*I)2@=Xe ze0.0) { vec2 local=mod(vWorldPosition.xy-vTileData.xy,vTileData.z); @@ -707,7 +717,8 @@ void main() bool HeightTint,bool Headlamp,bool TileEdges, double TileEdgeWidthMeters,bool HasLightDirection, const std::array& LightDirection,bool ClipPlaneEnabled, - const std::array& ClipPlane) + const std::array& ClipPlane,bool FogEnabled, + double FogStartMeters,double FogEndMeters,const std::array& FogColor) { EGLDisplay Display=MakeDisplay(); EGLint Major=0,Minor=0; @@ -796,7 +807,10 @@ void main() glViewport(0,0,Width,Height);glDisable(GL_CULL_FACE);glEnable(GL_DEPTH_TEST); // A covered empty ray represents open air. Keep it visibly blue so it cannot // be mistaken for the old near-black result from a scene that lacked tiles. - glDepthFunc(GL_LEQUAL);glClearColor(0.30f,0.48f,0.62f,1.0f); + glDepthFunc(GL_LEQUAL); + glClearColor(FogEnabled?static_cast(FogColor[0]):0.30f, + FogEnabled?static_cast(FogColor[1]):0.48f, + FogEnabled?static_cast(FogColor[2]):0.62f,1.0f); glClearDepthf(1.0f);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glUseProgram(Program); glUniform1f(glGetUniformLocation(Program,"uTanHalfHorizontal"),static_cast(TanHalfHorizontal)); @@ -825,6 +839,18 @@ void main() static_cast(ActiveLightDirection[0]), static_cast(ActiveLightDirection[1]), static_cast(ActiveLightDirection[2])); + glUniform3f(glGetUniformLocation(Program,"uCameraWorld"), + static_cast(Scene.CameraMeters[0]), + static_cast(Scene.CameraMeters[1]), + static_cast(Scene.CameraMeters[2])); + glUniform3f(glGetUniformLocation(Program,"uFogColor"), + static_cast(FogColor[0]),static_cast(FogColor[1]), + static_cast(FogColor[2])); + glUniform1f(glGetUniformLocation(Program,"uFogStart"), + static_cast(FogStartMeters)); + glUniform1f(glGetUniformLocation(Program,"uFogEnd"), + static_cast(FogEndMeters)); + glUniform1i(glGetUniformLocation(Program,"uFogEnabled"),FogEnabled?1:0); glDrawElements(GL_TRIANGLES,static_cast(Scene.Indices.size()),GL_UNSIGNED_INT,nullptr); glFinish(); std::vector Pixels(static_cast(Width)*Height*4); @@ -863,7 +889,7 @@ int main(int argc,char** argv) if(argc==2&&std::string_view(argv[1])=="--print-source-stamp")return 0; if(argc<3) { - std::fprintf(stderr,"Usage: %s scene.vfmesh output.png [--size=WIDTHxHEIGHT] [--fov-deg=90] [--height-range-m=N] [--height-center-m=N] [--camera-m=X,Y,Z] [--yaw-deg=N] [--pitch-deg=N] [--shade=readable|normal] [--height-tint=on|off] [--headlamp=on|off] [--light-direction=X,Y,Z] [--tile-edges=on|off] [--tile-skirts=on|off] [--tile-edge-width-m=N] [--clip-plane=NX,NY,NZ,D] [--focus-region=X,Y,Z,R]\n",argv[0]); + std::fprintf(stderr,"Usage: %s scene.vfmesh output.png [--size=WIDTHxHEIGHT] [--fov-deg=90] [--height-range-m=N] [--height-center-m=N] [--camera-m=X,Y,Z] [--yaw-deg=N] [--pitch-deg=N] [--shade=readable|normal] [--height-tint=on|off] [--headlamp=on|off] [--light-direction=X,Y,Z] [--fog=on|off] [--fog-start-m=N] [--fog-end-m=N] [--fog-color=R,G,B] [--tile-edges=on|off] [--tile-skirts=on|off] [--tile-edge-width-m=N] [--clip-plane=NX,NY,NZ,D] [--focus-region=X,Y,Z,R]\n",argv[0]); return 2; } int Width=2560,Height=1440;double Fov=90.0,HeightRangeMeters=64.0; @@ -871,6 +897,8 @@ int main(int argc,char** argv) bool HasYaw=false,HasPitch=false;double Yaw=0.0,Pitch=0.0; bool HasCamera=false;std::array Camera{}; bool HasLightDirection=false;std::array LightDirection{}; + bool FogEnabled=false;double FogStartMeters=50.0,FogEndMeters=200.0; + std::array FogColor={0.62,0.70,0.75}; bool ReadableMode=true,HeightTint=true,TileEdges=false,TileSkirts=true,Headlamp=false; double TileEdgeWidthMeters=0.15; bool ClipPlaneEnabled=false;std::array ClipPlane{}; @@ -967,6 +995,38 @@ int main(int argc,char** argv) {std::fprintf(stderr,"Invalid light direction; use X,Y,Z with non-zero length\n");return 2;} HasLightDirection=true; } + else if(Arg.starts_with("--fog=")) + { + const std::string_view Mode=Arg.substr(6); + if(Mode=="on")FogEnabled=true; + else if(Mode=="off")FogEnabled=false; + else {std::fprintf(stderr,"Invalid fog mode\n");return 2;} + } + else if(Arg.starts_with("--fog-start-m=")) + { + char* End=nullptr;const std::string Value(Arg.substr(14)); + FogStartMeters=std::strtod(Value.c_str(),&End); + if(End==Value.c_str()||*End!='\0'||!std::isfinite(FogStartMeters) + ||FogStartMeters<0.0||FogStartMeters>20000.0) + {std::fprintf(stderr,"Invalid fog start distance\n");return 2;} + } + else if(Arg.starts_with("--fog-end-m=")) + { + char* End=nullptr;const std::string Value(Arg.substr(12)); + FogEndMeters=std::strtod(Value.c_str(),&End); + if(End==Value.c_str()||*End!='\0'||!std::isfinite(FogEndMeters) + ||FogEndMeters<=0.0||FogEndMeters>20000.0) + {std::fprintf(stderr,"Invalid fog end distance\n");return 2;} + } + else if(Arg.starts_with("--fog-color=")) + { + const std::string Value(Arg.substr(12)); + if(std::sscanf(Value.c_str(),"%lf,%lf,%lf",&FogColor[0],&FogColor[1], + &FogColor[2])!=3 + ||std::any_of(FogColor.begin(),FogColor.end(),[](double Channel) + {return !std::isfinite(Channel)||Channel<0.0||Channel>1.0;})) + {std::fprintf(stderr,"Invalid fog color; use R,G,B in 0..1\n");return 2;} + } else if(Arg.starts_with("--tile-edges=")) { const std::string_view Mode=Arg.substr(13); @@ -1002,6 +1062,8 @@ int main(int argc,char** argv) } else {std::fprintf(stderr,"Unknown option: %.*s\n",static_cast(Arg.size()),Arg.data());return 2;} } + if(FogEnabled&&FogEndMeters<=FogStartMeters) + {std::fprintf(stderr,"Fog end distance must be greater than fog start distance\n");return 2;} FScene Scene;std::string Error; if(!LoadScene(argv[1],Scene,Error)) {std::fprintf(stderr,"[OffscreenCapture] FAIL: %s\n",Error.c_str());return 2;} @@ -1077,14 +1139,16 @@ int main(int argc,char** argv) FocusRadius,static_cast(Scene.DrawnTriangles)); } } - std::printf("[OffscreenScene] voxels_per_meter=%.6f camera_m=(%.3f,%.3f,%.3f) yaw=%.2f pitch=%.2f height_m=%.3f..%.3f height_center_m=%.3f shade=%s height_tint=%s tile_edges=%s vertices=%zu triangles=%llu\n", + std::printf("[OffscreenScene] voxels_per_meter=%.6f camera_m=(%.3f,%.3f,%.3f) yaw=%.2f pitch=%.2f height_m=%.3f..%.3f height_center_m=%.3f shade=%s height_tint=%s tile_edges=%s fog=%s fog_distance_m=%.1f..%.1f fog_color=(%.2f,%.2f,%.2f) vertices=%zu triangles=%llu\n", 1000000.0/Scene.VoxelSizeMicrons,Scene.CameraMeters[0],Scene.CameraMeters[1],Scene.CameraMeters[2], Scene.YawDegrees,Scene.PitchDegrees,Scene.MinimumHeight,Scene.MaximumHeight, HeightCenterMeters, ReadableMode?"readable":"normal",HeightTint?"on":"off", - TileEdges?"on":"off",Scene.Vertices.size(), + TileEdges?"on":"off",FogEnabled?"on":"off",FogStartMeters,FogEndMeters, + FogColor[0],FogColor[1],FogColor[2],Scene.Vertices.size(), static_cast(Scene.Triangles)); return Render(Scene,argv[2],Width,Height,Fov,HeightRangeMeters,HeightCenterMeters,ReadableMode, HeightTint,Headlamp,TileEdges, - TileEdgeWidthMeters,HasLightDirection,LightDirection,ClipPlaneEnabled,ClipPlane)?0:3; + TileEdgeWidthMeters,HasLightDirection,LightDirection,ClipPlaneEnabled,ClipPlane, + FogEnabled,FogStartMeters,FogEndMeters,FogColor)?0:3; } diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp index a0cb0a7..bc48c9d 100644 --- a/Standalone/Source/StreamBenchmarkHeadless.cpp +++ b/Standalone/Source/StreamBenchmarkHeadless.cpp @@ -108,6 +108,7 @@ namespace bool ResolvedLayoutOnly=false; bool AuditResolvedLayoutBoundaries=false; bool AuditResolvedLayoutPassages=false; + bool AuditLiminalRoadWalk=false; bool AuditSurfaceClearance=false; bool AuditStrateTransitions=false; int TransitionModeOverride=-1; @@ -1447,6 +1448,7 @@ namespace " --audit-strate-transitions (measure interface crossing, band thickness and holes)\n" " --transition-mode=hard|gradient|interleaved --transition-width-chunks=N (fixture override)\n" " --audit-resolved-layout-passages (capsule, support, route-binding and blocked-control audit)\n" + " --audit-liminal-road-walk (capsule, support, width and grade audit for the liminal road)\n" " --audit-heightfield-profile (brute-force the profiled field intervals and route)\n" " --audit-motif-intervals (brute-force motif bounds, BVH and shape-free guards)\n" " --audit-landmark-reachability (capsule, support, grade and buried-L43 control)\n" @@ -1494,6 +1496,7 @@ namespace else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true; else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true; else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true; + else if(arg=="--audit-liminal-road-walk")options.AuditLiminalRoadWalk=true; else if(arg=="--native-owner-fi")options.NativeOwnerFloatingIslandsRecipe=true; else if(arg=="--density-dispatch-report") {options.DensityDispatchReport=true;options.StreamHash=true;options.StreamHashTiles=true;} @@ -1875,7 +1878,13 @@ int main(int argc,char** argv) &&!StandaloneWorldAssemblyPrototype::ValidateExportedCorePlanMatchesOwner( *ownerWorld,worldAssembly,resolvedLayout,error)) {std::fprintf(stderr,"Unreal-resolved Core plan/owner slot validation failed: %s\n",error.c_str());return 2;} - if(!std::holds_alternative(ownerJsonRoot.Get("motif_family_override").Data)) + const bool hasMotifFamily=!std::holds_alternative( + ownerJsonRoot.Get("motif_family_override").Data); + const bool hasLiminalRoad=!std::holds_alternative( + ownerJsonRoot.Get("liminal_road_override").Data); + if(hasMotifFamily&&hasLiminalRoad) + {std::fprintf(stderr,"motif_family_override and liminal_road_override cannot be combined\n");return 2;} + if(hasMotifFamily) { std::vector requests; if(!StandaloneWorldAssemblyPrototype::BuildMotifFamilyRequests(ownerJsonRoot, @@ -1903,6 +1912,25 @@ int main(int argc,char** argv) static_cast(motif.irregularitySeed)); } } + if(hasLiminalRoad) + { + std::vector requests(1); + if(!StandaloneWorldAssemblyPrototype::BuildLiminalRoadRequest(ownerJsonRoot, + *ownerWorld,worldAssembly,resolvedLayout,requests.front(),error) + ||!VoxelForgeCore::ResolveResolvedLayoutStampsV1( + resolvedLayout,seed,requests,error)) + {std::fprintf(stderr,"Liminal road resolution failed: %s\n",error.c_str());return 2;} + const auto& road=resolvedLayout.stamps.front(); + std::printf("[LiminalRoadResolved] vocabulary=%u id=%s strate=%d shape=rounded_dais " + "center_m=(%.3f,%.3f,%.3f) top_m=%.3f span_m=%.3f width_m=%.3f " + "grade_deg=0.000 placement=free route_keepout=PASS band_clearance=PASS status=PASS\n", + resolvedLayout.stampVocabularyVersion,road.stableId.c_str(),road.strateIndex, + road.positionMicrometres[0]/1.0e6,road.positionMicrometres[1]/1.0e6, + road.positionMicrometres[2]/1.0e6, + (road.positionMicrometres[2]+road.halfExtentsMicrometres[2])/1.0e6, + 2.0*road.halfExtentsMicrometres[0]/1.0e6, + 2.0*road.halfExtentsMicrometres[1]/1.0e6); + } if(options.LandmarkChamberDemo) { std::vector requests; @@ -2404,5 +2432,5 @@ int main(int argc,char** argv) options.MovingCameraSpeedMetersPerSecond,options.MovingCameraDurationSeconds, options.MovingCameraDwellSeconds,options.ColdWorkerTimeline, options.AuditPassageSourceClamp,options.AuditPassageCarveReach, - options.AuditPassageTileSeams); + options.AuditPassageTileSeams,options.AuditLiminalRoadWalk); } diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h index 06626c9..e151fbd 100644 --- a/Standalone/Source/StreamBenchmarkPipeline.h +++ b/Standalone/Source/StreamBenchmarkPipeline.h @@ -2384,7 +2384,8 @@ namespace StandaloneStreamBenchmark bool bMovingCameraTrace=false,double MovingCameraSpeedMetersPerSecond=0.0, double MovingCameraDurationSeconds=12.0,double MovingCameraDwellSeconds=5.0, bool bColdWorkerTimeline=false,bool bAuditPassageSourceClamp=false, - bool bAuditPassageCarveReach=false,bool bAuditPassageTileSeams=false) + bool bAuditPassageCarveReach=false,bool bAuditPassageTileSeams=false, + bool bAuditLiminalRoadWalk=false) { using Clock=std::chrono::steady_clock; const std::clock_t ColdCpuOrigin=std::clock(); @@ -2554,7 +2555,7 @@ namespace StandaloneStreamBenchmark ||bAuditOreVeinYield||bAuditLandmarkReachability||bAuditStrateTransitions ||bAuditResolvedLayoutPassages||bAuditResolvedLayoutBoundaries ||bAuditTileShapeReadiness||bAuditBasePageEquivalence||bAuditRouteParity - ||bAuditPassageSourceClamp||bAuditPassageCarveReach; + ||bAuditPassageSourceClamp||bAuditPassageCarveReach||bAuditLiminalRoadWalk; const bool bDeferFarWorld=bIncludeFarWorld&&!bMovingCameraTrace&&!bFarWorldAudit; VoxelForgeCore::ShapeBuildRequest ShapeRequest; ShapeRequest.minVoxel={FineMin.X,FineMin.Y,FineMin.Z}; @@ -2820,10 +2821,10 @@ namespace StandaloneStreamBenchmark Streamer.Stop(); return passed?0:11; } - if(bAuditResolvedLayoutPassages) + if(bAuditResolvedLayoutPassages||bAuditLiminalRoadWalk) { if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout()) - {Streamer.Stop();std::fprintf(stderr,"[ResolvedPassageAudit] missing resolved-layout snapshot\n");return 2;} + {Streamer.Stop();std::fprintf(stderr,"[ResolvedWalkAudit] missing resolved-layout snapshot\n");return 2;} struct FWalkAudit { std::uint64_t segments=0,centers=0,capsuleSamples=0,blocked=0; @@ -2961,6 +2962,67 @@ namespace StandaloneStreamBenchmark &&audit.gradeFailures==0; return audit; }; + const auto auditLiminalRoadWalk=[&](bool required) + { + bool found=false; + bool passed=true; + for(const auto& road:ResolvedLayout->stamps) + { + if(road.stableId!="liminal/raised-road-v1")continue; + found=true; + const bool longAxisX=(road.yawQuarterTurns&1u)==0; + const double centerX=road.positionMicrometres[0]/1.0e6; + const double centerY=road.positionMicrometres[1]/1.0e6; + const double centerZ=road.positionMicrometres[2]/1.0e6; + const double halfLength=road.halfExtentsMicrometres[longAxisX?0:1]/1.0e6; + const double halfWidth=road.halfExtentsMicrometres[longAxisX?1:0]/1.0e6; + const double deckZ=centerZ+road.halfExtentsMicrometres[2]/1.0e6; + constexpr double CapsuleRadiusMetres=0.34; + constexpr double CapsuleHeightMetres=1.76; + constexpr double HalfCapsuleHeightMetres=CapsuleHeightMetres*0.5; + constexpr double EndInsetMetres=1.0; + std::vector> roadPath; + const double inset=std::min(EndInsetMetres,halfLength*0.25); + for(const double along:{-halfLength+inset,halfLength-inset}) + roadPath.push_back({(centerX+(longAxisX?along:0.0))/coreMetres, + (centerY+(longAxisX?0.0:along))/coreMetres, + (deckZ+HalfCapsuleHeightMetres)/coreMetres}); + const FWalkAudit roadAudit=auditPlayerPath(Shape,roadPath, + 0.125,0.125,1.125,3,0.45); + const double widthMetres=2.0*halfWidth; + const double horizontal=std::hypot(roadPath[1][0]-roadPath[0][0], + roadPath[1][1]-roadPath[0][1])*coreMetres; + const double vertical=std::abs(roadPath[1][2]-roadPath[0][2])*coreMetres; + const double gradeDegrees=std::atan2(vertical,horizontal)* + 57.29577951308232; + passed=road.kind==VoxelForgeCore::ResolvedSdfStampKindV1::fillDais + &&widthMetres>=2.0*CapsuleRadiusMetres + &&gradeDegrees<=15.0&&roadAudit.passed; + std::printf("[LiminalRoadWalkAudit] id=%s stations=%llu capsule_samples=%llu " + "blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu " + "width_m=%.3f capsule_diameter_m=%.3f grade_deg=%.3f grade_limit_deg=15.000 " + "status=%s\n",road.stableId.c_str(), + static_cast(roadAudit.centers), + static_cast(roadAudit.capsuleSamples), + static_cast(roadAudit.blocked), + static_cast(roadAudit.supportSamples), + static_cast(roadAudit.unsupported), + static_cast(roadAudit.gradeFailures),widthMetres, + 2.0*CapsuleRadiusMetres,gradeDegrees,passed?"PASS":"FAIL"); + } + if(!found&&required) + { + std::printf("[LiminalRoadWalkAudit] status=FAIL reason=road_stamp_missing\n"); + return false; + } + return passed; + }; + if(bAuditLiminalRoadWalk&&!bAuditResolvedLayoutPassages) + { + const bool passed=auditLiminalRoadWalk(true); + Streamer.Stop(); + return passed?0:14; + } const auto qToMetres=[&](std::int64_t q) {return static_cast(q)*World.CoreVoxelSizeMicrons /(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel*1000000.0);}; @@ -4919,6 +4981,7 @@ namespace StandaloneStreamBenchmark routeAudit.firstUnsupported[i][1],routeAudit.firstUnsupported[i][2], routeAudit.firstUnsupported[i][3]); } + const bool liminalRoadAuditPassed=auditLiminalRoadWalk(false); for(std::size_t passageIndex=0; passageIndexpassages.size();++passageIndex) { @@ -4984,6 +5047,7 @@ namespace StandaloneStreamBenchmark &&shellAuditPassed &&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings &&routeBindingFailures==0 + &&liminalRoadAuditPassed &&blockedCompiled&&blockedControls==passageCount; std::printf("[ResolvedPassageAuditSummary] interfaces=%llu passed=%llu natural=%llu/%llu strate_routes=%llu route_walkable=%llu route_points=%llu route_bindings=%llu route_binding_failures=%llu blocked_controls=%llu/%llu status=%s\n", static_cast(passageCount),static_cast(passagePasses), diff --git a/Standalone/Source/WorldAssemblyPrototype.h b/Standalone/Source/WorldAssemblyPrototype.h index dbe2a7c..bafe419 100644 --- a/Standalone/Source/WorldAssemblyPrototype.h +++ b/Standalone/Source/WorldAssemblyPrototype.h @@ -587,6 +587,69 @@ namespace StandaloneWorldAssemblyPrototype return true; } + inline bool BuildLiminalRoadRequest(const VFJson::Value& root, + const FStandaloneOwnerWorld& owner, + const VoxelForgeCore::WorldAssemblyManifest& assembly, + const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout, + VoxelForgeCore::ResolvedLayoutStampRequestV1& request,std::string& error) + { + using namespace VoxelForgeCore; + const VFJson::Value& overrideValue=root.Get("liminal_road_override"); + if(!overrideValue.IsObject()) + {error="liminal_road_override must be an object";return false;} + if(assembly.recipe.surfaceProfile.version!=1 + ||assembly.recipe.surfaceProfile.kind!=WorldAssemblySurfaceProfileKindV1::flatField + ||owner.CoreVoxelSizeMicrons==0) + {error="the liminal road requires a valid flatField SurfaceWorld recipe";return false;} + + const VFJson::Value& strateValue=overrideValue.Get("strate_index"); + const auto* id=std::get_if(&overrideValue.Get("stable_id").Data); + const auto& position=overrideValue.Get("position_xy_m").AsArray(); + const auto& extents=overrideValue.Get("half_extents_m").AsArray(); + const VFJson::Value& heightValue=overrideValue.Get("deck_height_above_field_m"); + const VFJson::Value& yawValue=overrideValue.Get("yaw_quarter_turns"); + if(!strateValue.IsNumber()||!id||id->empty()||position.size()!=2||extents.size()!=3 + ||!heightValue.IsNumber()||!std::isfinite(heightValue.Number()) + ||!yawValue.IsNumber()||yawValue.Integer()<0||yawValue.Integer()>3) + {error="liminal road requires stable_id, strate_index, position, extents, deck height and yaw";return false;} + for(const auto& value:position) + if(!value.IsNumber()||!std::isfinite(value.Number())) + {error="liminal road position_xy_m must contain finite metre values";return false;} + for(const auto& value:extents) + if(!value.IsNumber()||!std::isfinite(value.Number()) + ||value.Number()<0.5||value.Number()>100.0) + {error="liminal road half_extents_m must contain values from 0.5 to 100 metres";return false;} + if(heightValue.Number()<1.0||heightValue.Number()>100.0) + {error="liminal road deck height must be from 1 to 100 metres above the field";return false;} + const auto strateIndex=static_cast(strateValue.Integer()); + const auto strate=std::find_if(layout.strates.begin(),layout.strates.end(), + [strateIndex](const auto& value){return value.strateIndex==strateIndex + &&value.generatorKind=="SurfaceWorld";}); + if(strate==layout.strates.end()) + {error="liminal_road_override must select a resolved SurfaceWorld strate";return false;} + if(extents[0].Number()<10.0||extents[1].Number()<0.75 + ||extents[0].Number()<=extents[1].Number()*4.0) + {error="liminal road must be long and narrow, with at least 1.5 metres half-width";return false;} + + const auto metresToMicrometres=[](double value) + {return static_cast(std::llround(value*1000000.0));}; + const std::int64_t fieldFloorMicrometres=static_cast(std::llround( + static_cast(assembly.recipe.surfaceProfile.h0Subvoxels) + *owner.CoreVoxelSizeMicrons/WorldAssemblySubvoxelsPerVoxel)); + request={}; + request.stableId=*id; + request.strateIndex=strateIndex; + request.kind=ResolvedSdfStampKindV1::fillDais; + request.anchorPositionMicrometres={metresToMicrometres(position[0].Number()), + metresToMicrometres(position[1].Number()), + fieldFloorMicrometres+metresToMicrometres(heightValue.Number()) + -metresToMicrometres(extents[2].Number())}; + request.halfExtentsMicrometres={metresToMicrometres(extents[0].Number()), + metresToMicrometres(extents[1].Number()),metresToMicrometres(extents[2].Number())}; + request.yawQuarterTurns=static_cast(yawValue.Integer()); + return true; + } + inline bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root, std::string& error,const std::string& manifestOutputPath, VoxelForgeCore::WorldAssemblyManifest* outManifest=nullptr)