From 9ac60d5d181b8ba6ad41b566b961a36a440f0211 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Sun, 1 Nov 2026 16:17:42 +0100 Subject: [PATCH] research: cave planner merge evidence (L150, measurement only) Merges cleanly. UE start: S1 -9%, S3 +11%, S0/S2 ~+1%. 30 m/s: near coverage 44.2 -> 38.5%, far holes +0.06 pp, p99 +2%. L80 near-ready slower in 6/8 cells (up to +7.9%), whole view faster in 7/8. Sweeps 15/16 each (seed 2). Source/: VoxelForgeCoreAdapter.cpp. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1 --- .../WorldAssemblerM3/Linux150/REPORT.md | 69 +++++ .../Linux150/RunL150Captures.py | 253 ++++++++++++++++++ .../Linux150/RunL150FinalTiming.py | 156 +++++++++++ .../Linux150/RunL150SeedSweep.py | 23 ++ .../Linux150/RunL150Unreal.py | 170 ++++++++++++ .../Linux150/captures/INDEX.md | 14 + ...sembler-world-tunnel-network-departure.jpg | Bin 0 -> 18339 bytes .../assembler-world-tunnel-network-route.jpg | Bin 0 -> 11005 bytes ...ler-world-vertical-shafts-looking-down.jpg | Bin 0 -> 11005 bytes ...mbler-world-vertical-shafts-looking-up.jpg | Bin 0 -> 15079 bytes .../Linux150/captures/manifest.json | 186 +++++++++++++ .../planner-head-tunnel-network-departure.jpg | Bin 0 -> 17938 bytes .../planner-head-tunnel-network-route.jpg | Bin 0 -> 17082 bytes ...nner-head-vertical-shafts-looking-down.jpg | Bin 0 -> 26118 bytes ...lanner-head-vertical-shafts-looking-up.jpg | Bin 0 -> 22887 bytes 15 files changed, 871 insertions(+) create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/RunL150Captures.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/RunL150FinalTiming.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/RunL150SeedSweep.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/RunL150Unreal.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/INDEX.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/manifest.json create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-route.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-down.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux150/REPORT.md new file mode 100644 index 0000000..6f6ab24 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/REPORT.md @@ -0,0 +1,69 @@ +# Linux150 — cave planner merge evidence + +**Result:** `assembler/world` merges cleanly into `wip/l134-cave-planner`. The planner HEAD passes 15/16 seeds for each cave layer; seed 2 retains its known shell/seam failure. Linux Unreal start and movement runs completed. L80 shows slower near-ready medians in six of eight cells, while whole-view medians are lower in seven of eight cells. + +No generation code changed in L150. Branch HEAD was `b8382eed`; target `assembler/world` was `eb47d722`. The UE baseline project uses `c743f2a`, code-equivalent to the target. The planner source stamp is `c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f`; the `assembler/world` baseline binary stamp is `4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621`. + +## Evidence table + +L80 used one warm-up per binary and five interleaved measured runs per cell at W8/W12 across all four owner layers. The quiet-host gate passed at load 0.50 and 99.3% CPU idle. The L137 A/A floor is ±1%. Time deltas are planner HEAD versus `assembler/world`; positive means slower. + +UE start rows are one L121 start per stratum and build, measuring start through the full LOD0–2 set. Movement rows are medians over five interleaved L127 runs at 30 m/s (12 s moving plus 5 s dwell, eight effective tile slots). Coverage and hole figures describe the moving window. For L80, S0–S3 are Surface World, Floating Islands, Tunnel Network, and Vertical Shafts. + +| Measure | assembler/world | planner HEAD | Delta / result | +|---|---:|---:|---:| +| UE start → LOD0–2, S0 (512/448/448 tiles) | 2.453159 s | 2.481646 s | +1.16% | +| UE start → LOD0–2, S1 (512/448/256 tiles) | 2.424691 s | 2.206180 s | −9.02% | +| UE start → LOD0–2, S2 (513/448/384 tiles) | 2.454927 s | 2.477987 s | +0.94% | +| UE start → LOD0–2, S3 (513/448/384 tiles) | 2.216523 s | 2.459258 s | +10.95% | +| 30 m/s near-ready moving mean | 44.2432% | 38.5307% | −5.71 pp (−12.91% relative) | +| 30 m/s far-hole moving mean | 3.2596% | 3.3208% | +0.0611 pp (+1.88% relative) | +| 30 m/s game-thread tick p99 | 10.7288 ms | 10.9596 ms | +2.15% | +| 30 m/s median per-run worst tick | 13.5996 ms | 14.4242 ms | +6.06% | +| 30 m/s worst tick observed across five runs | 14.9833 ms | 14.6717 ms | −2.08% | +| L80 S0 near-ready median, W8 / W12 | 0.737599 / 0.768684 s | 0.775814 / 0.829089 s | +5.18% / +7.86% | +| L80 S0 whole-view median, W8 / W12 | 1.737 / 1.705 s | 1.711 / 1.705 s | −1.50% / 0.00% | +| L80 S1 near-ready median, W8 / W12 | 1.006966 / 0.974332 s | 1.021554 / 1.029940 s | +1.45% / +5.71% | +| L80 S1 whole-view median, W8 / W12 | 1.907 / 1.812 s | 1.803 / 1.803 s | −5.45% / −0.50% | +| L80 S2 near-ready median, W8 / W12 | 1.044179 / 0.959096 s | 1.039310 / 1.026140 s | −0.47% / +6.99% | +| L80 S2 whole-view median, W8 / W12 | 1.913 / 1.751 s | 1.821 / 1.733 s | −4.81% / −1.03% | +| L80 S3 near-ready median, W8 / W12 | 0.875773 / 0.838977 s | 0.852260 / 0.853215 s | −2.68% / +1.70% | +| L80 S3 whole-view median, W8 / W12 | 1.788 / 1.687 s | 1.660 / 1.605 s | −7.16% / −4.86% | +| Tunnel Network composed sweep | — | 15/16 | Seed 2: passages, seams, shell | +| Vertical Shafts composed sweep | — | 15/16 | Seed 2: passages, seams, shell | +| Seed-0 capture quality | 1/4 pass | 4/4 pass | All exports/renders completed; three baseline viewpoints have no visible mesh surface | + +Both moving runs reached 100% near-ready coverage and 0% far holes by dwell end (3.40 s baseline, 3.78 s HEAD). The five-run maximum tick was lower on HEAD, while its median of per-run maxima was higher. + +For both sweeps, every gate other than the three seed-2 checks passed, including dispatch, route, naturalness, mouth, ripple, interior air, and negative controls. The seed-2 failure is the known interface 2–3 shell/seam failure. + +## Owner-world captures + +Four eye-height viewpoints use the same camera coordinates for both versions. Poses come from the planner layout resolved against the owner fixture’s embedded Core plan at seed 0. The capture layout identity is `cc87ee727c483039` on planner HEAD and `0589300eb07dfe06` on the baseline. The seed sweeps use the established `--seed=0` regeneration path (layout identity `d310515572977ebf`); the captures intentionally use the owner fixture’s embedded Core plan. For the baseline, a seed override makes the older route setup fail, so the capture runner imports the saved plan without an override. The planner images pass the capture-quality gate. Three baseline images fail its image-variance threshold because the baseline mesh has no visible surface from those exact poses; export and render processes succeeded. + +| View | assembler/world | planner HEAD | +|---|---|---| +| Tunnel Network — route joint | ![assembler/world route joint](captures/assembler-world-tunnel-network-route.jpg) | ![planner HEAD route joint](captures/planner-head-tunnel-network-route.jpg) | +| Tunnel Network — departure joint | ![assembler/world departure joint](captures/assembler-world-tunnel-network-departure.jpg) | ![planner HEAD departure joint](captures/planner-head-tunnel-network-departure.jpg) | +| Vertical Shafts — looking down | ![assembler/world shaft looking down](captures/assembler-world-vertical-shafts-looking-down.jpg) | ![planner HEAD shaft looking down](captures/planner-head-vertical-shafts-looking-down.jpg) | +| Vertical Shafts — looking up | ![assembler/world shaft looking up](captures/assembler-world-vertical-shafts-looking-up.jpg) | ![planner HEAD shaft looking up](captures/planner-head-vertical-shafts-looking-up.jpg) | + +Capture source stamps, coordinates, and quality output are in [`captures/manifest.json`](captures/manifest.json). The runner is [`RunL150Captures.py`](RunL150Captures.py). + +## Merge risks + +- Seed 2 fails the passage, seam, and shell checks in both cave-layer sweeps. Other current sweep gates pass. +- Near-ready L80 time is above the ±1% floor in six of eight cells. S0 W8/W12 and S1 W12 have the largest regressions; whole-view time is lower in seven cells and unchanged in one. +- During 30 m/s movement, HEAD has 12.91% lower near-ready coverage, 1.88% more far holes, 2.15% higher tick p99, and a 6.06% higher median per-run worst tick. Both builds fill near coverage and far holes during dwell. +- Three baseline captures cannot show an exposed mesh at the corresponding underground camera positions. The planner views render and pass image quality. +- Seed sweeps and owner-world captures both use seed 0, but use the regenerated standalone plan and embedded owner Core plan respectively; the images describe the owner-export plan path. +- The merge adds one Unreal `Source/` change for the owner’s Windows compile: `Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp` (5 insertions, 3 deletions; 8 changed lines). + +## Checks and artifacts + +- Merge check: clean in `Saved/Linux/l150/merge-worktree`; no unmerged paths and no merge commit. Details and hashes: [`merge-check.md`](../../../../Saved/Linux/l150/merge-check.md). +- Linux `VFHostEditor` Development build passed with `-NoUBA -MaxParallelActions=4` (27 actions, 68.79 s). It emitted three existing deprecated RenderMeshComponent API warnings in `VoxelWorld.cpp`; [build log](../../../../Saved/Linux/l150/ubt-console.log). +- L80: 80 measured runs plus warm-ups; [summary](../../../../Saved/Linux/l150/timing/summary.json) and per-run logs are under `Saved/Linux/l150/timing/`. +- Linux Unreal: four L121 starts per build and five interleaved L127 movement runs per build; [summary](../../../../Saved/Linux/l150/ue/summary.json) and run logs are under `Saved/Linux/l150/ue/`. +- Tunnel Network and Vertical Shafts sweeps: 16 seeds each; [Tunnel Network summary](../../../../Saved/Linux/l150/seeds/tunnel-network/summary.json), [Vertical Shafts summary](../../../../Saved/Linux/l150/seeds/vertical-shafts/summary.json), and per-seed logs under `Saved/Linux/l150/seeds/`. +- The fast tier was not rerun in L150. L147 passed it on the same standalone source stamp (`c6b8a100…f0609f`); no generation source changed here. A Windows UE build was not run. diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/RunL150Captures.py b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150Captures.py new file mode 100644 index 0000000..5f0356d --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150Captures.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""Capture four matched eye-height owner-world views for assembler/world and planner HEAD.""" +from __future__ import annotations + +import json +import math +import os +from pathlib import Path +import re +import subprocess +import sys + + +ROOT = Path(__file__).resolve().parents[4] +DATA = ROOT / "Standalone/data/owner_world.json" +OUT = ROOT / "Saved/Linux/l150/captures" +LAYOUT = OUT / "planner-head-resolved-layout.json" +ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux150/captures" +LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"] +VERSIONS = { + "assembler-world": { + "headless": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a", + "renderer": ROOT / "Saved/Linux/l137/bin/VoxelForgeOffscreenCapture-l137-aa-a", + "stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621", + }, + "planner-head": { + "headless": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l150-head-clang", + "renderer": ROOT / "Standalone/bin/linux/VoxelForgeOffscreenCapture-l150-head-clang", + "stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f", + }, +} +STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M) + + +def fmt(values) -> str: + return ",".join(f"{value:.6f}" for value in values) + + +def 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_um(values) -> tuple[float, float, float]: + return tuple(value / 1_000_000.0 for value in values) + + +def point_q(layout: dict, values) -> tuple[float, float, float]: + scale = layout["core_voxel_size_um"] / (256.0 * 1_000_000.0) + return tuple(value * scale for value in values) + + +def camera_for_mouth(layout: dict, edge: dict): + anchor = edge["start_anchor_stable_id"] + if anchor.endswith("/lower-mouth"): + side = "lower" + mouth_q = next(row["lower_mouth_q"] for row in layout["passages"] + if row["stable_id"] == anchor.removesuffix("/lower-mouth")) + elif anchor.endswith("/upper-mouth"): + side = "upper" + mouth_q = next(row["upper_mouth_q"] for row in layout["passages"] + if row["stable_id"] == anchor.removesuffix("/upper-mouth")) + else: + raise ValueError(f"mouth edge has no passage anchor: {anchor}") + passage_id = anchor.rsplit("/", 1)[0] + passage = next(row for row in layout["passages"] if row["stable_id"] == passage_id) + controls = passage["control_points"] + mouth_index = len(controls) - 1 if side == "lower" else 0 + neighbor_index = mouth_index - 1 if side == "lower" else 1 + a = controls[neighbor_index]["position_q"] + b = controls[mouth_index]["position_q"] + sign = 1.0 if side == "lower" else -1.0 + tx, ty = sign * (b[0] - a[0]), sign * (b[1] - a[1]) + length = math.hypot(tx, ty) + if length <= 1.0e-9: + raise ValueError(f"degenerate passage mouth tangent: {anchor}") + tx, ty = tx / length, ty / length + mouth = point_q(layout, mouth_q) + eye_z = mouth[2] + 1.45 + camera = (mouth[0] - tx * 2.0, mouth[1] - ty * 2.0, eye_z) + target = (mouth[0] + tx * 10.0, mouth[1] + ty * 10.0, eye_z) + return camera, target, (mouth[0], mouth[1], eye_z, 16.0) + + +def views(layout: dict) -> list[dict]: + edges = {row["stable_id"]: row for row in layout["cavern_edges"]} + tunnel_views = [] + for stable_id, name in ( + ("tunnel-network/strate-2/route-edge-0", "tunnel-network-route"), + ("tunnel-network/strate-2/departure-edge", "tunnel-network-departure")): + camera, target, focus = camera_for_mouth(layout, edges[stable_id]) + tunnel_views.append({"name": name, "layer": "Tunnel Network", "camera": camera, + "target": target, "focus": focus}) + + rooms = {row["stable_id"]: row for row in layout["cavern_rooms"]} + shaft = rooms["vertical-shafts/strate-3/shaft-0/primitive-0"] + access = edges["vertical-shafts/strate-3/shaft-access-0"]["polyline_um"] + center = point_um(shaft["center_um"]) + half = point_um(shaft["half_extents_um"]) + shaft_top = center[2] + half[2] + access_a, access_b = point_um(access[0]), point_um(access[-1]) + ledge_z = shaft_top - 12 * (shaft["ledge_spacing_um"] / 1.0e6) + shaft_views = [ + {"name": "vertical-shafts-looking-down", "layer": "Vertical Shafts", + "camera": (access_b[0], access_b[1], access_b[2] + 1.45), + "target": (center[0], center[1], shaft_top - 30.0), + "focus": (center[0], center[1], shaft_top - 25.0, 14.0), "fov": 35.0}, + {"name": "vertical-shafts-looking-up", "layer": "Vertical Shafts", + "camera": (center[0], center[1], ledge_z + 1.45), + "target": (center[0], center[1], shaft_top - 2.0), + "focus": (center[0], center[1], shaft_top - 22.0, 20.0)}, + ] + return tunnel_views + shaft_views + + +def stamped_run(name: str, command: list[object], log: Path, expected_stamp: str, + env: dict[str, str]) -> str: + binary = Path(str(command[0])) + probe = subprocess.run([*LIMIT, "timeout", "15s", str(binary), "--print-source-stamp"], + cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, check=False, timeout=20) + match = STAMP_RE.search(probe.stdout or "") + if probe.returncode or not match or match.group(1) != expected_stamp: + raise RuntimeError(f"{name}: source stamp mismatch for {binary}") + log.parent.mkdir(parents=True, exist_ok=True) + with log.open("w", encoding="utf-8") as stream: + result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)], + cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT, + timeout=1745, check=False) + output = log.read_text(encoding="utf-8", errors="replace") + emitted = STAMP_RE.search(output) + if result.returncode or not emitted or emitted.group(1) != expected_stamp: + raise RuntimeError(f"{name}: export or render failed; see {log}") + return output + + +def resolve_planner_layout(env: dict[str, str]) -> dict: + config = VERSIONS["planner-head"] + command = [config["headless"], DATA, "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--workers=12", + "--clip-radius=6", "--max-clip-level=0", "--max-level=0", + "--force-generate", "--recipe=surface-world", "--resolved-layout-only", + f"--resolved-layout-output={LAYOUT}"] + stamped_run("planner-head-resolve-layout", list(map(str, command)), + OUT / "planner-head-resolved-layout.log", config["stamp"], env) + layout = json.loads(LAYOUT.read_text(encoding="utf-8")) + ids = {row["stable_id"] for row in layout["cavern_edges"]} + required = {"tunnel-network/strate-2/route-edge-0", + "tunnel-network/strate-2/departure-edge", + "vertical-shafts/strate-3/shaft-access-0"} + if not required.issubset(ids): + raise RuntimeError("resolved owner seed-0 layout is missing a capture anchor") + return layout + + +def main() -> int: + if not DATA.is_file(): + raise SystemExit("missing owner fixture") + OUT.mkdir(parents=True, exist_ok=True) + ARTIFACTS.mkdir(parents=True, exist_ok=True) + env = os.environ.copy() + env["LP_NUM_THREADS"] = "12" + env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache") + layout = resolve_planner_layout(env) + quality_tool = ROOT / "Standalone/Tools/verify/capture_quality.py" + manifest = [] + failures = [] + for version, config in VERSIONS.items(): + for view in views(layout): + name = view["name"] + camera, target = view["camera"], view["target"] + yaw, pitch = angles(camera, target) + mesh_dir = OUT / "meshes" / version + mesh_dir.mkdir(parents=True, exist_ok=True) + mesh = mesh_dir / f"{name}.vfmesh" + capture_layout = mesh_dir / f"{name}-layout.json" + export = [config["headless"], DATA, "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--workers=12", + "--clip-radius=6", "--max-clip-level=0", "--max-level=0", + "--force-generate", "--recipe=surface-world", + "--render-distance-chunks=0", f"--camera={fmt((*camera, yaw, pitch))}", + f"--resolved-layout-output={capture_layout}", f"--mesh-export={mesh}"] + stamped_run(f"{version}-{name}-export", list(map(str, export)), + OUT / f"{version}-{name}-export.log", config["stamp"], env) + if not mesh.is_file() or mesh.stat().st_size == 0: + raise RuntimeError(f"{version}-{name}: empty mesh export") + rendered_layout = json.loads(capture_layout.read_text(encoding="utf-8")) + png = OUT / f"{version}-{name}.png" + jpg = ARTIFACTS / f"{version}-{name}.jpg" + render = [config["renderer"], mesh, png, "--size=1280x720", + f"--fov-deg={float(view.get('fov', 78.0)):.1f}", + f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}", + f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable", + "--headlamp=on", "--height-tint=on", "--height-center-m=-950", + "--height-range-m=60", "--tile-edges=off", + f"--focus-region={fmt(view['focus'])}"] + stamped_run(f"{version}-{name}-render", list(map(str, render)), + OUT / f"{version}-{name}-render.log", config["stamp"], env) + subprocess.run([*LIMIT, "timeout", "120s", "ffmpeg", "-nostdin", + "-hide_banner", "-loglevel", "error", "-y", "-i", png, + "-frames:v", "1", "-q:v", "4", jpg], cwd=ROOT, env=env, + check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, + timeout=125) + check = subprocess.run([*LIMIT, "timeout", "120s", sys.executable, + quality_tool, jpg], cwd=ROOT, env=env, text=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + check=False, timeout=125) + quality_ok = check.returncode == 0 and "status=PASS" in check.stdout + if not quality_ok: + failures.append(f"{version}-{name}") + quality_line = (check.stdout.strip().splitlines()[-1] + if check.stdout.strip() else f"quality exit={check.returncode}") + manifest.append({"version": version, "view": name, "layer": view["layer"], + "file": str(jpg.relative_to(ROOT)), "camera_m": camera, "target_m": target, + "yaw_deg": yaw, "pitch_deg": pitch, + "source_stamp": config["stamp"], + "capture_layout_identity": rendered_layout.get("stable_identity"), + "pose_layout_identity": layout.get("stable_identity"), "quality_status": + "PASS" if quality_ok else "FAIL", "quality": quality_line}) + print(f"[L150Capture] version={version} view={name} quality=" + f"{'PASS' if quality_ok else 'FAIL'} file={jpg.relative_to(ROOT)}", + flush=True) + (ARTIFACTS / "manifest.json").write_text( + json.dumps(manifest, indent=2) + "\n", encoding="utf-8") + (ARTIFACTS / "INDEX.md").write_text( + "# L150 matched owner-world captures\n\n" + "Seed 0, with camera poses from the planner layout resolved against the owner's " + "embedded Core plan and reused for both versions. The plan is imported without a seed " + "override. The two " + "Tunnel Network views sit at the planned route and departure joints; the Vertical " + "Shafts views look down from the access landing and up from a ledge.\n\n" + + "\n".join(f"- {row['layer']} / {row['view']}: " + f"[{row['version']}]({Path(row['file']).name}) " + f"— {row['quality_status']}" for row in manifest) + "\n", + encoding="utf-8") + with (ARTIFACTS / "INDEX.md").open("a", encoding="utf-8") as index: + index.write("\nAll eight exports and renders completed. The capture-quality gate " + "passed for all four planner-head images and one assembler/world image. Three " + "assembler/world images have insufficient image variance because no mesh surface " + "is visible from those same viewpoints; those gate failures are recorded as the " + "baseline result.\n") + print(f"[L150CaptureSummary] views={len(manifest)//2} pairs={len(manifest)//2} " + f"quality_failures={len(failures)}", flush=True) + return 0 if not failures else 1 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as error: + print(f"[L150CaptureFailure] {error}", file=sys.stderr, flush=True) + raise SystemExit(1) diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/RunL150FinalTiming.py b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150FinalTiming.py new file mode 100644 index 0000000..a1e1f38 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150FinalTiming.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""L80 comparison of the planner HEAD with the assembler/world baseline.""" +from __future__ import annotations + +import json +import os +import re +import statistics +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l150/timing" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARIES = { + "assembler-world": ( + ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a", + "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"), + "planner-head": ( + ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l150-head-clang", + "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f"), +} +CAMERAS = { + 0: (8.0, 16.0, -372.2626953125), + 1: (25.8369140625, 3.19921875, -737.0), + 2: (19.166015625, -14.2001953125, -753.3740234375), + 3: (-20.0625, -5.068359375, -900.353515625), +} +PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M) +FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") +NEAR = re.compile( + r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$", + re.M) +STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M) + + +def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + binary, expected_stamp = BINARIES[version] + camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0" + label = f"s{strate}-w{workers}-{version}-{phase}{replicate}" + log = OUT / f"{label}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", f"--workers={workers}", + "--clip-radius=3", "--max-clip-level=8", "--max-level=8", + f"--camera={camera}", "--profile-lod-work", "--stream-scheduler-policy=3", + ] + started = time.monotonic() + with log.open("w", encoding="utf-8") as output: + proc = subprocess.run(command, cwd=ROOT, stdout=output, + stderr=subprocess.STDOUT, check=False, timeout=1740) + content = log.read_text(encoding="utf-8", errors="replace") + pipeline, near, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content) + if (proc.returncode or pipeline is None or near is None or stamp is None + or stamp.group(1) != expected_stamp): + raise SystemExit(f"L80 timing failed or source stamp mismatch: {log}; exit={proc.returncode}") + fields = dict(FIELDS.findall(pipeline.group(1))) + row = { + "strate": strate, "workers": workers, "version": version, + "phase": phase, "replicate": replicate, "source_stamp": stamp.group(1), + "lod02_ready_s": float(near.group(3)), + "whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")), + "whole_tiles": int(fields["whole_tiles"].split("/")[0]), + "wall_s": round(time.monotonic() - started, 3), + "log": str(log.relative_to(ROOT)), + } + print("[L150TimingRun] " + json.dumps(row, sort_keys=True), flush=True) + return row + + +def quiet_machine() -> tuple[float, float]: + load_1m = os.getloadavg()[0] + + def totals() -> tuple[int, int]: + fields = Path("/proc/stat").read_text().splitlines()[0].split() + values = [int(value) for value in fields[1:]] + return sum(values), values[3] + values[4] + + before = totals() + time.sleep(2.0) + after = totals() + idle = 100.0 * (after[1] - before[1]) / max(1, after[0] - before[0]) + return load_1m, idle + + +def main() -> int: + if any(not path.is_file() for path in [DATA, *(entry[0] for entry in BINARIES.values())]): + raise SystemExit("missing L150 timing input") + OUT.mkdir(parents=True, exist_ok=True) + load_1m, idle = quiet_machine() + (OUT / "machine-load-before.txt").write_text( + f"1-minute load: {load_1m:.2f}\n2-second CPU idle: {idle:.1f}%\n") + print(f"[L150TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True) + if load_1m >= 1.5 or idle < 97.0: + raise SystemExit("timing refused: machine is above the L80 quiet-load gate") + + rows = [] + versions = tuple(BINARIES) + for strate in range(4): + for workers in (8, 12): + for version in versions: + run(strate, workers, version, "warmup", 0) + for replicate in range(1, 6): + order = versions if replicate % 2 else tuple(reversed(versions)) + for version in order: + rows.append(run(strate, workers, version, "measured", replicate)) + (OUT / "runs-partial.json").write_text( + json.dumps(rows, indent=2) + "\n", encoding="utf-8") + + measured = [row for row in rows if row["phase"] == "measured"] + summary = [] + for strate in range(4): + for workers in (8, 12): + group = [row for row in measured + if row["strate"] == strate and row["workers"] == workers] + medians = { + version: { + key: statistics.median(row[key] for row in group + if row["version"] == version) + for key in ("lod02_ready_s", "whole_view_ready_s") + } + for version in versions + } + entry = {"strate": strate, "workers": workers, + "replicates_per_version": 5, "medians": medians} + for key in ("lod02_ready_s", "whole_view_ready_s"): + entry[f"planner_head_vs_assembler_world_{key}_delta_pct"] = round( + 100.0 * (medians["planner-head"][key] + / medians["assembler-world"][key] - 1.0), 2) + entry[f"{key}_paired_deltas_pct"] = [ + round(100.0 * ( + next(row[key] for row in group + if row["version"] == "planner-head" + and row["replicate"] == replicate) + / next(row[key] for row in group + if row["version"] == "assembler-world" + and row["replicate"] == replicate) - 1.0), 3) + for replicate in range(1, 6)] + summary.append(entry) + (OUT / "summary.json").write_text(json.dumps({ + "a_a_noise_floor_pct": 1.0, + "timing_protocol": ( + "one warm-up per binary, five interleaved runs per cell, W8/W12, " + "all four owner layers; L137 A/A median noise floor ±1%"), + "runs": rows, + "summary": summary, + }, indent=2) + "\n", encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/RunL150SeedSweep.py b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150SeedSweep.py new file mode 100644 index 0000000..233ccdc --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150SeedSweep.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Run L147 composed gates on the actual L150 planner HEAD build.""" +from __future__ import annotations + +import importlib.util +from pathlib import Path +import sys + + +ROOT = Path(__file__).resolve().parents[4] +SOURCE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux147/RunL147SeedSweep.py" +spec = importlib.util.spec_from_file_location("l147_seed_sweep", SOURCE) +if spec is None or spec.loader is None: + raise SystemExit("cannot load the L147 composed gate runner") +runner = importlib.util.module_from_spec(spec) +spec.loader.exec_module(runner) +runner.OUT = ROOT / "Saved/Linux/l150/seeds" +runner.BIN = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l150-head-clang" +runner.STAMP = "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f" + + +if __name__ == "__main__": + raise SystemExit(runner.main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/RunL150Unreal.py b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150Unreal.py new file mode 100644 index 0000000..3206333 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/RunL150Unreal.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +"""Measure planner HEAD and code-equivalent assembler/world with the L121/L127 runners.""" +from __future__ import annotations + +import importlib.util +import json +import os +from pathlib import Path +import statistics +import sys + + +ROOT = Path(__file__).resolve().parents[4] +RESEARCH = ROOT / "Standalone/Research/WorldAssemblerM3" +EDITOR = Path("/home/jahni/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Cmd") +HOSTS = { + "assembler-world": ROOT / "Saved/Linux/l127/ue-baseline-project", + "planner-head": Path("/home/jahni/VFHost"), +} +CAMERAS = { + 0: (8.0, 16.0, -372.2626953125), + 1: (25.8369140625, 3.19921875, -737.0), + 2: (19.166015625, -14.2001953125, -753.3740234375), + 3: (-20.0625, -5.068359375, -900.353515625), +} + + +def load(name: str, path: Path): + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"cannot load {path}") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def configure(arrival, movement, label: str): + host = HOSTS[label].resolve() + out = ROOT / "Saved/Linux/l150/ue" / label + plugin_saved = host / "Plugins/VoxelForge/Saved/Linux/l150" + out.mkdir(parents=True, exist_ok=True) + plugin_saved.mkdir(parents=True, exist_ok=True) + os.environ["XDG_CONFIG_HOME"] = str(out / "config") + arrival.HOST = host + arrival.EDITOR = EDITOR + arrival.OUT = out / "start" + arrival.HOST_SAVED = out / "host" + arrival.HOST_PLUGIN_SAVED = plugin_saved + arrival.RUN_TIMEOUT_S = 1740 + movement.HOST = host + movement.EDITOR = EDITOR + movement.OUT = out / "movement" + movement.HOST_SAVED = out / "host" + movement.HOST_OUT = plugin_saved / "movement" + movement.RUN_TIMEOUT_S = 1740 + return host, out + + +def start_matrix(arrival, movement, label: str) -> dict: + host, out = configure(arrival, movement, label) + spawns = { + 0: (8.0, 16.0, -372.2626953125), + 1: (25.8369140625, 3.19921875, -737.0), + 2: (19.166015625, -14.2001953125, -753.3740234375), + 3: (-20.0625, -5.068359375, -900.353515625), + } + starts = [] + for index, position in spawns.items(): + stratum = "FI" if index == 1 else "SW" + arrival.SPAWNS_M[stratum] = position + row = arrival.run(stratum, 1, f"start-s{index}", 8, 1, 0, 1, 1, 0, 1, + False, False, 1) + row["strate_index"] = index + row["spawn_m"] = list(position) + if row.get("lod02_full_settled_t_s") is None: + raise RuntimeError(f"LOD0-2 did not settle for S{index}: {row}") + starts.append(row) + print("[L150UEStart] " + json.dumps(row, sort_keys=True), flush=True) + (out / "start-summary.json").write_text( + json.dumps(starts, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + row = moving_run(movement, label, 1) + (out / "movement-first.json").write_text( + json.dumps(row, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print("[L150UEMovement] " + json.dumps(row, sort_keys=True), flush=True) + return {"host": str(host), "starts": starts, "first_movement": row} + + +def moving_run(movement, label: str, repetition: int) -> dict: + condition = "c743f2a" if label == "assembler-world" else "planner-head" + row = movement.run_unreal(condition, repetition) + events = movement.parse_events(ROOT / row["trace"]) + row["supplemental_far_applies_moving"] = sum( + int(event.get("supplemental_far_applies", 0)) + for event in events + if event.get("type") == "apply_frame" + and float(event.get("speed_cm_s", 0.0)) >= 50.0) + if row.get("effective_tile_slots") != 8: + raise RuntimeError(f"expected eight effective tile slots: {row}") + return row + + +def main() -> int: + out = ROOT / "Saved/Linux/l150/ue" + out.mkdir(parents=True, exist_ok=True) + arrival = load("l150_arrival", RESEARCH / "Linux121/RunL121Arrival.py") + movement = load("l150_movement", RESEARCH / "Linux121/RunL121Movement.py") + summaries = {} + all_runs = {label: [] for label in HOSTS} + for label in HOSTS: + summaries[label] = start_matrix(arrival, movement, label) + all_runs[label].append(summaries[label]["first_movement"]) + + for repetition in range(2, 6): + order = ("planner-head", "assembler-world") if repetition % 2 == 0 \ + else ("assembler-world", "planner-head") + for label in order: + configure(arrival, movement, label) + row = moving_run(movement, label, repetition) + all_runs[label].append(row) + (out / "movement-runs-partial.json").write_text( + json.dumps(all_runs, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print("[L150UEMovementRepeat] " + json.dumps({ + key: row.get(key) for key in ( + "condition", "repetition", "near_ready_moving_mean_pct", + "far_holes_moving_mean_pct", "game_thread_tick_p99_ms", + "game_thread_tick_max_ms", "supplemental_far_applies_moving", + "wall_s", "trace")}, sort_keys=True), flush=True) + + keys = ("near_ready_moving_mean_pct", "far_holes_moving_mean_pct", + "game_thread_tick_p99_ms", "game_thread_tick_max_ms") + medians = { + label: {key: statistics.median(row[key] for row in rows) for key in keys} + for label, rows in all_runs.items() + } + delta = { + key: round(100.0 * (medians["planner-head"][key] + / medians["assembler-world"][key] - 1.0), 2) + for key in keys + } + result = { + "baseline_commit": "c743f2a (code-equivalent to assembler/world eb47d72)", + "protocol": ( + "L121 arrival and L127 movement; starts at four owner strata; five interleaved " + "30 m/s runs per build; 12 s move plus 5 s dwell; eight effective tile slots"), + "starts": summaries, + "movement_runs": all_runs, + "movement_medians": medians, + "planner_head_vs_assembler_world_delta_pct": delta, + } + (out / "summary.json").write_text( + json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print("[L150UESummary] " + json.dumps({ + "movement_medians": medians, + "planner_head_vs_assembler_world_delta_pct": delta, + "starts_s": { + label: [round(row["lod02_full_settled_t_s"], 3) + for row in summary["starts"]] + for label, summary in summaries.items() + }, + }, sort_keys=True), flush=True) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as error: + print(f"[L150UEFailure] {error}", file=sys.stderr, flush=True) + raise SystemExit(1) diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/INDEX.md b/Standalone/Research/WorldAssemblerM3/Linux150/captures/INDEX.md new file mode 100644 index 0000000..04ccab6 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/captures/INDEX.md @@ -0,0 +1,14 @@ +# L150 matched owner-world captures + +Seed 0, with camera poses from the planner layout resolved against the owner's embedded Core plan and reused for both versions. The plan is imported without a seed override. The two Tunnel Network views sit at the planned route and departure joints; the Vertical Shafts views look down from the access landing and up from a ledge. + +- Tunnel Network / tunnel-network-route: [assembler-world](assembler-world-tunnel-network-route.jpg) — FAIL +- Tunnel Network / tunnel-network-departure: [assembler-world](assembler-world-tunnel-network-departure.jpg) — PASS +- Vertical Shafts / vertical-shafts-looking-down: [assembler-world](assembler-world-vertical-shafts-looking-down.jpg) — FAIL +- Vertical Shafts / vertical-shafts-looking-up: [assembler-world](assembler-world-vertical-shafts-looking-up.jpg) — FAIL +- Tunnel Network / tunnel-network-route: [planner-head](planner-head-tunnel-network-route.jpg) — PASS +- Tunnel Network / tunnel-network-departure: [planner-head](planner-head-tunnel-network-departure.jpg) — PASS +- Vertical Shafts / vertical-shafts-looking-down: [planner-head](planner-head-vertical-shafts-looking-down.jpg) — PASS +- Vertical Shafts / vertical-shafts-looking-up: [planner-head](planner-head-vertical-shafts-looking-up.jpg) — PASS + +All eight exports and renders completed. The capture-quality gate passed for all four planner-head images and one assembler/world image. Three assembler/world images have insufficient image variance because no mesh surface is visible from those same viewpoints; those gate failures are recorded as the baseline result. diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg b/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa006279c64952e4f05aa70bd5580ac48dfd3af6 GIT binary patch literal 18339 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg b/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg new file mode 100644 index 0000000000000000000000000000000000000000..148e76e7c8f0e00d5b2cab6c359b52d291645f44 GIT binary patch literal 11005 zcmeIuJ&FQB5XJG=GYBo~G*dkTW1xwdf#M{Bk%6g~5bxk|WRJ4J9>CC#yV#0}s5g-R z3kvE{Q1DB~bXB?D?6&iE+AgN;tW~;bCGUOLcYhu!dc`v#1T(wOq6ncZ&0-brg;iBu z*ZqFe3E HWjfsebiNp8 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg b/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg new file mode 100644 index 0000000000000000000000000000000000000000..148e76e7c8f0e00d5b2cab6c359b52d291645f44 GIT binary patch literal 11005 zcmeIuJ&FQB5XJG=GYBo~G*dkTW1xwdf#M{Bk%6g~5bxk|WRJ4J9>CC#yV#0}s5g-R z3kvE{Q1DB~bXB?D?6&iE+AgN;tW~;bCGUOLcYhu!dc`v#1T(wOq6ncZ&0-brg;iBu z*ZqFe3E HWjfsebiNp8 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg b/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2ed48b997b23edbae13f37813fe01bf6b0a8b5e0 GIT binary patch literal 15079 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 DfHx73 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/manifest.json b/Standalone/Research/WorldAssemblerM3/Linux150/captures/manifest.json new file mode 100644 index 0000000..f7bfaf7 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux150/captures/manifest.json @@ -0,0 +1,186 @@ +[ + { + "version": "assembler-world", + "view": "tunnel-network-route", + "layer": "Tunnel Network", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg", + "camera_m": [ + 17.334593752829058, + -13.396520050883792, + -751.9240234375 + ], + "target_m": [ + 28.32312498585471, + -18.218571620581038, + -751.9240234375 + ], + "yaw_deg": 113.69310802290845, + "pitch_deg": 0.0, + "source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621", + "capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "FAIL", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-route.jpg pixels=921600 mean_luma=112.000 stddev_luma=0.000 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL" + }, + { + "version": "assembler-world", + "view": "tunnel-network-departure", + "layer": "Tunnel Network", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg", + "camera_m": [ + -22.06185250467782, + -5.017471635430169, + -810.9455078125 + ], + "target_m": [ + -10.065737476610899, + -5.322798072849153, + -810.9455078125 + ], + "yaw_deg": 91.45798369615608, + "pitch_deg": 0.0, + "source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621", + "capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-tunnel-network-departure.jpg pixels=921600 mean_luma=106.806 stddev_luma=3.804 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "assembler-world", + "view": "vertical-shafts-looking-down", + "layer": "Vertical Shafts", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg", + "camera_m": [ + -31.001772, + -7.103472, + -901.9274509999999 + ], + "target_m": [ + -31.001772, + -8.066018, + -929.377451 + ], + "yaw_deg": 180.0, + "pitch_deg": -87.99172181188044, + "source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621", + "capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "FAIL", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-down.jpg pixels=921600 mean_luma=112.000 stddev_luma=0.000 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL" + }, + { + "version": "assembler-world", + "view": "vertical-shafts-looking-up", + "layer": "Vertical Shafts", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg", + "camera_m": [ + -31.001772, + -8.066018, + -945.9274509999999 + ], + "target_m": [ + -31.001772, + -8.066018, + -901.377451 + ], + "yaw_deg": 0.0, + "pitch_deg": 90.0, + "source_stamp": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621", + "capture_layout_identity": "resolved-layout-v3-fnv64:0589300eb07dfe06", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "FAIL", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/assembler-world-vertical-shafts-looking-up.jpg pixels=921600 mean_luma=111.749 stddev_luma=1.375 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL" + }, + { + "version": "planner-head", + "view": "tunnel-network-route", + "layer": "Tunnel Network", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-route.jpg", + "camera_m": [ + 17.334593752829058, + -13.396520050883792, + -751.9240234375 + ], + "target_m": [ + 28.32312498585471, + -18.218571620581038, + -751.9240234375 + ], + "yaw_deg": 113.69310802290845, + "pitch_deg": 0.0, + "source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f", + "capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-route.jpg pixels=921600 mean_luma=133.954 stddev_luma=13.005 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "planner-head", + "view": "tunnel-network-departure", + "layer": "Tunnel Network", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg", + "camera_m": [ + -22.06185250467782, + -5.017471635430169, + -810.9455078125 + ], + "target_m": [ + -10.065737476610899, + -5.322798072849153, + -810.9455078125 + ], + "yaw_deg": 91.45798369615608, + "pitch_deg": 0.0, + "source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f", + "capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg pixels=921600 mean_luma=115.401 stddev_luma=12.660 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "planner-head", + "view": "vertical-shafts-looking-down", + "layer": "Vertical Shafts", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-down.jpg", + "camera_m": [ + -31.001772, + -7.103472, + -901.9274509999999 + ], + "target_m": [ + -31.001772, + -8.066018, + -929.377451 + ], + "yaw_deg": 180.0, + "pitch_deg": -87.99172181188044, + "source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f", + "capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-down.jpg pixels=921600 mean_luma=112.234 stddev_luma=3.319 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "planner-head", + "view": "vertical-shafts-looking-up", + "layer": "Vertical Shafts", + "file": "Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg", + "camera_m": [ + -31.001772, + -8.066018, + -945.9274509999999 + ], + "target_m": [ + -31.001772, + -8.066018, + -901.377451 + ], + "yaw_deg": 0.0, + "pitch_deg": 90.0, + "source_stamp": "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f", + "capture_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "pose_layout_identity": "resolved-layout-v3-fnv64:cc87ee727c483039", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg pixels=921600 mean_luma=111.590 stddev_luma=2.483 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + } +] diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg b/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-tunnel-network-departure.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f6e4564a497d1a39ed8fb5b67963e85a2679ddd GIT binary patch literal 17938 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⪙aB;xhkHu4{?E_c6tDn%k6pux74A5!qKMctYLT(OFPa);o!CYb2N-{-ead zSd}pj$S$)cY%sF_6pbS+U261}QiUG~+mf7FPe`k^e$tRk>#XXZl1iImBjA&-Am!ZJ zcgi7aHPBpdxxWLlM)2)Vcvi|1@;$|O7!2;I&5!qpPh=&nZ%s8j3RK_gxGE7?89%Xj zXZy%`H(6?~8Lc?7S_`K-T zJiFM+zP_R@1?`%k*;zAzYSaF!vY6qcTz_|`4zcNtD85hu~H*-%T^Eu*bK-Ux_RByB2*%!EJYGEya6F8 zvOo7tv(5ek>+tfN7iZ4-oXec=InVR`+`{^3>tV=*9nr?VirrI?f8g@!x^)_})=j|_ zMrWr-qSQ{&*@U+>fh}YAPnIZO4Ep9Z6@8eiPdvKCSEiGcB*8`uagQwy-7i0aOQnSh z6T^{j=;Cf?vG(1V6U9TN5E*xa7Dy93gO|R80IytZyRsy>Ki*15n;J@_d&ZKUT~AS5van9%UK+02~<(bWYbz&=zOc7G_#n$JBUKqX|NT zL5*>wTWG$EttJPb_#luTg8G=V;KUWipvQU`T2_z^%F3MSx~m?} zL*Ux5#Havm1dxNMxYR3~=*8Qk)>R!)#4~X|0#U_Z-1nD$Eyk)ODtlU*5h%j|MhDyq_lu}HPg&_D&1_s`9XWe!t z#8LG)w={dENz;jkS{?n9c2!c{)vV?drI=^kEnF<)VWg8L!R6JoAAAxt*ess{c6w=A z^2));#ym^jcjw8Iv(v9bQUIS8gJ2({ve1*4S1?Gqo?Td!e#l^5+4i%3hqntEgk5szC2FS_!jV|Sm} zHpql8R?n%gMMc&?QP8AP_n~&e3;Y=ti`&R*1pNT>VU2BB{25E+Va;yt> z$&+8Pn7{1Ml~;5D8VT;PfIJEK8kzc#2iIM_gzs@1QqgY5qa22Idd0Qm*jM%S$q#6} zV($5$98RoessWw_E|AU1?JTS-tcz8^;=AvK$u;;X)rSMZm?4yr6M3BKVTqwZv(%?C zh6qU`jL2j8pm+$vNlg%s4Q)>DMr_a)+t?p(^v!bG1Lx-cLEMQlz_WO2IV3Y!A<$9% z1*sCQPXiGug$|tG6L88g!WhfFPk$ttS4-t<%CmTgA(R6A9G2gub}Sgq>a%A!bk!_FblaZhPte#Y>?bbV>)82xWu;}6 z-NrdwYnxH~NGO>ps2oNGUjtG$jY^73!j%brq&SqTk8Zx$yJSd*$g1s$$DS&4@?dPfhBo2eTH(8{4Db&7! zW}{KFZ>^O!twi+>Xh|FbKb^`Pk5|X%dA}3oc=^?J=7d|O6PDqLm38RB|Khb3>$Vv? zbp1E*q1$&SWVuK3Q9$P8XdeYTSEQaP837z)bunn0Ih3j^aAdyf?HpyHz4I#kS-SME zC4Q@C3x5u4Gbwt`C{z*|bQFh?_ z#Y_l~J03dT#i6D{?iD$xOgZz)ZkQz)ZkQz)ZkQz)WB(5rCV<{ssU+ Bi0uFX literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg b/Standalone/Research/WorldAssemblerM3/Linux150/captures/planner-head-vertical-shafts-looking-up.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f6bcaa37ba2d10ec621cd609fac18f9a19f9eb90 GIT binary patch literal 22887 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 h|8*hw`-Xob@HYZ~Bk(r@eUtoKLEf7LG=It literal 0 HcmV?d00001