diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/FinalizeL156SeedSweep.py b/Standalone/Research/WorldAssemblerM3/Linux156/FinalizeL156SeedSweep.py new file mode 100644 index 0000000..a069b7f --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/FinalizeL156SeedSweep.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""Recompute the full 16-seed sweep summaries and landing band margins.""" +from __future__ import annotations + +import json +from pathlib import Path + +from PassageEnvelopeAudit import _landing_content_band_audit + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l156/seeds" + + +def finalize(kind: str) -> dict: + directory = OUT / kind + partial = directory / "runs-partial.json" + results = json.loads(partial.read_text(encoding="utf-8")) if partial.is_file() else [] + for row in results: + layout_path = ROOT / row["layout"] + bands = _landing_content_band_audit( + json.loads(layout_path.read_text(encoding="utf-8"))) if layout_path.is_file() else [] + band_pass = len(bands) == 6 and all(entry["passed"] for entry in bands) + envelope = row.get("envelope_audit") or {} + envelope["landing_content_band"] = bands + envelope["landing_content_band_passed"] = band_pass + envelope["passed"] = bool(envelope.get("passed") and band_pass) + row["envelope_audit"] = envelope + checks = row.setdefault("checks", {}) + checks["envelope"] = bool(envelope["passed"] + and envelope.get("outside_chamber_conflicts") == 0) + checks["envelope_negative_control"] = bool( + envelope.get("negative_controls") == envelope.get("negative_controls_expected") == 1) + checks["landing_content_band"] = band_pass + failed = [name for name, passed in checks.items() if not passed] + row["verdict"] = "PASS" if not failed else "FAIL" + row["reason"] = ", ".join(failed) if failed else "all requested gates pass" + results.sort(key=lambda row: row["seed"]) + completed = len(results) + passed = sum(row["verdict"] == "PASS" for row in results) + missing = sorted(set(range(16)) - {row["seed"] for row in results}) + summary = {"kind": kind, "source_stamp": ( + "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0"), + "completed": completed, "passed": passed, "failed": completed - passed, + "missing_seeds": missing, "status": "PASS" if completed == 16 and passed == 16 else "FAIL", + "results": results} + partial.write_text(json.dumps(results, indent=2) + "\n", encoding="utf-8") + (directory / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", + encoding="utf-8") + print(f"[L156SeedSweepFinal] kind={kind} passed={passed}/{completed} " + f"missing={len(missing)} status={summary['status']}") + return summary + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--kind", choices=("tunnel-network", "vertical-shafts"), required=True) + args = parser.parse_args() + raise SystemExit(0 if finalize(args.kind)["status"] == "PASS" else 1) diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/PassageEnvelopeAudit.py b/Standalone/Research/WorldAssemblerM3/Linux156/PassageEnvelopeAudit.py new file mode 100644 index 0000000..90712da --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/PassageEnvelopeAudit.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python3 +"""Audit cave primitive bounds against passage envelopes, allowing chamber joints only.""" +from __future__ import annotations + +import math +from typing import Any + + +INFLUENCE_REACH_M = 1.053334 +SHELL_THICKNESS_M = 0.75 + + +def _point(control: dict[str, Any], scale: float) -> tuple[float, float, float, float]: + x, y, z = control["position_q"] + return (x * scale, y * scale, z * scale, control["radius_q"] * scale) + + +def _knot_span(a, b) -> float: + return max(1.0e-6, math.sqrt(math.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2))) + + +def _evaluate(controls, span: int, t: float): + p1, p2 = controls[span], controls[span + 1] + p0 = controls[span - 1] if span else controls[0] + p3 = controls[min(span + 2, len(controls) - 1)] + dt0, dt1, dt2 = _knot_span(p0, p1), _knot_span(p1, p2), _knot_span(p2, p3) + t2, t3 = t * t, t * t * t + result = [] + for axis in (0, 1, 3): + m1 = dt1 * ((p1[axis] - p0[axis]) / dt0 + - (p2[axis] - p0[axis]) / (dt0 + dt1) + + (p2[axis] - p1[axis]) / dt1) + m2 = dt1 * ((p2[axis] - p1[axis]) / dt1 + - (p3[axis] - p1[axis]) / (dt1 + dt2) + + (p3[axis] - p2[axis]) / dt2) + value = ((2 * t3 - 3 * t2 + 1) * p1[axis] + + (-2 * t3 + 3 * t2) * p2[axis] + + (t3 - 2 * t2 + t) * m1 + (t3 - t2) * m2) + result.append(value) + radius = min(max(result[2], min(p1[3], p2[3])), max(p1[3], p2[3])) + return result[0], result[1], p1[2] + (p2[2] - p1[2]) * t, radius + + +def _segments(passage: dict[str, Any], scale: float): + controls = [_point(row, scale) for row in passage["control_points"]] + route_end = min(max(1, int(passage.get("lower_mouth_control_index", len(controls) - 1))), + len(controls) - 1) + along = 0.0 + for span in range(route_end): + a, b = controls[span], controls[span + 1] + chord = math.hypot(b[0] - a[0], b[1] - a[1]) + steps = max(1, math.ceil(chord / 2.0)) + prior = _evaluate(controls, span, 0.0) + for step in range(1, steps + 1): + current = _evaluate(controls, span, step / steps) + length = math.dist(prior[:3], current[:3]) + yield prior, current, along, max(prior[3], current[3]) + INFLUENCE_REACH_M + along += length + prior = current + + +def _distance_squared(a, b, bounds, t: float) -> float: + total = 0.0 + for axis in range(3): + pos = a[axis] + (b[axis] - a[axis]) * t + lo, hi = bounds[axis] + gap = lo - pos if pos < lo else pos - hi if pos > hi else 0.0 + total += gap * gap + return total + + +def _overlap_interval(a, b, bounds, radius: float): + cuts = {0.0, 1.0} + for axis in range(3): + delta = b[axis] - a[axis] + if abs(delta) < 1.0e-15: + continue + for edge in bounds[axis]: + t = (edge - a[axis]) / delta + if 0.0 < t < 1.0: + cuts.add(t) + cuts = sorted(cuts) + best_t, best_d2 = 0.0, math.inf + for lo, hi in zip(cuts, cuts[1:]): + mid = (lo + hi) * 0.5 + quadratic = linear = 0.0 + for axis in range(3): + delta = b[axis] - a[axis] + pos = a[axis] + delta * mid + low, high = bounds[axis] + edge = low if pos < low else high if pos > high else None + if edge is not None: + quadratic += delta * delta + linear += delta * (a[axis] - edge) + candidate = min(hi, max(lo, -linear / quadratic)) if quadratic else lo + d2 = _distance_squared(a, b, bounds, candidate) + if d2 < best_d2: + best_t, best_d2 = candidate, d2 + for t in (0.0, 1.0): + d2 = _distance_squared(a, b, bounds, t) + if d2 < best_d2: + best_t, best_d2 = t, d2 + if best_d2 > radius * radius: + return None + + radius2 = radius * radius + if _distance_squared(a, b, bounds, 0.0) <= radius2: + left = 0.0 + else: + low, high = 0.0, best_t + for _ in range(42): + mid = (low + high) * 0.5 + if _distance_squared(a, b, bounds, mid) <= radius2: + high = mid + else: + low = mid + left = high + if _distance_squared(a, b, bounds, 1.0) <= radius2: + right = 1.0 + else: + low, high = best_t, 1.0 + for _ in range(42): + mid = (low + high) * 0.5 + if _distance_squared(a, b, bounds, mid) <= radius2: + low = mid + else: + high = mid + right = low + return left, right, best_t + + +def _inside_chamber(point, chamber: dict[str, Any], scale: float) -> bool: + cx, cy, cz = (value * scale for value in chamber["floor_center_q"]) + radius = chamber["radius_q"] * scale + clear = chamber["clear_height_q"] * scale + gx, gy = (value / 65536.0 for value in chamber["floor_gradient_q16"]) + floor = cz + gx * (point[0] - cx) + gy * (point[1] - cy) + chamber_distance = max(math.hypot(point[0] - cx, point[1] - cy) - radius, + floor - point[2], point[2] - (floor + clear)) + return chamber_distance <= SHELL_THICKNESS_M + + +def _primitive_bounds(row: dict[str, Any]): + return tuple((row["bounds_min_um"][i] / 1.0e6, + row["bounds_max_um"][i] / 1.0e6) for i in range(3)) + + +def _landing_content_band_audit(layout: dict[str, Any]) -> list[dict[str, Any]]: + """Recheck the validator's complete graded floor, headroom and shell envelope.""" + voxel_um = int(layout["core_voxel_size_um"]) + q_per_metre = 256 * 1_000_000 / voxel_um + strata = {int(row["strate_index"]): row for row in layout.get("strates", [])} + + def q_to_um(q: int) -> int: + numerator = q * voxel_um + return numerator // 256 if numerator >= 0 else -((-numerator) // 256) + + result = [] + shell_q = math.floor(0.75 * q_per_metre + 0.5) + for passage in layout.get("passages", []): + for side, key, strate_key in ( + ("upper", "upper_landing_chamber", "upper_strate_index"), + ("lower", "lower_landing_chamber", "lower_strate_index")): + chamber = passage[key] + host = strata.get(int(passage[strate_key])) + content = host.get("content", {}) if host else {} + gradient = chamber["floor_gradient_q16"] + gradient_magnitude = math.isqrt(gradient[0] ** 2 + gradient[1] ** 2) + plane_reach_q = (int(chamber["radius_q"]) * gradient_magnitude + 65535) // 65536 + floor_q = int(chamber["floor_center_q"][2]) + minimum_um = q_to_um(floor_q - plane_reach_q - shell_q) + maximum_um = q_to_um(floor_q + plane_reach_q + + int(chamber["clear_height_q"]) + shell_q) + band_bottom_um, band_top_um = int(content.get("bottom_um", 0)), int( + content.get("top_um", 0)) + passed = bool(content.get("present", False) + and minimum_um >= band_bottom_um + and maximum_um <= band_top_um) + result.append({ + "passage": passage["stable_id"], "side": side, + "host_strate": int(passage[strate_key]), + "minimum_um": minimum_um, "maximum_um": maximum_um, + "content_bottom_um": band_bottom_um, "content_top_um": band_top_um, + "bottom_margin_m": (minimum_um - band_bottom_um) / 1_000_000.0, + "top_margin_m": (band_top_um - maximum_um) / 1_000_000.0, + "passed": passed, + }) + return result + + +def _bezier(points, t: float): + u = 1.0 - t + return tuple(u**3 * points[0][axis] + 3*u*u*t * points[1][axis] + + 3*u*t*t * points[2][axis] + t**3 * points[3][axis] + for axis in range(3)) + + +def _distance_to_ellipse(radial: float, vertical: float, + radius: float, height: float) -> float: + """Euclidean distance to the edge bore's horizontal/vertical ellipse.""" + radial = abs(radial) + vertical = abs(vertical) + if (radial / radius) ** 2 + (vertical / height) ** 2 <= 1.0: + return 0.0 + + lo, hi = 0.0, math.pi / 2.0 + ratio = (math.sqrt(5.0) - 1.0) / 2.0 + + def squared_distance(angle: float) -> float: + return ((radial - radius * math.cos(angle)) ** 2 + + (vertical - height * math.sin(angle)) ** 2) + + left = hi - ratio * (hi - lo) + right = lo + ratio * (hi - lo) + left_value, right_value = squared_distance(left), squared_distance(right) + for _ in range(48): + if left_value < right_value: + hi, right, right_value = right, left, left_value + left = hi - ratio * (hi - lo) + left_value = squared_distance(left) + else: + lo, left, left_value = left, right, right_value + right = lo + ratio * (hi - lo) + right_value = squared_distance(right) + return math.sqrt(min(left_value, right_value)) + + +def _actual_edge_clearance(passage, edge, scale: float, + chamber: dict[str, Any]) -> float: + """Return minimum true tube clearance outside the landing chamber. + + The route edge is a swept ellipse, with its recorded polyline as the axis. + Comparing the tunnel tube to that ellipse avoids the false clashes caused by + intersecting two enclosing boxes. + """ + controls = [tuple(value / 1.0e6 for value in point) + for point in edge["control_points_um"]] + axis = [tuple(value / 1.0e6 for value in point) + for point in edge.get("polyline_um", [])] + if len(axis) < 2: + axis = [_bezier(controls, index / 64.0) for index in range(65)] + edge_radius = (edge["bore_radius_um"] + + edge["irregularity_amplitude_um"]) / 1.0e6 + edge_height = (edge["bore_height_um"] + + edge["irregularity_amplitude_um"]) / 1.0e6 + passage_segments = passage.get("_audit_segments") or tuple(_segments(passage, scale)) + minimum_clearance = math.inf + for a, b, _, passage_radius in passage_segments: + length = math.dist(a[:3], b[:3]) + steps = max(1, math.ceil(length / 0.05)) + for step in range(steps + 1): + t = step / steps + point = tuple(a[i] + (b[i] - a[i]) * t for i in range(3)) + if _inside_chamber(point, chamber, scale): + continue + tunnel_radius = (a[3] + (b[3] - a[3]) * t + + INFLUENCE_REACH_M) + for start, end in zip(axis, axis[1:]): + delta = tuple(end[i] - start[i] for i in range(3)) + length_squared = sum(value * value for value in delta) + projection = (sum((point[i] - start[i]) * delta[i] + for i in range(3)) / length_squared + if length_squared else 0.0) + projection = min(1.0, max(0.0, projection)) + center = tuple(start[i] + projection * delta[i] for i in range(3)) + radial = math.hypot(point[0] - center[0], point[1] - center[1]) + vertical = abs(point[2] - center[2]) + clearance = (_distance_to_ellipse(radial, vertical, + edge_radius, edge_height) + - tunnel_radius) + minimum_clearance = min(minimum_clearance, clearance) + return minimum_clearance + + +def _test_bounds(bounds, strate_index: int, passages, scale: float, + *, allow_chamber: bool = True): + for passage in passages: + if strate_index not in (passage["upper_strate_index"], passage["lower_strate_index"]): + continue + chamber_key = ("upper_landing_chamber" if strate_index == passage["upper_strate_index"] + else "lower_landing_chamber") + chamber = passage[chamber_key] + segments = passage.get("_audit_segments") + if segments is None: + segments = tuple(_segments(passage, scale)) + for a, b, along, radius in segments: + expanded = [(a[i] - radius, b[i] + radius) if a[i] <= b[i] + else (b[i] - radius, a[i] + radius) for i in range(3)] + if any(expanded[i][1] < bounds[i][0] or expanded[i][0] > bounds[i][1] + for i in range(3)): + continue + overlap = _overlap_interval(a, b, bounds, radius) + if overlap is None: + continue + first, last, closest = overlap + if allow_chamber: + points = [tuple(a[i] + (b[i] - a[i]) * t for i in range(3)) + for t in (first, closest, last)] + if all(_inside_chamber(point, chamber, scale) for point in points): + continue + point = tuple(a[i] + (b[i] - a[i]) * closest for i in range(3)) + return passage["stable_id"], along + closest * math.dist(a[:3], b[:3]), point + return None + + +def audit_layout(layout: dict[str, Any]) -> dict[str, Any]: + scale = layout["core_voxel_size_um"] / (256.0 * 1.0e6) + passages = [dict(row, _audit_segments=tuple(_segments(row, scale))) + for row in layout.get("passages", [])] + primitives = [*layout.get("cavern_rooms", []), *layout.get("cavern_edges", [])] + checked = conflicts = chamber_joints = bounds_candidates = refined_false_positives = 0 + first_conflict = None + for primitive in primitives: + bounds = _primitive_bounds(primitive) + checked += 1 + primitive_conflicted = False + for passage in passages: + found = _test_bounds(bounds, primitive["strate_index"], [passage], scale) + if not found: + continue + bounds_candidates += 1 + passage_id, station, point = found + if "control_points_um" in primitive: + side = ("upper_landing_chamber" + if primitive["strate_index"] == passage["upper_strate_index"] + else "lower_landing_chamber") + clearance = _actual_edge_clearance(passage, primitive, scale, + passage[side]) + if clearance >= 0.0: + refined_false_positives += 1 + continue + primitive_conflicted = True + conflicts += 1 + if first_conflict is None: + first_conflict = {"primitive": primitive["stable_id"], + "passage": passage_id, "station_m": round(station, 3), + "center_m": [round(value, 3) for value in point]} + break + if not primitive_conflicted: + # Count the edge's named landing association as a confirmed joint. + anchor = primitive.get("start_anchor_stable_id", "") + if anchor.endswith(("/upper-landing-chamber", "/lower-landing-chamber")): + chamber_joints += 1 + + negative_controls = 0 + long_passage = max(passages, key=lambda row: len(row["control_points"]), default=None) + if long_passage: + controls = [_point(row, scale) for row in long_passage["control_points"]] + index = min(max(1, len(controls) // 2), len(controls) - 2) + center = controls[index] + bounds = tuple((center[axis] - 0.25, center[axis] + 0.25) for axis in range(3)) + # Select the side whose chamber is farther from this forced tunnel overlap. + strate = long_passage["lower_strate_index"] + if _test_bounds(bounds, strate, [long_passage], scale, allow_chamber=False): + negative_controls = 1 + landing_content_band = _landing_content_band_audit(layout) + landing_content_band_passed = bool(landing_content_band) and all( + row["passed"] for row in landing_content_band) + return {"primitives_checked": checked, "bounds_candidates": bounds_candidates, + "refined_edge_false_positives": refined_false_positives, + "outside_chamber_conflicts": conflicts, + "chamber_joints": chamber_joints, "negative_controls": negative_controls, + "negative_controls_expected": 1, "first_conflict": first_conflict, + "landing_content_band": landing_content_band, + "landing_content_band_passed": landing_content_band_passed, + "passed": conflicts == 0 and negative_controls == 1 + and landing_content_band_passed} diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux156/REPORT.md new file mode 100644 index 0000000..2b2fb80 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/REPORT.md @@ -0,0 +1,40 @@ +# L156 — native-floor carve reaches the grade stop rule + +| Landing | Before (L151) | After (L156) | +|---|---|---| +| passage/0-1/0 · upper | ![Before upper 0-1](captures/before-l151-0-1-0-upper-landing.jpg) | ![After upper 0-1](captures/after-l156-0-1-0-upper-landing.jpg) | +| passage/0-1/0 · lower | ![Before lower 0-1](captures/before-l151-0-1-0-lower-landing.jpg) | ![After lower 0-1](captures/after-l156-0-1-0-lower-landing.jpg) | +| passage/1-2/0 · upper | ![Before upper 1-2](captures/before-l151-1-2-0-upper-landing.jpg) | ![After upper 1-2](captures/after-l156-1-2-0-upper-landing.jpg) | +| passage/1-2/0 · lower | ![Before lower 1-2](captures/before-l151-1-2-0-lower-landing.jpg) | ![After lower 1-2](captures/after-l156-1-2-0-lower-landing.jpg) | +| passage/2-3/0 · upper | ![Before upper 2-3](captures/before-l151-2-3-0-upper-landing.jpg) | ![After upper 2-3](captures/after-l156-2-3-0-upper-landing.jpg) | +| passage/2-3/0 · lower | ![Before lower 2-3](captures/before-l151-2-3-0-lower-landing.jpg) | ![After lower 2-3](captures/after-l156-2-3-0-lower-landing.jpg) | + +## Result + +The headroom-only carve was implemented and captured, but L156 does not pass: fast tier still fails the upper Surface World landing's evaluated floor grade at 22.847° against 15°. The plan profile itself is 10.037°. The passage-interior support exemption and its open-floor negative control pass. The Floating Islands chamber-band failure is fixed: every chamber in both 16-seed sweeps is inside its host band, although other gates leave both sweeps at 0/16. The grade cause stops here after its three failed fits. + +## What changed + +- Bumped `WorldAssemblyEvaluatorVersion` from 239 to 240. The chamber carve now starts at the planned floor raised to the receiving native ground height when that surface is within the chamber's 4.5 m clear-height budget. Surface World samples its deterministic heightfield; a missing or higher, separate surface falls back to the planned floor. The chamber clears upward from that floor and retains its 0.75 m shell. Passage joining remains bounded to 15°. +- Added a passage-interior exemption to chamber support sampling. On the fast fixture it skipped 5,933 bore-overlap support samples; 76,671 passage-interior capsule samples had zero solid hits and the chamber audit reported zero unsupported samples. +- Added an open-floor negative control outside the bore. It found 3 unsupported samples and reported the expected failure (control status PASS). +- Fit each landing's full grade reach, shell, clear height, and Surface World rise inside its host content band. Seed 0's six chambers all pass; its tightest lower and upper band margins are 3.195 m and 3.642 m. The band gate passed all 16 seeds in both sweep types. + +## Measurements + +- **Fast tier:** failed after 64.31 s on the upper chamber. `PassagePlanGradeAudit` passed at 10.037°; the evaluated walk-floor chord at segment 10 was 22.847° over 0.479 m horizontal and 0.202 m vertical. The chamber audit had one grade failure. Passage interior was open (0 solid hits); the outside-bore open-floor negative control detected its unsupported floor. +- **Composed sweeps:** Tunnel Network 0/16 and Vertical Shafts 0/16 passed, with all 16 seeds completed for each at source stamp `4bf096c0…`. The exact landing-band and envelope gates passed across both sweeps. Remaining failures include passage summary/walkability, seams, interior-capsule and chamber-floor gates, and some route-to-chamber envelope conflicts. +- **Determinism:** Clang and GCC at W1/W8/W12 passed 6/6 with identical output hashes. Density `0179a7f6937dab67`, mesh `5f84fb3d3509f12e`, payload `8b857b09f6cb0ea4`, topology `1601efe43ef906b9`, and material `bd37729ce9c3d68d`. A seed-0 candidate baseline is recorded at `baselines/native-owner-fi-seed0.json`; it is not promoted as canonical while fast and composed gates fail. +- **Captures:** six matched eye-height views before (L151) and after (L156), same camera poses and layout identity; image quality passed 12/12. Before stamp `1ca3b84f…`; after stamp `4bf096c0…`. +- **Linux UE:** `VFHostEditor` built successfully, 27/27 actions in 69 s. No Unreal `Source/` files changed. Four start poses per condition settled LOD0–2 at median 2.421 s (`assembler-world`) and 2.457 s (planner). Five interleaved 30 m/s movement runs per condition: near-ready moving mean was 45.51% vs 39.04% (−14.21% planner); far holes were 3.15% vs 3.55% (+12.66%); max and p99 game-thread ticks were +4.13% and +6.64%. Each trace covered 46 moving samples over roughly 53–62 s. +- **L80:** no comparison result. Its quiet gate passed at load 0.74 and CPU idle 99.2%. The baseline warm-up passed in 1.72 s, but the L156 binary rejects the old owner snapshot's canonical identity. A regenerated v240 snapshot is accepted by L156, then the full pipeline exits 2: 176/21,103 compiled passage-centre samples are solid. The L137 binary rejects that v240 snapshot, so no shared serialized snapshot yields a valid cross-version comparison. `--force-generate` does not bypass the initial identity check. + +## Checks and limits + +- Ran: Clang and GCC headless builds; fast tier; both 16-seed sweeps; chamber support and open-floor negative control; content-band/envelope audits; six-way compiler/worker determinism; 12 capture-quality checks; Linux UE build, four start poses, and five 30 m/s runs per condition. +- Not run: L80 `assembler/world` timing at W8/W12 across all four layers. The host met its quiet gate, but the candidate fails its full-pipeline walkability check and the two revisions reject each other's canonical snapshots. The lone baseline warm-up is not treated as a timing comparison. +- The native-floor representation removes below-ground carving in the intended headroom case, but the effective chamber floor still violates the 15° join at a Surface World heightfield transition. No canonical baseline promotion is justified until that gate and the two composed sweeps pass. + +## Remaining risk + +The exact upper-chamber failure is at a native-surface transition, while the planned passage floor remains within its grade bound. The other sweep failures are also unresolved; the band fix and support-audit change do not make those gates pass. The movement result is slower for near-ready coverage than `assembler/world` in this five-run window. diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Captures.py b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Captures.py new file mode 100644 index 0000000..16a62d6 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Captures.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Capture matched eye-height views from both ends of every owner tunnel.""" +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/l156/captures" +LAYOUT = OUT / "after-resolved-layout.json" +ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux156/captures" +LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"] +VERSIONS = { + "before-l151": { + "headless": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l151-clang", + "renderer": ROOT / "Standalone/bin/linux/VoxelForgeOffscreenCapture-l151-clang", + "stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + }, + "after-l156": { + "headless": ROOT / "Saved/Linux/l156/bin/VoxelForgeCoreHeadless-l156-clang", + "renderer": ROOT / "Saved/Linux/l156/bin/VoxelForgeOffscreenCapture-l156-clang", + "stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + }, +} +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_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 chamber_views(layout: dict) -> list[dict]: + result = [] + for passage in layout["passages"]: + controls = passage["control_points"] + for side, endpoint, neighbor in ( + ("upper", controls[0], controls[1]), + ("lower", controls[-1], controls[-2])): + chamber = passage[f"{side}_landing_chamber"] + center = point_q(layout, chamber["floor_center_q"]) + a = point_q(layout, neighbor["position_q"]) + b = point_q(layout, endpoint["position_q"]) + dx, dy = a[0] - b[0], a[1] - b[1] + length = math.hypot(dx, dy) + if length <= 1.0e-9: + raise ValueError(f"degenerate {side} tunnel tangent: {passage['stable_id']}") + dx, dy = dx / length, dy / length + eye_z = center[2] + 1.45 + distance = 9.0 + target = (center[0] + dx * distance, center[1] + dy * distance, + eye_z + (chamber["floor_gradient_q16"][0] / 65536.0 * dx + + chamber["floor_gradient_q16"][1] / 65536.0 * dy) + * distance) + camera = (center[0], center[1], eye_z) + slug = passage["stable_id"].replace("passage/", "").replace("/", "-") + result.append({"name": f"{slug}-{side}-landing", "passage": passage["stable_id"], + "side": side, "camera": camera, "target": target, + "focus": (center[0], center[1], eye_z, 18.0)}) + return result + + +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_layout(env: dict[str, str]) -> dict: + config = VERSIONS["after-l156"] + 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", "--seed=0", "--recipe=surface-world", "--resolved-layout-only", + f"--resolved-layout-output={LAYOUT}"] + stamped_run("after-l156-resolve-layout", list(map(str, command)), + OUT / "after-l156-resolved-layout.log", config["stamp"], env) + layout = json.loads(LAYOUT.read_text(encoding="utf-8")) + if len(layout.get("passages", [])) != 3: + raise RuntimeError("owner layout did not resolve the expected three tunnels") + 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_layout(env) + views = chamber_views(layout) + quality_tool = ROOT / "Standalone/Tools/verify/capture_quality.py" + manifest = [] + failures = [] + for version, config in VERSIONS.items(): + for view in views: + 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"{view['name']}.vfmesh" + capture_layout = mesh_dir / f"{view['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", "--seed=0", "--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}-{view['name']}-export", list(map(str, export)), + OUT / f"{version}-{view['name']}-export.log", config["stamp"], env) + if not mesh.is_file() or mesh.stat().st_size == 0: + raise RuntimeError(f"{version}-{view['name']}: empty mesh export") + png = OUT / f"{version}-{view['name']}.png" + jpg = ARTIFACTS / f"{version}-{view['name']}.jpg" + render = [config["renderer"], mesh, png, "--size=1280x720", + "--fov-deg=78.0", 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}-{view['name']}-render", list(map(str, render)), + OUT / f"{version}-{view['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}-{view['name']}") + quality_line = (check.stdout.strip().splitlines()[-1] + if check.stdout.strip() else f"quality exit={check.returncode}") + manifest.append({"version": version, "view": view["name"], + "passage": view["passage"], "side": view["side"], + "file": str(jpg.relative_to(ROOT)), "camera_m": camera, + "target_m": target, "yaw_deg": yaw, "pitch_deg": pitch, + "source_stamp": config["stamp"], + "pose_layout_identity": layout.get("stable_identity"), + "quality_status": "PASS" if quality_ok else "FAIL", + "quality": quality_line}) + print(f"[L156Capture] version={version} view={view['name']} " + f"quality={'PASS' if quality_ok else 'FAIL'} " + f"file={jpg.relative_to(ROOT)}", flush=True) + (ARTIFACTS / "manifest.json").write_text( + json.dumps(manifest, indent=2) + "\n", encoding="utf-8") + with (ARTIFACTS / "INDEX.md").open("w", encoding="utf-8") as index: + index.write("# L156 matched owner tunnel landing captures\n\n" + "Seed 0 camera poses resolved from the L156 owner layout and reused for both " + "versions. Each view is at eye height at one of the three tunnel landing " + "chambers. Before uses the L151 head build; after uses L156 source stamp " + f"`{VERSIONS['after-l156']['stamp']}`.\n\n") + for view in views: + index.write(f"## {view['passage']} / {view['side']} landing\n\n" + f"- Before: [image](before-l151-{view['name']}.jpg)\n" + f"- After: [image](after-l156-{view['name']}.jpg)\n\n") + index.write(f"Capture quality failures: {len(failures)}/{len(manifest)}.\n") + print(f"[L156CaptureSummary] chamber_views={len(views)} " + f"images={len(manifest)} 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"[L156CaptureFailure] {error}", file=sys.stderr, flush=True) + raise SystemExit(1) diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Determinism.py b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Determinism.py new file mode 100644 index 0000000..b87dfd5 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Determinism.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Compare the composed native Floating Islands owner at Clang/GCC W1/W8/W12.""" +from __future__ import annotations + +import json +import re +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l156/determinism" +BASELINE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux156/baselines/native-owner-fi-seed0.json" +DATA = ROOT / "Standalone/data/owner_world.json" +STAMP = "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0" +BINARIES = { + "clang": ROOT / "Saved/Linux/l156/bin/VoxelForgeCoreHeadless-l156-clang", + "gcc": ROOT / "Saved/Linux/l156/bin/VoxelForgeCoreHeadless-l156-gcc", +} +CAMERA = "25.836914,3.199219,-737.000000,0,0" +STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M) +HASH_RE = re.compile( + r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$", + re.M) +NATIVE_FI = re.compile( + r"^\[DensityDispatch\] strate=1 generator=FloatingIslands " + r"recipe=FloatingIslands\.ClusterField version=\d+ " + r"bound_path=FloatingIslands-native adapter=FFloatingIslandFieldAdapter$", re.M) +FI_RECIPE_HASH = re.compile( + r"^\[FloatingIslandWorldAssembly\].*recipe_hash=([0-9a-f]+) status=PASS$", re.M) +FI_CLEARANCE = re.compile(r"^\[FloatingIslandPassageClearance\].*status=PASS$", re.M) +FI_ROUTE_AUDIT = re.compile(r"^\[FloatingIslandRouteAudit\].*status=PASS$", re.M) + + +def main() -> int: + if not DATA.is_file() or any(not path.is_file() for path in BINARIES.values()): + raise SystemExit("missing L156 Clang/GCC binary or composed owner fixture") + OUT.mkdir(parents=True, exist_ok=True) + rows: list[dict[str, object]] = [] + reference_hashes: list[str] | None = None + reference_identity: str | None = None + reference_recipe_hash: str | None = None + for compiler, binary in BINARIES.items(): + for workers in (1, 8, 12): + name = f"native-owner-fi-{compiler}-w{workers}" + log, layout = OUT / f"{name}.log", OUT / f"{name}.json" + 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=0", + f"--camera={CAMERA}", "--stream-hash", "--force-generate", "--seed=0", + "--recipe=surface-world", "--native-owner-fi", "--render-distance-chunks=0", + "--density-dispatch-report", f"--resolved-layout-output={layout}"] + 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") + stamp = STAMP_RE.search(content) + hashes = HASH_RE.findall(content) + current_layout = json.loads(layout.read_text(encoding="utf-8")) if layout.is_file() else {} + identity = current_layout.get("stable_identity") + native_ok = bool(NATIVE_FI.search(content)) + recipe_hash = FI_RECIPE_HASH.search(content) + clearance = FI_CLEARANCE.search(content) + route_audit = FI_ROUTE_AUDIT.search(content) + local_pass = (proc.returncode == 0 and stamp is not None + and stamp.group(1) == STAMP and len(hashes) >= 4 and native_ok + and recipe_hash is not None and route_audit is not None and bool(identity)) + if reference_hashes is None and local_pass: + reference_hashes, reference_identity = hashes, identity + reference_recipe_hash = recipe_hash.group(1) if recipe_hash else None + same = (hashes == reference_hashes and identity == reference_identity + and (recipe_hash.group(1) if recipe_hash else None) + == reference_recipe_hash) + passed = bool(local_pass and same) + row = {"compiler": compiler, "workers": workers, "exit": proc.returncode, + "source_stamp": stamp.group(1) if stamp else None, "hash_lines": hashes, + "layout_identity": identity, "native_fi_dispatch": native_ok, + "floating_island_recipe_hash": recipe_hash.group(1) if recipe_hash else None, + "passage_clearance_line": clearance.group(0) if clearance else None, + "route_audit_line": route_audit.group(0) if route_audit else None, + "passed": passed, "elapsed_s": round(time.monotonic() - started, 3), + "log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))} + rows.append(row) + print("[L156DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True) + (OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n") + if not passed: + summary = {"status": "FAIL", "source_stamp": STAMP, "runs": rows} + (OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n") + raise SystemExit(f"native owner determinism mismatch; see {log}") + + summary = {"status": "PASS", "source_stamp": STAMP, + "world_assembly_evaluator_version": 240, + "layout_identity": reference_identity, + "floating_island_recipe_hash": reference_recipe_hash, + "baseline_scope": "candidate only; fast tier remains failed", + "runs": rows} + OUT.joinpath("summary.json").write_text(json.dumps(summary, indent=2) + "\n") + BASELINE.parent.mkdir(parents=True, exist_ok=True) + BASELINE.write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8") + print(f"[L156DeterminismSummary] runs={len(rows)} source_stamp={STAMP} status=PASS " + "baseline=candidate-only", flush=True) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/RunL156SeedSweep.py b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156SeedSweep.py new file mode 100644 index 0000000..32ed92e --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156SeedSweep.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +"""Run the L156 passage and envelope gates across the 16 owner seeds.""" +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import time +from pathlib import Path + +from PassageEnvelopeAudit import audit_layout + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l156/seeds" +BIN = ROOT / "Saved/Linux/l156/bin/VoxelForgeCoreHeadless-l156-clang" +DATA = ROOT / "Standalone/data/owner_world.json" +STAMP = "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0" +STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M) +DISPATCH = { + "tunnel-network": re.compile( + r"^\[DensityDispatch\] strate=2 generator=TunnelNetwork " + r"recipe=Core\.Caves\.PrimitiveField version=1 " + r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M), + "vertical-shafts": re.compile( + r"^\[DensityDispatch\] strate=3 generator=VerticalShafts " + r"recipe=Core\.Caves\.PrimitiveField version=1 " + r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M), +} +ROUTES = { + "tunnel-network": re.compile( + r"^\[ResolvedStrateRouteAudit\] strate=2 .*unsupported=0 " + r"grade_failures=0 .*status=PASS$", re.M), + "vertical-shafts": re.compile( + r"^\[ResolvedStrateRouteAudit\] strate=3 .*unsupported=0 " + r"grade_failures=0 .*status=PASS$", re.M), +} +CONTROLS = { + "tunnel-network": re.compile( + r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M), + "vertical-shafts": re.compile( + r"^\[VerticalShaftsBlockedRouteControl\].*expected=FAIL status=PASS$", re.M), +} +REQUIRED = { + "passage_summary": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M), + "seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M), + "shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M), + "naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M), + "mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M), + "ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M), + "interior_capsules": re.compile( + r"^\[PassageInteriorCapsuleAuditSummary\].*status=PASS$", re.M), + "layout_controls": re.compile( + r"^\[ResolvedLayoutValidatorControls\] rejected=10/10 status=PASS$", re.M), + "floor_profile_controls": re.compile( + r"^\[FloorProfileBlendNegativeControls\].*detected=true/true status=PASS$", re.M), + "naturalness_control": re.compile( + r"^\[PassageNaturalnessNegativeControl\].*detected=true$", re.M), + "mouth_control": re.compile( + r"^\[PassageMouthProfileNegativeControl\].*detected=true$", re.M), +} + + +def passage_rows(content: str) -> list[str]: + return re.findall(r"^\[ResolvedPassageAudit\].*$", content, re.M) + + +def grade_audit(content: str) -> tuple[bool, list[str]]: + rows = re.findall(r"^\[PassagePlanGradeAudit\].*$", content, re.M) + ids = set() + passed = bool(rows) + for row in rows: + identifier = re.search(r"\bid=([^ ]+)", row) + maximum = re.search(r"\bmax_grade_deg=([0-9.]+)", row) + limit = re.search(r"\blimit_deg=([0-9.]+)", row) + violations = re.search(r"\bviolations=(\d+)", row) + status = "status=PASS" in row + if identifier: + ids.add(identifier.group(1)) + passed &= bool(identifier and maximum and limit and violations and status + and float(maximum.group(1)) <= float(limit.group(1)) + and float(limit.group(1)) == 15.0 + and int(violations.group(1)) == 0) + return passed and len(ids) >= 3, rows + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--kind", choices=tuple(DISPATCH), required=True) + parser.add_argument("--start-seed", type=int, default=0) + parser.add_argument("--end-seed", type=int, default=15) + args = parser.parse_args() + if not 0 <= args.start_seed <= args.end_seed <= 15: + raise SystemExit("seed range must be within 0..15 and start <= end") + if not BIN.is_file() or not DATA.is_file(): + raise SystemExit("missing L156 Clang binary or composed owner fixture") + mode_out = OUT / args.kind + mode_out.mkdir(parents=True, exist_ok=True) + partial = mode_out / "runs-partial.json" + results = (json.loads(partial.read_text(encoding="utf-8")) + if partial.is_file() and args.start_seed > 0 else []) + completed = {row["seed"] for row in results} + for seed in range(args.start_seed, args.end_seed + 1): + if seed in completed: + continue + log = mode_out / f"seed{seed}.log" + layout = mode_out / f"seed{seed}.json" + command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3", + "--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}", + "--recipe=surface-world", "--density-dispatch-report", + "--audit-resolved-layout-passages", "--audit-passage-tile-seams", + f"--resolved-layout-output={layout}"] + 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") + stamp = STAMP_RE.search(content) + checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()} + checks["process_exit_zero"] = proc.returncode == 0 + checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP + checks["dispatch"] = bool(DISPATCH[args.kind].search(content)) + checks["route"] = bool(ROUTES[args.kind].search(content)) + checks["blocked_route_control"] = bool(CONTROLS[args.kind].search(content)) + + passages = passage_rows(content) + checks["three_passages_walkable_and_open"] = ( + len(passages) == 3 and all("walkability=PASS" in row + and "interior_air=PASS" in row + and re.search(r"\bblocked=0\b", row) + and re.search(r"\bunsupported=0\b", row) + and re.search(r"\bgrade_failures=0\b", row) + for row in passages)) + checks["three_interior_negative_controls"] = len(re.findall( + r"^\[ResolvedPassageInteriorNegativeControl\].*detected=true status=PASS$", + content, re.M)) == 3 + checks["three_ripple_gap_controls"] = len(re.findall( + r"^\[PassageRippleSamplingControl\].*status=PASS$", content, re.M)) == 3 + checks["three_blocked_passage_controls"] = len(re.findall( + r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3 + chamber_rows = re.findall(r"^\[PassageLandingChamberAudit\].*$", content, re.M) + checks["six_chamber_floors"] = (len(chamber_rows) == 6 and all( + "status=PASS" in row and re.search(r"\bsolid_samples=0\b", row) + and re.search(r"\bgrade_failures=0\b", row) for row in chamber_rows)) + checks["plan_grade"] = grade_audit(content)[0] + chamber_controls = re.search( + r"^\[PassageLandingChamberNegativeControlSummary\] " + r"detected=(\d+)/6 .*status=OBSERVED$", content, re.M) + checks["chamber_negative_control"] = bool( + chamber_controls and int(chamber_controls.group(1)) > 0) + + checks["three_ripple_profiles"] = len(re.findall( + r"^\[PassageRippleAudit\].*status=PASS$", content, re.M)) >= 3 + checks["passage_count"] = len(re.findall( + r"^\[ResolvedPassageAudit\].*walkability=PASS", content, re.M)) == 3 + checks["layout_output"] = layout.is_file() + envelope = audit_layout(json.loads(layout.read_text(encoding="utf-8"))) \ + if layout.is_file() else None + checks["envelope"] = bool(envelope and envelope["passed"] + and envelope["outside_chamber_conflicts"] == 0) + checks["envelope_negative_control"] = bool(envelope + and envelope["negative_controls"] == envelope["negative_controls_expected"] == 1) + checks["landing_content_band"] = bool(envelope + and envelope["landing_content_band_passed"] + and len(envelope["landing_content_band"]) == 6) + passed = all(checks.values()) + failed = [name for name, ok in checks.items() if not ok] + row = {"kind": args.kind, "seed": seed, + "verdict": "PASS" if passed else "FAIL", + "reason": ", ".join(failed) if failed else "all requested gates pass", + "exit": proc.returncode, "checks": checks, + "plan_grade_rows": grade_audit(content)[1], + "envelope_audit": envelope, + "elapsed_s": round(time.monotonic() - started, 3), + "log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))} + results.append(row) + print("[L156SeedSweep] " + json.dumps(row, sort_keys=True), flush=True) + partial.write_text(json.dumps(results, indent=2) + "\n", encoding="utf-8") + + results.sort(key=lambda row: row["seed"]) + passed_count = sum(row["verdict"] == "PASS" for row in results) + completed_count = len(results) + missing_seeds = sorted(set(range(16)) - {row["seed"] for row in results}) + summary = {"kind": args.kind, "source_stamp": STAMP, + "seeds": [row["seed"] for row in results], + "completed": completed_count, "passed": passed_count, + "failed": completed_count - passed_count, "missing_seeds": missing_seeds, + "results": results} + (mode_out / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", + encoding="utf-8") + status = "PASS" if completed_count == 16 and passed_count == 16 else "FAIL" + print(f"[L156SeedSweepSummary] kind={args.kind} passed={passed_count}/{completed_count} " + f"failed={completed_count-passed_count} missing={len(missing_seeds)} status={status}", + flush=True) + return 0 if status == "PASS" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Timing.py b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Timing.py new file mode 100644 index 0000000..bcaa666 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Timing.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +"""L80 comparison of L156 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/l156/timing" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARIES = { + "assembler-world": ( + ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a", + "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"), + "planner-head": ( + ROOT / "Saved/Linux/l156/bin/VoxelForgeCoreHeadless-l156-clang", + "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0"), +} +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("[L156TimingRun] " + 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 L156 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"[L156TimingGate] 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") + failed_cells = [] + for entry in summary: + for measure in ("lod02_ready_s", "whole_view_ready_s"): + delta = entry[f"planner_head_vs_assembler_world_{measure}_delta_pct"] + if delta > 1.0: + failed_cells.append({"strate": entry["strate"], + "workers": entry["workers"], + "measure": measure, "delta_pct": delta}) + report = json.loads((OUT / "summary.json").read_text(encoding="utf-8")) + report["performance_floor_pct"] = 1.0 + report["performance_floor_rule"] = ( + "pass when no L156 median is more than 1% slower than assembler/world") + report["performance_floor_failures"] = failed_cells + report["status"] = "PASS" if not failed_cells else "FAIL" + (OUT / "summary.json").write_text(json.dumps(report, indent=2) + "\n", + encoding="utf-8") + print(f"[L156TimingSummary] cells={len(summary)} failing_metrics={len(failed_cells)} " + f"status={'PASS' if not failed_cells else 'FAIL'}", flush=True) + return 0 if not failed_cells else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Unreal.py b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Unreal.py new file mode 100644 index 0000000..3befe87 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/RunL156Unreal.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/l156/ue" / label + plugin_saved = host / "Plugins/VoxelForge/Saved/Linux/l156" + 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("[L156UEStart] " + 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("[L156UEMovement] " + 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/l156/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("[L156UEMovementRepeat] " + 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("[L156UESummary] " + 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"[L156UEFailure] {error}", file=sys.stderr, flush=True) + raise SystemExit(1) diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/__pycache__/PassageEnvelopeAudit.cpython-313.pyc b/Standalone/Research/WorldAssemblerM3/Linux156/__pycache__/PassageEnvelopeAudit.cpython-313.pyc new file mode 100644 index 0000000..727d1b5 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/__pycache__/PassageEnvelopeAudit.cpython-313.pyc differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/__pycache__/RunL156SeedSweep.cpython-313.pyc b/Standalone/Research/WorldAssemblerM3/Linux156/__pycache__/RunL156SeedSweep.cpython-313.pyc new file mode 100644 index 0000000..ff2ecc4 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/__pycache__/RunL156SeedSweep.cpython-313.pyc differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/baselines/native-owner-fi-seed0.json b/Standalone/Research/WorldAssemblerM3/Linux156/baselines/native-owner-fi-seed0.json new file mode 100644 index 0000000..adf66b1 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/baselines/native-owner-fi-seed0.json @@ -0,0 +1,190 @@ +{ + "status": "PASS", + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "world_assembly_evaluator_version": 240, + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "floating_island_recipe_hash": "789d86eed28fbfdd", + "baseline_scope": "candidate only; fast tier remains failed", + "runs": [ + { + "compiler": "clang", + "workers": 1, + "exit": 0, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "hash_lines": [ + "[PipelineFullDetailHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineWholeViewHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineMaterialHash] tiles=512 material=0xbd37729ce9c3d68d", + "[PipelineLODHash] lod=0 tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineExactQueryHash] probes=9 density=0x0536f1ff213604da unclamped=0x0536f1ff213604da detail=0xefcba20c0c70c415 collision=0xe604813a2490280c path=direct-Core-queries" + ], + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "native_fi_dispatch": true, + "floating_island_recipe_hash": "789d86eed28fbfdd", + "passage_clearance_line": null, + "route_audit_line": "[FloatingIslandRouteAudit] points=29 centers=286 capsule_samples=77792 blocked=0 support_samples=1554 unsupported=0 grade_failures=0 status=PASS", + "passed": true, + "elapsed_s": 34.327, + "log": "Saved/Linux/l156/determinism/native-owner-fi-clang-w1.log", + "layout": "Saved/Linux/l156/determinism/native-owner-fi-clang-w1.json" + }, + { + "compiler": "clang", + "workers": 8, + "exit": 0, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "hash_lines": [ + "[PipelineFullDetailHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineWholeViewHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineMaterialHash] tiles=512 material=0xbd37729ce9c3d68d", + "[PipelineLODHash] lod=0 tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineExactQueryHash] probes=9 density=0x0536f1ff213604da unclamped=0x0536f1ff213604da detail=0xefcba20c0c70c415 collision=0xe604813a2490280c path=direct-Core-queries" + ], + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "native_fi_dispatch": true, + "floating_island_recipe_hash": "789d86eed28fbfdd", + "passage_clearance_line": null, + "route_audit_line": "[FloatingIslandRouteAudit] points=29 centers=286 capsule_samples=77792 blocked=0 support_samples=1554 unsupported=0 grade_failures=0 status=PASS", + "passed": true, + "elapsed_s": 20.415, + "log": "Saved/Linux/l156/determinism/native-owner-fi-clang-w8.log", + "layout": "Saved/Linux/l156/determinism/native-owner-fi-clang-w8.json" + }, + { + "compiler": "clang", + "workers": 12, + "exit": 0, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "hash_lines": [ + "[PipelineFullDetailHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineWholeViewHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineMaterialHash] tiles=512 material=0xbd37729ce9c3d68d", + "[PipelineLODHash] lod=0 tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineExactQueryHash] probes=9 density=0x0536f1ff213604da unclamped=0x0536f1ff213604da detail=0xefcba20c0c70c415 collision=0xe604813a2490280c path=direct-Core-queries" + ], + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "native_fi_dispatch": true, + "floating_island_recipe_hash": "789d86eed28fbfdd", + "passage_clearance_line": null, + "route_audit_line": "[FloatingIslandRouteAudit] points=29 centers=286 capsule_samples=77792 blocked=0 support_samples=1554 unsupported=0 grade_failures=0 status=PASS", + "passed": true, + "elapsed_s": 20.182, + "log": "Saved/Linux/l156/determinism/native-owner-fi-clang-w12.log", + "layout": "Saved/Linux/l156/determinism/native-owner-fi-clang-w12.json" + }, + { + "compiler": "gcc", + "workers": 1, + "exit": 0, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "hash_lines": [ + "[PipelineFullDetailHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineWholeViewHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineMaterialHash] tiles=512 material=0xbd37729ce9c3d68d", + "[PipelineLODHash] lod=0 tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineExactQueryHash] probes=9 density=0x0536f1ff213604da unclamped=0x0536f1ff213604da detail=0xefcba20c0c70c415 collision=0xe604813a2490280c path=direct-Core-queries" + ], + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "native_fi_dispatch": true, + "floating_island_recipe_hash": "789d86eed28fbfdd", + "passage_clearance_line": null, + "route_audit_line": "[FloatingIslandRouteAudit] points=29 centers=286 capsule_samples=77792 blocked=0 support_samples=1554 unsupported=0 grade_failures=0 status=PASS", + "passed": true, + "elapsed_s": 33.269, + "log": "Saved/Linux/l156/determinism/native-owner-fi-gcc-w1.log", + "layout": "Saved/Linux/l156/determinism/native-owner-fi-gcc-w1.json" + }, + { + "compiler": "gcc", + "workers": 8, + "exit": 0, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "hash_lines": [ + "[PipelineFullDetailHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineWholeViewHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineMaterialHash] tiles=512 material=0xbd37729ce9c3d68d", + "[PipelineLODHash] lod=0 tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineExactQueryHash] probes=9 density=0x0536f1ff213604da unclamped=0x0536f1ff213604da detail=0xefcba20c0c70c415 collision=0xe604813a2490280c path=direct-Core-queries" + ], + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "native_fi_dispatch": true, + "floating_island_recipe_hash": "789d86eed28fbfdd", + "passage_clearance_line": null, + "route_audit_line": "[FloatingIslandRouteAudit] points=29 centers=286 capsule_samples=77792 blocked=0 support_samples=1554 unsupported=0 grade_failures=0 status=PASS", + "passed": true, + "elapsed_s": 18.02, + "log": "Saved/Linux/l156/determinism/native-owner-fi-gcc-w8.log", + "layout": "Saved/Linux/l156/determinism/native-owner-fi-gcc-w8.json" + }, + { + "compiler": "gcc", + "workers": 12, + "exit": 0, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "hash_lines": [ + "[PipelineFullDetailHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineWholeViewHash] tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineMaterialHash] tiles=512 material=0xbd37729ce9c3d68d", + "[PipelineLODHash] lod=0 tiles=512 density=0x0179a7f6937dab67 mesh=0x5f84fb3d3509f12e payload=0x8b857b09f6cb0ea4 topology=0x1601efe43ef906b9", + "[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325", + "[PipelineExactQueryHash] probes=9 density=0x0536f1ff213604da unclamped=0x0536f1ff213604da detail=0xefcba20c0c70c415 collision=0xe604813a2490280c path=direct-Core-queries" + ], + "layout_identity": "resolved-layout-v3-fnv64:d4651758d452bca2", + "native_fi_dispatch": true, + "floating_island_recipe_hash": "789d86eed28fbfdd", + "passage_clearance_line": null, + "route_audit_line": "[FloatingIslandRouteAudit] points=29 centers=286 capsule_samples=77792 blocked=0 support_samples=1554 unsupported=0 grade_failures=0 status=PASS", + "passed": true, + "elapsed_s": 17.859, + "log": "Saved/Linux/l156/determinism/native-owner-fi-gcc-w12.log", + "layout": "Saved/Linux/l156/determinism/native-owner-fi-gcc-w12.json" + } + ] +} diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/INDEX.md b/Standalone/Research/WorldAssemblerM3/Linux156/captures/INDEX.md new file mode 100644 index 0000000..c189399 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/captures/INDEX.md @@ -0,0 +1,35 @@ +# L156 matched owner tunnel landing captures + +Seed 0 camera poses resolved from the L156 owner layout and reused for both versions. Each view is at eye height at one of the three tunnel landing chambers. Before uses the L151 head build; after uses L156 source stamp `4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0`. + +## passage/0-1/0 / upper landing + +- Before: [image](before-l151-0-1-0-upper-landing.jpg) +- After: [image](after-l156-0-1-0-upper-landing.jpg) + +## passage/0-1/0 / lower landing + +- Before: [image](before-l151-0-1-0-lower-landing.jpg) +- After: [image](after-l156-0-1-0-lower-landing.jpg) + +## passage/1-2/0 / upper landing + +- Before: [image](before-l151-1-2-0-upper-landing.jpg) +- After: [image](after-l156-1-2-0-upper-landing.jpg) + +## passage/1-2/0 / lower landing + +- Before: [image](before-l151-1-2-0-lower-landing.jpg) +- After: [image](after-l156-1-2-0-lower-landing.jpg) + +## passage/2-3/0 / upper landing + +- Before: [image](before-l151-2-3-0-upper-landing.jpg) +- After: [image](after-l156-2-3-0-upper-landing.jpg) + +## passage/2-3/0 / lower landing + +- Before: [image](before-l151-2-3-0-lower-landing.jpg) +- After: [image](after-l156-2-3-0-lower-landing.jpg) + +Capture quality failures: 0/12. diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-lower-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-lower-landing.jpg new file mode 100644 index 0000000..136d057 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-lower-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-upper-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-upper-landing.jpg new file mode 100644 index 0000000..62fde03 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-upper-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-lower-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-lower-landing.jpg new file mode 100644 index 0000000..20a06ab Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-lower-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-upper-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-upper-landing.jpg new file mode 100644 index 0000000..b737b34 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-upper-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-lower-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-lower-landing.jpg new file mode 100644 index 0000000..a67a368 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-lower-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-upper-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-upper-landing.jpg new file mode 100644 index 0000000..3a29930 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-upper-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-lower-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-lower-landing.jpg new file mode 100644 index 0000000..c4013c0 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-lower-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-upper-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-upper-landing.jpg new file mode 100644 index 0000000..d648161 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-upper-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-lower-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-lower-landing.jpg new file mode 100644 index 0000000..aff38d6 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-lower-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-upper-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-upper-landing.jpg new file mode 100644 index 0000000..5763300 Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-upper-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-lower-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-lower-landing.jpg new file mode 100644 index 0000000..7765a7f Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-lower-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-upper-landing.jpg b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-upper-landing.jpg new file mode 100644 index 0000000..86ecb2b Binary files /dev/null and b/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-upper-landing.jpg differ diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/captures/manifest.json b/Standalone/Research/WorldAssemblerM3/Linux156/captures/manifest.json new file mode 100644 index 0000000..d8cc0ad --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/captures/manifest.json @@ -0,0 +1,278 @@ +[ + { + "version": "before-l151", + "view": "0-1-0-upper-landing", + "passage": "passage/0-1/0", + "side": "upper", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-upper-landing.jpg", + "camera_m": [ + 37.0, + -5.25, + -372.3810546875 + ], + "target_m": [ + 28.40474992442715, + -7.918646874047773, + -371.83751160914875 + ], + "yaw_deg": -107.24848481126237, + "pitch_deg": 3.4561048595792685, + "source_stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-upper-landing.jpg pixels=921600 mean_luma=118.644 stddev_luma=15.499 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "before-l151", + "view": "0-1-0-lower-landing", + "passage": "passage/0-1/0", + "side": "lower", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-lower-landing.jpg", + "camera_m": [ + 25.8369140625, + 3.19921875, + -736.6798828125 + ], + "target_m": [ + 17.97380590951016, + -1.1793118453464206, + -734.970169262755 + ], + "yaw_deg": -119.11101808688004, + "pitch_deg": 10.756207017349817, + "source_stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-0-1-0-lower-landing.jpg pixels=921600 mean_luma=111.426 stddev_luma=12.411 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "before-l151", + "view": "1-2-0-upper-landing", + "passage": "passage/1-2/0", + "side": "upper", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-upper-landing.jpg", + "camera_m": [ + 19.166015625, + -14.2001953125, + -731.8810546875 + ], + "target_m": [ + 10.35825176774404, + -16.050410390488395, + -733.216132282697 + ], + "yaw_deg": -101.86342177874246, + "pitch_deg": -8.437834550031965, + "source_stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-upper-landing.jpg pixels=921600 mean_luma=117.134 stddev_luma=15.664 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "before-l151", + "view": "1-2-0-lower-landing", + "passage": "passage/1-2/0", + "side": "lower", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-lower-landing.jpg", + "camera_m": [ + 19.166015625, + -14.2001953125, + -753.05390625 + ], + "target_m": [ + 10.48848361510052, + -16.587754362485967, + -751.7193175939389 + ], + "yaw_deg": -105.38384894032598, + "pitch_deg": 8.434788863627224, + "source_stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-1-2-0-lower-landing.jpg pixels=921600 mean_luma=119.710 stddev_luma=14.975 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "before-l151", + "view": "2-3-0-upper-landing", + "passage": "passage/2-3/0", + "side": "upper", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-upper-landing.jpg", + "camera_m": [ + -20.0625, + -5.068359375, + -812.075390625 + ], + "target_m": [ + -13.604954164712868, + -11.337338700629157, + -813.6662645746064 + ], + "yaw_deg": 134.15112141809087, + "pitch_deg": -10.024269857937275, + "source_stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-upper-landing.jpg pixels=921600 mean_luma=118.198 stddev_luma=15.882 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "before-l151", + "view": "2-3-0-lower-landing", + "passage": "passage/2-3/0", + "side": "lower", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-lower-landing.jpg", + "camera_m": [ + -20.0625, + -5.068359375, + -900.0333984375 + ], + "target_m": [ + -14.865342978484083, + -12.416118141706198, + -898.4427583280197 + ], + "yaw_deg": 144.72777711381306, + "pitch_deg": 10.022826283920647, + "source_stamp": "1ca3b84f6d35f7f355ec0f72dabd3b87a455ea02e5789fea611916962e5db8e5", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/before-l151-2-3-0-lower-landing.jpg pixels=921600 mean_luma=121.734 stddev_luma=16.193 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "after-l156", + "view": "0-1-0-upper-landing", + "passage": "passage/0-1/0", + "side": "upper", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-upper-landing.jpg", + "camera_m": [ + 37.0, + -5.25, + -372.3810546875 + ], + "target_m": [ + 28.40474992442715, + -7.918646874047773, + -371.83751160914875 + ], + "yaw_deg": -107.24848481126237, + "pitch_deg": 3.4561048595792685, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-upper-landing.jpg pixels=921600 mean_luma=117.423 stddev_luma=13.370 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "after-l156", + "view": "0-1-0-lower-landing", + "passage": "passage/0-1/0", + "side": "lower", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-lower-landing.jpg", + "camera_m": [ + 25.8369140625, + 3.19921875, + -736.6798828125 + ], + "target_m": [ + 17.97380590951016, + -1.1793118453464206, + -734.970169262755 + ], + "yaw_deg": -119.11101808688004, + "pitch_deg": 10.756207017349817, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-0-1-0-lower-landing.jpg pixels=921600 mean_luma=111.621 stddev_luma=12.323 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "after-l156", + "view": "1-2-0-upper-landing", + "passage": "passage/1-2/0", + "side": "upper", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-upper-landing.jpg", + "camera_m": [ + 19.166015625, + -14.2001953125, + -731.8810546875 + ], + "target_m": [ + 10.35825176774404, + -16.050410390488395, + -733.216132282697 + ], + "yaw_deg": -101.86342177874246, + "pitch_deg": -8.437834550031965, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-upper-landing.jpg pixels=921600 mean_luma=117.135 stddev_luma=15.664 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "after-l156", + "view": "1-2-0-lower-landing", + "passage": "passage/1-2/0", + "side": "lower", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-lower-landing.jpg", + "camera_m": [ + 19.166015625, + -14.2001953125, + -753.05390625 + ], + "target_m": [ + 10.48848361510052, + -16.587754362485967, + -751.7193175939389 + ], + "yaw_deg": -105.38384894032598, + "pitch_deg": 8.434788863627224, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-1-2-0-lower-landing.jpg pixels=921600 mean_luma=119.962 stddev_luma=14.895 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "after-l156", + "view": "2-3-0-upper-landing", + "passage": "passage/2-3/0", + "side": "upper", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-upper-landing.jpg", + "camera_m": [ + -20.0625, + -5.068359375, + -812.075390625 + ], + "target_m": [ + -13.604954164712868, + -11.337338700629157, + -813.6662645746064 + ], + "yaw_deg": 134.15112141809087, + "pitch_deg": -10.024269857937275, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-upper-landing.jpg pixels=921600 mean_luma=118.197 stddev_luma=15.883 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + }, + { + "version": "after-l156", + "view": "2-3-0-lower-landing", + "passage": "passage/2-3/0", + "side": "lower", + "file": "Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-lower-landing.jpg", + "camera_m": [ + -20.0625, + -5.068359375, + -900.0333984375 + ], + "target_m": [ + -14.865342978484083, + -12.416118141706198, + -898.4427583280197 + ], + "yaw_deg": 144.72777711381306, + "pitch_deg": 10.022826283920647, + "source_stamp": "4bf096c0a06f72eb765ff8785d55c00ded946ed1bd1f39fa1914b3658ceb61f0", + "pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170", + "quality_status": "PASS", + "quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux156/captures/after-l156-2-3-0-lower-landing.jpg pixels=921600 mean_luma=121.801 stddev_luma=16.169 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS" + } +] diff --git a/Standalone/Research/WorldAssemblerM3/Linux156/l156-headroom-landing.patch b/Standalone/Research/WorldAssemblerM3/Linux156/l156-headroom-landing.patch new file mode 100644 index 0000000..28d706a --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux156/l156-headroom-landing.patch @@ -0,0 +1,841 @@ +diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +index 4799b9e..82bc9e1 100644 +--- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h ++++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +@@ -512,7 +512,8 @@ namespace VoxelForgeCore + // boundary-blended host rock from widening the authored passage wall. + // v238 plans one resolved passage spline with explicit landing chambers. + // v239 plans their floor planes as the passage's continuous floor profile. +- inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 239; ++ // v240 treats SurfaceWorld landings as headroom above max(planned floor,native ground). ++ inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 240; + + enum class FieldChannel : std::uint32_t + { +@@ -1212,8 +1213,9 @@ namespace VoxelForgeCore + + struct ResolvedLayoutPassageLandingChamberV1 + { +- // The chamber floor plane passes through this point. Gradient is dz/dx, +- // dz/dy in Q16 so the chamber continues the passage's authored h(s) grade. ++ // The planned chamber floor passes through this point. SurfaceWorld adds ++ // native ground where it lies within the clear-height envelope. Gradient is ++ // dz/dx,dy in Q16 and continues the passage's authored h(s) grade. + std::array floorCenterSubvoxels{}; + std::array floorGradientQ16{}; + std::int64_t radiusSubvoxels = 0; +@@ -2009,6 +2011,9 @@ namespace VoxelForgeCore + ShapeBuildStats buildStats() const; + + float densityAt(Vec3d voxelPosition) const; ++ // Deterministic native SurfaceWorld or receiving-route ground in Core voxels. ++ bool nativeSurfaceGroundHeightAt(Vec3d voxelPosition, ++ double& groundHeightVoxels) const; + // Companion material query for the bound strate evaluator; coordinates use Core voxels. + MaterialSampleV1 materialAt(Vec3d voxelPosition) const; + // Measure-only carve estimate. This does not apply an edit or grant inventory. +diff --git a/Standalone/Core/src/ResolvedLayoutInternal.h b/Standalone/Core/src/ResolvedLayoutInternal.h +index 7ea6c8c..c80b95d 100644 +--- a/Standalone/Core/src/ResolvedLayoutInternal.h ++++ b/Standalone/Core/src/ResolvedLayoutInternal.h +@@ -3993,6 +3993,72 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t + reach/static_cast(qPerMetre)); + } + } ++ // Reserve each landing envelope inside its host band before building h(s). ++ // SurfaceWorld can raise its floor by at most the 4.5 m headroom allowance, ++ // so include that bounded rise as well as the grade reach and floor shell. ++ constexpr std::int64_t MaximumLandingGradeQ16=17560; ++ const std::int64_t landingRadius=mouthRadius+3*qPerMetre; ++ const std::int64_t landingClearHeight=static_cast( ++ std::llround(4.5*qPerMetre)); ++ const std::int64_t landingShell=static_cast( ++ std::llround(0.75*qPerMetre)); ++ const std::int64_t landingPlaneReach=(landingRadius ++ *MaximumLandingGradeQ16+65535)/65536; ++ const std::array upperMouthBeforeBandFit=upperMouth; ++ const std::array lowerMouthBeforeBandFit=lowerMouth; ++ const auto fitMouth=[&](std::array& mouth, ++ const ResolvedLayoutStratumV2& host,std::int64_t playerHeightQ, ++ const char* side) ++ { ++ if(!host.content.presentInCurrentField)return true; ++ const std::int64_t minimumFloor=contentZQ(host.content.bottomMicrometres) ++ +landingPlaneReach+landingShell; ++ const std::int64_t nativeFloorRise=host.generatorKind=="SurfaceWorld" ++ ?landingClearHeight:0; ++ const std::int64_t maximumFloor=contentZQ(host.content.topMicrometres) ++ -landingPlaneReach-landingShell-landingClearHeight-nativeFloorRise; ++ if(maximumFloor(qPerMetre)); ++ } ++ return true; ++ }; ++ if(!fitMouth(upperMouth,upper,upperPlayerCenterAboveFloorQ,"upper") ++ ||!fitMouth(lowerMouth,lower,playerCenterAboveFloorQ,"lower"))return false; ++ const auto bindFittedMouthAnchor=[&]( ++ const ResolvedLayoutStratumV2& host, ++ const std::array& before, ++ const std::array& after,const char* side) ++ { ++ if(before==after)return; ++ auto mutableHost=std::find_if(layout.strates.begin(),layout.strates.end(), ++ [&](const auto& stratum) ++ {return stratum.strateIndex==host.strateIndex;}); ++ if(mutableHost!=layout.strates.end()) ++ for(auto& anchor:mutableHost->anchors) ++ if((anchor.kind=="passage_arrival" ++ ||anchor.kind=="passage_departure") ++ &&anchor.values.size()>=3 ++ &&anchor.values[0]==after[0] ++ &&anchor.values[1]==after[1]) ++ { ++ anchor.values[2]=after[2]; ++ std::printf("[PassageLandingChamberArrivalBinding] id=%s side=%s anchor=%s status=PASS\n", ++ passage.stableId.c_str(),side,anchor.stableId.c_str()); ++ break; ++ } ++ }; ++ bindFittedMouthAnchor(upper,upperMouthBeforeBandFit,upperMouth,"upper"); ++ bindFittedMouthAnchor(lower,lowerMouthBeforeBandFit,lowerMouth,"lower"); + if(!radialWithin(upperMouth,placementRadius)||!radialWithin(lowerMouth,placementRadius)) + {error=passage.stableId+" route mouth is outside the resolved PassageConfig.DistanceMax radius";return false;} + passage.upperMouthSubvoxels=upperMouth;passage.lowerMouthSubvoxels=lowerMouth; +@@ -5048,6 +5114,91 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t + surfaceHorizontalSquared)}; + } + } ++ if(host.generatorKind=="FloatingIslands"&&side[0]=='u') ++ { ++ // The island route is authored as player-center heights. Keep ++ // the chamber center bound to passage h(s), while selecting the ++ // closest grade plane that leaves the audited lower capsule arc ++ // above every receiving route point under the chamber footprint. ++ const auto route=routePoints(host); ++ const std::int64_t routeRadius=chamber.radiusSubvoxels ++ +static_cast(std::llround(0.34*qPerMetre)); ++ const std::int64_t routeRadiusSquared=routeRadius*routeRadius; ++ const std::int64_t footClearanceQ=static_cast( ++ std::llround((0.84+0.025)*qPerMetre)); ++ struct FRouteFloorConstraint ++ { ++ std::int64_t dx=0,dy=0,maximumRiseQ16=0; ++ }; ++ std::vector constraints; ++ constraints.reserve(route.size()); ++ for(const auto& point:route) ++ { ++ const auto dx=static_cast(std::llround( ++ point[0]-chamber.floorCenterSubvoxels[0])); ++ const auto dy=static_cast(std::llround( ++ point[1]-chamber.floorCenterSubvoxels[1])); ++ if(dx*dx+dy*dy>routeRadiusSquared)continue; ++ const auto routeFloorMaximum=static_cast( ++ std::llround(point[2]))-footClearanceQ; ++ constraints.push_back({dx,dy, ++ (routeFloorMaximum-chamber.floorCenterSubvoxels[2])*65536}); ++ } ++ const auto originalGradient=chamber.floorGradientQ16; ++ for(std::size_t pass=0;pass<256&&!constraints.empty();++pass) ++ { ++ bool adjusted=false; ++ for(const auto& constraint:constraints) ++ { ++ const std::int64_t distanceSquared=constraint.dx*constraint.dx ++ +constraint.dy*constraint.dy; ++ if(distanceSquared<=0)continue; ++ const std::int64_t rise= ++ static_cast(chamber.floorGradientQ16[0]) ++ *constraint.dx ++ +static_cast(chamber.floorGradientQ16[1]) ++ *constraint.dy; ++ const std::int64_t excess=rise-constraint.maximumRiseQ16; ++ if(excess<=0)continue; ++ const auto correction=[&](std::int64_t delta) ++ { ++ if(delta==0)return std::int64_t{0}; ++ const std::uint64_t product=static_cast(excess) ++ *static_cast(std::abs(delta)); ++ return static_cast((product ++ +static_cast(distanceSquared)-1) ++ /static_cast(distanceSquared)); ++ }; ++ const auto correctionX=correction(constraint.dx); ++ const auto correctionY=correction(constraint.dy); ++ chamber.floorGradientQ16[0]-=static_cast( ++ constraint.dx<0?-correctionX:correctionX); ++ chamber.floorGradientQ16[1]-=static_cast( ++ constraint.dy<0?-correctionY:correctionY); ++ adjusted|=correctionX!=0||correctionY!=0; ++ } ++ if(!adjusted)break; ++ } ++ for(const auto& constraint:constraints) ++ { ++ const std::int64_t rise= ++ static_cast(chamber.floorGradientQ16[0]) ++ *constraint.dx ++ +static_cast(chamber.floorGradientQ16[1]) ++ *constraint.dy; ++ if(rise>constraint.maximumRiseQ16) ++ {error=passage.stableId ++ +" upper landing floor cannot clear its receiving FloatingIslands route"; ++ return false;} ++ } ++ if(chamber.floorGradientQ16!=originalGradient) ++ std::printf("[PassageLandingChamberRouteFit] id=%s side=upper host=FloatingIslands route_points=%zu fitted_constraints=%zu source_gradient_q16=(%lld,%lld) fitted_gradient_q16=(%lld,%lld) foot_clearance_m=0.865 max_grade_deg=15.000 status=PASS\n", ++ passage.stableId.c_str(),route.size(),constraints.size(), ++ static_cast(originalGradient[0]), ++ static_cast(originalGradient[1]), ++ static_cast(chamber.floorGradientQ16[0]), ++ static_cast(chamber.floorGradientQ16[1])); ++ } + const std::uint64_t gradientSquared=static_cast( + chamber.floorGradientQ16[0]*chamber.floorGradientQ16[0] + +chamber.floorGradientQ16[1]*chamber.floorGradientQ16[1]); +diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h +index 6b1a6af..c20b0ce 100644 +--- a/Standalone/Core/src/ShapeWorldInternal.h ++++ b/Standalone/Core/src/ShapeWorldInternal.h +@@ -1280,11 +1280,32 @@ public: + double unionDistance=std::numeric_limits::infinity(); + double unionShellDistance=std::numeric_limits::infinity(); + double unionFloorShellDistance=std::numeric_limits::infinity(); ++ double unionLandingChamberDistance=std::numeric_limits::infinity(); + double unionLandingChamberFloorShellDistance= + std::numeric_limits::infinity(); + double plannedTunnelPassageShellDistance=std::numeric_limits::infinity(); + const std::uint32_t reliefSeed=static_cast(Owner?Owner->Seed:0); + const auto* queryStratum=FindBoundStratumForManagerZ(Z); ++ const auto effectiveLandingFloor=[&]( ++ const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& chamber, ++ double plannedFloorZ,double sampleXSubvoxels,double sampleYSubvoxels) ++ { ++ if(!std::isfinite(sampleXSubvoxels)||!std::isfinite(sampleYSubvoxels)) ++ return plannedFloorZ; ++ double nativeGroundCoreVoxels=0.0; ++ if(!GetNativeSurfaceGroundHeightAt( ++ static_cast(sampleXSubvoxels/Q), ++ static_cast(sampleYSubvoxels/Q), ++ static_cast(plannedFloorZ/Q),nativeGroundCoreVoxels)) ++ return plannedFloorZ; ++ const double clearHeight=static_cast( ++ chamber.clearHeightSubvoxels); ++ const double nativeFloor=nativeGroundCoreVoxels*Q; ++ // A native surface above the chamber's clear-height budget denotes a ++ // different layer surface, not a landing floor to clear from this room. ++ return nativeFloor>plannedFloorZ&&nativeFloor<=plannedFloorZ+clearHeight ++ ?nativeFloor:plannedFloorZ; ++ }; + const bool plannedTunnelStratum=queryStratum&&queryStratum->Resolved + &&(queryStratum->Resolved->generatorKind=="TunnelNetwork" + ||queryStratum->Resolved->generatorKind=="VerticalShafts") +@@ -1339,10 +1360,12 @@ public: + landingChamber->floorCenterSubvoxels[0]); + const double centerY=static_cast( + landingChamber->floorCenterSubvoxels[1]); +- const double floorCenterZ=static_cast( ++ const double plannedFloorCenterZ=static_cast( + landingChamber->floorCenterSubvoxels[2]) + +(landingChamber->floorGradientQ16[0]*(qx-centerX) + +landingChamber->floorGradientQ16[1]*(qy-centerY))/65536.0; ++ const double floorCenterZ=effectiveLandingFloor(*landingChamber, ++ plannedFloorCenterZ,qx,qy); + const double radialDistance=DeterministicSqrt( + (qx-centerX)*(qx-centerX)+(qy-centerY)*(qy-centerY)) + -landingChamber->radiusSubvoxels; +@@ -1357,6 +1380,8 @@ public: + std::max(-floorDistance,floorDistance-shellThickness), + radialDistance); + unionDistance=std::min(unionDistance,chamberDistance); ++ unionLandingChamberDistance=std::min(unionLandingChamberDistance, ++ chamberDistance); + unionShellDistance=std::min(unionShellDistance,chamberShellDistance); + unionFloorShellDistance=std::min(unionFloorShellDistance, + chamberFloorShellDistance); +@@ -1746,15 +1771,18 @@ public: + departureTailDistance) + ||nearRouteSegment(*lastTwo,*last,PortalWalkLeadMetres,true,0.0); + }; +- const auto connectedSurfaceLandingShellContains=[&]() ++ const auto connectedNativeLandingShellContains=[&]() + { + if(!queryStratum||!queryStratum->Resolved +- ||queryStratum->Resolved->generatorKind!="SurfaceWorld")return false; ++ ||(queryStratum->Resolved->generatorKind!="SurfaceWorld" ++ &&queryStratum->Resolved->generatorKind!="FloatingIslands")) ++ return false; + const double queryX=qx/qPerMetre,queryY=qy/qPerMetre,queryZ=qz/qPerMetre; + constexpr double ChamberShellThicknessMetres=0.75; + constexpr double RouteClearanceMetres=1.25; + constexpr double RouteHeightClearanceMetres=1.5; +- constexpr double PortalWalkLeadMetres=9.0; ++ const double portalWalkLeadMetres= ++ queryStratum->Resolved->generatorKind=="FloatingIslands"?12.0:9.0; + for(const auto& passage:ResolvedLayout->passages) + { + if(passage.upperStrateIndex!=queryStratum->StrateIndex)continue; +@@ -1762,9 +1790,13 @@ public: + if(chamber.radiusSubvoxels<=0||chamber.clearHeightSubvoxels<=0)continue; + const double cx=static_cast(chamber.floorCenterSubvoxels[0])/qPerMetre; + const double cy=static_cast(chamber.floorCenterSubvoxels[1])/qPerMetre; +- const double floorZ=static_cast(chamber.floorCenterSubvoxels[2])/qPerMetre ++ const double plannedFloorZ=static_cast( ++ chamber.floorCenterSubvoxels[2])/qPerMetre + +chamber.floorGradientQ16[0]/65536.0*(queryX-cx) + +chamber.floorGradientQ16[1]/65536.0*(queryY-cy); ++ const double floorZ=effectiveLandingFloor(chamber, ++ plannedFloorZ*qPerMetre,queryX*qPerMetre, ++ queryY*qPerMetre)/qPerMetre; + const double radialDistance=std::hypot(queryX-cx,queryY-cy) + -static_cast(chamber.radiusSubvoxels)/qPerMetre; + const double floorDistance=floorZ-queryZ; +@@ -1784,7 +1816,11 @@ public: + const double mouthZ=static_cast(passage.upperMouthSubvoxels[2]) + /qPerMetre; + std::size_t endpoint=route.size(); +- double endpointDistanceSquared=1.0; ++ const double endpointRadius= ++ queryStratum->Resolved->generatorKind=="FloatingIslands"?3.0:1.0; ++ const double endpointHeight= ++ queryStratum->Resolved->generatorKind=="FloatingIslands"?2.0:1.5; ++ double endpointDistanceSquared=endpointRadius*endpointRadius; + for(std::size_t index=0;indexvalues[0]/qPerMetre; +@@ -1793,7 +1829,7 @@ public: + const double dx=x-cx,dy=y-cy; + const double distanceSquared=dx*dx+dy*dy; + if(distanceSquaredPortalWalkLeadMetres)continue; ++ if(alongFromEndpoint>portalWalkLeadMetres)continue; + const double routeX=ax+t*dx,routeY=ay+t*dy; + const double routeZ=az+t*(b.values[2]-a.values[2])/qPerMetre; +- if(std::hypot(queryX-routeX,queryY-routeY)<=RouteClearanceMetres +- &&std::abs(queryZ-routeZ)<=RouteHeightClearanceMetres)return true; ++ const double lateral=std::hypot(queryX-routeX,queryY-routeY); ++ const double vertical=std::abs(queryZ-routeZ); ++ if(lateral<=RouteClearanceMetres ++ &&vertical<=RouteHeightClearanceMetres)return true; + } + } + return false; +@@ -1887,13 +1925,19 @@ public: + } + if(nativeFloorSupport!=std::numeric_limits::lowest() + &&nativeFloorSupport<0.0f) ++ { ++ // Host floor support keeps solid ground under a landing chamber, but it ++ // cannot refill the chamber air above the selected planned/native floor. ++ if(unionLandingChamberDistance<=0.0) ++ return passageAirDistance; + return std::min(nativeFloorSupport,unionDistance<=0.0 + ?passageAirDistance:density); ++ } + if(nativeTunnelFieldDensity!=std::numeric_limits::lowest()) + return std::max(density,passageAirDistance); + if(unionDistance>0.0&&unionShellDistance<0.0&&density>=0.0f) + { +- if(connectedSurfaceLandingShellContains()) ++ if(connectedNativeLandingShellContains()) + return std::max(density,passageAirDistance); + return static_cast(unionShellDistance/Q); + } +@@ -2044,11 +2088,12 @@ public: + static_cast(chamber.floorGradientQ16[0])/65536.0, + static_cast(chamber.floorGradientQ16[1])/65536.0); + const double extent=reachCoreVoxels+0.01; ++ const double clearHeight=static_cast( ++ chamber.clearHeightSubvoxels)/Q; + const double low[3]={cx-radius-extent,cy-radius-extent, + cz-floorExtent-extent}; + const double high[3]={cx+radius+extent,cy+radius+extent, +- cz+floorExtent+static_cast( +- chamber.clearHeightSubvoxels)/Q+extent}; ++ cz+floorExtent+2.0*clearHeight+extent}; + return overlaps(low,high); + }; + if(chamberOverlaps(passage.upperLandingChamber) +@@ -5488,7 +5533,7 @@ private: + cx-radius-extent,cy-radius-extent,cz-floorExtent-extent}; + const std::array maximum{ + cx+radius+extent,cy+radius+extent, +- cz+floorExtent+height+extent}; ++ cz+floorExtent+2.0*height+extent}; + FPassageProofBound proof; + for(std::size_t axis=0;axis<3;++axis) + { +@@ -5859,6 +5904,71 @@ public: + return std::isfinite(OutGroundZ)&&std::isfinite(OutCeilingZ); + } + ++ bool GetNativeSurfaceGroundHeightAt(float X,float Y,float Z, ++ double& OutGroundZ) const ++ { ++ if(!ResolvedLayout||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z)) ++ return false; ++ const auto* bound=FindBoundStratumForManagerZ(Z); ++ if(!bound)return false; ++ constexpr double Scale=static_cast( ++ VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel); ++ const double maxVoxel=static_cast(std::numeric_limits::max())/Scale; ++ if(std::abs(static_cast(X))>maxVoxel ++ ||std::abs(static_cast(Y))>maxVoxel)return false; ++ const auto toFixed=[](float value) ++ {return static_cast(std::llround(static_cast(value) ++ *VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));}; ++ const auto* field=std::get_if(&bound->Evaluator); ++ if(field&&field->Evaluator.isValid()) ++ { ++ const auto column=field->Evaluator.point(toFixed(X),toFixed(Y),0); ++ OutGroundZ=static_cast(column.groundHeightSubvoxels)/Scale; ++ return std::isfinite(OutGroundZ); ++ } ++ const auto* islands=std::get_if(&bound->Evaluator); ++ if(!islands||!bound->IslandPlan)return false; ++ const auto& route=bound->IslandPlan->manifest.plan.route; ++ if(route.size()<2)return false; ++ const auto& recipe=bound->IslandPlan->manifest.recipe; ++ const std::int64_t routeLift=recipe.routeConstraints.capsuleHeightSubvoxels/2 ++ +recipe.routeConstraints.minimumClearanceSubvoxels ++ +(recipe.geometryVersion>=2 ++ ?VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel/8:0); ++ const std::int64_t supportRadius=recipe.routeConstraints ++ .supportProbeRadiusSubvoxels; ++ if(routeLift<0||supportRadius<0)return false; ++ const std::int64_t queryX=toFixed(X),queryY=toFixed(Y); ++ std::uint64_t nearestDistanceSquared=std::numeric_limits::max(); ++ std::int64_t nearestRouteZ=0; ++ bool found=false; ++ for(std::size_t index=1;index( ++ offsetX*offsetX+offsetY*offsetY); ++ if(distanceSquared>static_cast(supportRadius)*supportRadius ++ ||distanceSquared>=nearestDistanceSquared)continue; ++ const std::int64_t dz=b[2]-a[2]; ++ nearestRouteZ=a[2]+dz*projection/lengthSquared; ++ nearestDistanceSquared=distanceSquared; ++ found=true; ++ } ++ if(!found)return false; ++ OutGroundZ=static_cast(nearestRouteZ-routeLift)/Scale; ++ return std::isfinite(OutGroundZ); ++ } ++ + EVoxelTileClass ClassifyBox(const FIntVector& Origin,int32 Step,int32 Cells) const + { return ClassifyBoxDetailed(Origin,Step,Cells).Verdict; } + +diff --git a/Standalone/Core/src/VoxelForgeCore.cpp b/Standalone/Core/src/VoxelForgeCore.cpp +index b139ec4..e40250c 100644 +--- a/Standalone/Core/src/VoxelForgeCore.cpp ++++ b/Standalone/Core/src/VoxelForgeCore.cpp +@@ -1811,6 +1811,13 @@ namespace VoxelForgeCore + + float World::densityAt(Vec3d position) const + { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DensityAt(static_cast(position.x),static_cast(position.y),static_cast(position.z)):0.0f; } ++ bool World::nativeSurfaceGroundHeightAt(Vec3d position,double& groundHeightVoxels) const ++ { ++ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape ++ &&impl_->shape->GetNativeSurfaceGroundHeightAt(static_cast(position.x), ++ static_cast(position.y),static_cast(position.z), ++ groundHeightVoxels); ++ } + MaterialSampleV1 World::materialAt(Vec3d position) const + { + if(!impl_||!impl_->shapeReady.load(std::memory_order_acquire)||!impl_->shape) +diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h +index 2fcf4b5..3ed4dd1 100644 +--- a/Standalone/Source/StreamBenchmarkPipeline.h ++++ b/Standalone/Source/StreamBenchmarkPipeline.h +@@ -3051,7 +3051,8 @@ namespace StandaloneStreamBenchmark + double supportMaximumMetres,std::uint32_t minimumSupportProbes, + double supportProbeRadiusMetres, + const std::function& supportExemption={}, +- const std::function& passageInteriorPredicate={}) ++ const std::function& passageInteriorPredicate={}, ++ const std::function& densityOverride={}) + { + FWalkAudit audit; + if(path.size()<2)return audit; +@@ -3068,15 +3069,18 @@ namespace StandaloneStreamBenchmark + &&passageInteriorPredicate(xMetres,yMetres,zMetres); + if(inPassageInterior)++audit.passageInteriorCapsuleSamples; + const VoxelForgeCore::Vec3d position{xMetres/coreMetres,yMetres/coreMetres,zMetres/coreMetres}; +- const float density=Field.densityAt(position); ++ const float density=densityOverride ++ ?densityOverride(position):Field.densityAt(position); + if(density<0.0f) + { + if(inPassageInterior)++audit.passageInteriorSolidSamples; + if(audit.firstBlockedCount::max(), + std::size_t ignoredSegment=std::numeric_limits::max(), +- std::size_t selectedPassage=std::numeric_limits::max()) ++ std::size_t selectedPassage=std::numeric_limits::max(), ++ bool includeLandingChambers=true) + { + constexpr double CornerBlendMetres=0.50; + constexpr double WallReliefMetres=0.21; +@@ -3748,8 +3761,9 @@ namespace StandaloneStreamBenchmark + +qToMetres(chamber.clearHeightSubvoxels)); + return std::max({radialDistance,floorDistance,ceilingDistance})<=0.0; + }; +- if(inLandingChamber(passage.upperLandingChamber) +- ||inLandingChamber(passage.lowerLandingChamber))return true; ++ if(includeLandingChambers ++ &&(inLandingChamber(passage.upperLandingChamber) ++ ||inLandingChamber(passage.lowerLandingChamber)))return true; + const auto& route=allPassageSmoothRoutes[passageIndex]; + if(route.size()<2)continue; + double bestRoundDistance=std::numeric_limits::infinity(); +@@ -4722,7 +4736,13 @@ namespace StandaloneStreamBenchmark + const double mouthZ=qToMetres(upper?passage.upperMouthSubvoxels[2] + :passage.lowerMouthSubvoxels[2]); + std::size_t endpoint=route.size(); +- double endpointDistanceSquared=1.0; ++ const double endpointRadius=stratum->generatorKind=="FloatingIslands" ++ ?3.0:1.0; ++ const double endpointHeight=stratum->generatorKind=="FloatingIslands" ++ ?2.0:1.5; ++ const double portalWalkLead=stratum->generatorKind=="FloatingIslands" ++ ?12.0:9.0; ++ double endpointDistanceSquared=endpointRadius*endpointRadius; + for(std::size_t index=0;indexvalues[0])-cx; +@@ -4730,7 +4750,7 @@ namespace StandaloneStreamBenchmark + const double z=qToMetres(route[index]->values[2]); + const double distanceSquared=dx*dx+dy*dy; + if(distanceSquared9.0)continue; ++ if(fromEndpoint>portalWalkLead)continue; + const double routeX=ax+t*dx,routeY=ay+t*dy; + const double routeZ=az+t*qToMetres(b.values[2]-a.values[2]); + const double lateralDistance=std::hypot(xMetres-routeX, +@@ -5167,6 +5187,21 @@ namespace StandaloneStreamBenchmark + const double radius=qToMetres(chamber.radiusSubvoxels); + const double gx=chamber.floorGradientQ16[0]/65536.0; + const double gy=chamber.floorGradientQ16[1]/65536.0; ++ const auto floorAt=[&](double x,double y) ++ { ++ const double planned=baseFloorZ+gx*(x-centerX)+gy*(y-centerY); ++ double nativeGroundVoxels=0.0; ++ if(Field.nativeSurfaceGroundHeightAt({x/coreMetres,y/coreMetres, ++ planned/coreMetres},nativeGroundVoxels)) ++ { ++ const double nativeGround=nativeGroundVoxels*coreMetres; ++ const double clearHeight=qToMetres( ++ chamber.clearHeightSubvoxels); ++ if(nativeGround>planned&&nativeGround<=planned+clearHeight) ++ return nativeGround; ++ } ++ return planned; ++ }; + for(const double radial:chamberFloorRadii) + { + if(radial>=radius-1.0)continue; +@@ -5177,16 +5212,18 @@ namespace StandaloneStreamBenchmark + *static_cast(angleIndex)/ChamberAngularSamples; + const double sampleX=centerX+radial*std::cos(angle); + const double sampleY=centerY+radial*std::sin(angle); +- const double floorZ=baseFloorZ+gx*(sampleX-centerX) +- +gy*(sampleY-centerY); ++ const double floorZ=floorAt(sampleX,sampleY); + for(const double depth:PassageShellAuditDepthsMetres) + { + const double sampleZ=floorZ-depth; + const VoxelForgeCore::Vec3d position{sampleX/coreMetres, + sampleY/coreMetres,sampleZ/coreMetres}; + if(Native.densityAt(position)<0.0f)continue; +- if(landingChamberConnectionContains(passage,upper, +- sampleX,sampleY,sampleZ)) ++ if(passageInteriorWinsAt(sampleX,sampleY,sampleZ, ++ std::numeric_limits::max(), ++ std::numeric_limits::max(),passageIndex) ++ ||landingChamberConnectionContains(passage,upper, ++ sampleX,sampleY,sampleZ)) + {++audit.chamberConnectionSkips;continue;} + ++audit.chamberFloorSamples; + const float density=Field.densityAt(position); +@@ -5211,8 +5248,7 @@ namespace StandaloneStreamBenchmark + { + const double sampleX=centerX+(radius+depth)*nx; + const double sampleY=centerY+(radius+depth)*ny; +- const double floorZ=baseFloorZ+gx*(sampleX-centerX) +- +gy*(sampleY-centerY); ++ const double floorZ=floorAt(sampleX,sampleY); + const double sampleZ=floorZ+height; + const VoxelForgeCore::Vec3d position{sampleX/coreMetres, + sampleY/coreMetres,sampleZ/coreMetres}; +@@ -5275,6 +5311,25 @@ namespace StandaloneStreamBenchmark + {return passageInteriorWinsAt(x,y,z, + std::numeric_limits::max(), + std::numeric_limits::max(),passageIndex);}; ++ const auto floorAt=[&](double x,double y) ++ { ++ const double planned=centerZ+gx*(x-centerX)+gy*(y-centerY); ++ double nativeGroundVoxels=0.0; ++ if(field.nativeSurfaceGroundHeightAt({x/coreMetres,y/coreMetres, ++ planned/coreMetres},nativeGroundVoxels)) ++ { ++ const double nativeGround=nativeGroundVoxels*coreMetres; ++ const double clearHeight=qToMetres(chamber.clearHeightSubvoxels); ++ if(nativeGround>planned&&nativeGround<=planned+clearHeight) ++ return nativeGround; ++ } ++ return planned; ++ }; ++ const std::function supportExemption= ++ [&](double x,double y,double z) ++ {return passageInteriorWinsAt(x,y,z, ++ std::numeric_limits::max(), ++ std::numeric_limits::max(),passageIndex,false);}; + for(const auto& direction:directions) + { + std::vector> chamberPath; +@@ -5286,12 +5341,12 @@ namespace StandaloneStreamBenchmark + const double distance=radius*step/intervals; + const double x=centerX+direction[0]*distance; + const double y=centerY+direction[1]*distance; +- const double floorZ=centerZ+gx*(x-centerX)+gy*(y-centerY); ++ const double floorZ=floorAt(x,y); + chamberPath.push_back({x/coreMetres,y/coreMetres, + (floorZ+1.13)/coreMetres}); + } + const FWalkAudit part=auditPlayerPath(field,chamberPath, +- 0.125,0.125,1.125,3,0.45,{},interiorPredicate); ++ 0.125,0.125,1.125,3,0.45,supportExemption,interiorPredicate); + total.segments+=part.segments;total.centers+=part.centers; + total.capsuleSamples+=part.capsuleSamples; + total.blocked+=part.blocked; +@@ -5305,10 +5360,116 @@ namespace StandaloneStreamBenchmark + } + return total; + }; ++ const auto auditOpenChamberFloorNegativeControl=[&]( ++ const VoxelForgeCore::World& field, ++ const VoxelForgeCore::ResolvedLayoutPassageV1& passage, ++ std::size_t passageIndex,bool upper) ++ { ++ const auto& chamber=upper?passage.upperLandingChamber ++ :passage.lowerLandingChamber; ++ const double centerX=qToMetres(chamber.floorCenterSubvoxels[0]); ++ const double centerY=qToMetres(chamber.floorCenterSubvoxels[1]); ++ const double centerZ=qToMetres(chamber.floorCenterSubvoxels[2]); ++ const double gx=chamber.floorGradientQ16[0]/65536.0; ++ const double gy=chamber.floorGradientQ16[1]/65536.0; ++ const double chamberAuditRadius=std::min(5.75, ++ qToMetres(chamber.radiusSubvoxels)-1.5); ++ const double mouthRadius=qToMetres(passage.mouthRadiusSubvoxels); ++ const double radius=std::min(chamberAuditRadius-0.1,mouthRadius+1.25); ++ if(radius<=0.0) ++ { ++ std::printf("[PassageLandingChamberOpenFloorNegativeControl] id=%s side=%s radius_m=%.3f mouth_radius_m=%.3f unsupported=0 support_overlap_skips=0 expected=FAIL status=FAIL reason=no_outer_floor_sample\n", ++ passage.stableId.c_str(),upper?"upper":"lower",radius,mouthRadius); ++ return false; ++ } ++ const auto floorAt=[&](double x,double y) ++ { ++ const double planned=centerZ+gx*(x-centerX)+gy*(y-centerY); ++ double nativeGroundVoxels=0.0; ++ if(field.nativeSurfaceGroundHeightAt({x/coreMetres,y/coreMetres, ++ planned/coreMetres},nativeGroundVoxels)) ++ { ++ const double nativeGround=nativeGroundVoxels*coreMetres; ++ const double clearHeight=qToMetres(chamber.clearHeightSubvoxels); ++ if(nativeGround>planned&&nativeGround<=planned+clearHeight) ++ return nativeGround; ++ } ++ return planned; ++ }; ++ const std::function interiorPredicate= ++ [&](double x,double y,double z) ++ {return passageInteriorWinsAt(x,y,z, ++ std::numeric_limits::max(), ++ std::numeric_limits::max(),passageIndex);}; ++ const std::function supportExemption= ++ [&](double x,double y,double z) ++ {return passageInteriorWinsAt(x,y,z, ++ std::numeric_limits::max(), ++ std::numeric_limits::max(),passageIndex,false);}; ++ constexpr std::array,5> supportOffsets{{ ++ {{0.0,0.0}},{{0.45,0.0}},{{-0.45,0.0}}, ++ {{0.0,0.45}},{{0.0,-0.45}}}}; ++ std::vector> path; ++ double selectedAngle=0.0; ++ bool foundOutsideBore=false; ++ for(std::size_t directionIndex=0;directionIndex<32&&!foundOutsideBore; ++ ++directionIndex) ++ { ++ selectedAngle=2.0*3.14159265358979323846 ++ *static_cast(directionIndex)/32.0; ++ const double nx=std::cos(selectedAngle),ny=std::sin(selectedAngle); ++ const double tangentX=-ny,tangentY=nx; ++ const double x0=centerX+radius*nx-0.25*tangentX; ++ const double y0=centerY+radius*ny-0.25*tangentY; ++ const double x1=centerX+radius*nx+0.25*tangentX; ++ const double y1=centerY+radius*ny+0.25*tangentY; ++ const double floor0=floorAt(x0,y0),floor1=floorAt(x1,y1); ++ const std::array,2> candidate{{ ++ {{x0,y0,floor0+1.13}},{{x1,y1,floor1+1.13}}}}; ++ bool samplesOutsideBore=true; ++ for(const auto& point:candidate) ++ { ++ const double supportZ=point[2]-0.88-0.125; ++ for(const auto& offset:supportOffsets) ++ if(supportExemption(point[0]+offset[0], ++ point[1]+offset[1],supportZ)) ++ {samplesOutsideBore=false;break;} ++ if(!samplesOutsideBore)break; ++ } ++ if(samplesOutsideBore) ++ { ++ path={{candidate[0][0]/coreMetres,candidate[0][1]/coreMetres, ++ candidate[0][2]/coreMetres}, ++ {candidate[1][0]/coreMetres,candidate[1][1]/coreMetres, ++ candidate[1][2]/coreMetres}}; ++ foundOutsideBore=true; ++ } ++ } ++ if(!foundOutsideBore) ++ { ++ std::printf("[PassageLandingChamberOpenFloorNegativeControl] id=%s side=%s radius_m=%.3f mouth_radius_m=%.3f unsupported=0 support_overlap_skips=0 expected=FAIL status=FAIL reason=no_bore_clear_spoke\n", ++ passage.stableId.c_str(),upper?"upper":"lower",radius,mouthRadius); ++ return false; ++ } ++ const std::function openDensity= ++ [](VoxelForgeCore::Vec3d){return 1.0f;}; ++ const FWalkAudit control=auditPlayerPath(field,path,0.125,0.125, ++ 1.125,3,0.45,supportExemption,interiorPredicate,openDensity); ++ const bool passed=!control.passed&&control.unsupported>0 ++ &&control.supportOverlapSkips==0; ++ std::printf("[PassageLandingChamberOpenFloorNegativeControl] id=%s side=%s radius_m=%.3f mouth_radius_m=%.3f angle_deg=%.1f unsupported=%llu support_overlap_skips=%llu result=%s expected=FAIL status=%s\n", ++ passage.stableId.c_str(),upper?"upper":"lower",radius,mouthRadius, ++ selectedAngle*180.0/3.14159265358979323846, ++ static_cast(control.unsupported), ++ static_cast(control.supportOverlapSkips), ++ control.passed?"PASS":"FAIL",passed?"PASS":"FAIL"); ++ return passed; ++ }; + std::uint64_t passageCount=0,passagePasses=0,blockedControls=0,naturalPasses=0; + std::uint64_t passageInteriorCapsuleSamples=0,passageInteriorSolidSamples=0; + std::uint64_t passageInteriorControls=0; + std::uint64_t landingChamberCount=0,landingChamberNegativeControls=0; ++ std::uint64_t openChamberFloorNegativeControls=0; + std::uint64_t mouthProfilePasses=0,ripplePasses=0,rippleControlsDetected=0; + std::uint64_t floorRippleControlsDetected=0,floorStepRippleControlsDetected=0; + std::uint64_t shellAudits=0,shellFloorSamples=0,shellFloorViolations=0; +@@ -5346,6 +5507,9 @@ namespace StandaloneStreamBenchmark + passage,passageIndex,true); + const FWalkAudit lowerChamberAudit=auditLandingChamber(Shape, + passage,passageIndex,false); ++ const bool openChamberFloorControl=auditOpenChamberFloorNegativeControl( ++ Shape,passage,passageIndex,true); ++ openChamberFloorNegativeControls+=openChamberFloorControl; + const bool landingChambersWalkable=upperChamberAudit.passed + &&lowerChamberAudit.passed; + const bool passageInteriorAirPassed= +@@ -5889,6 +6053,10 @@ namespace StandaloneStreamBenchmark + static_cast(landingChamberCount), + static_cast(landingChamberCount + -landingChamberNegativeControls)); ++ std::printf("[PassageLandingChamberOpenFloorNegativeControlSummary] expected_fail_detected=%llu/%llu status=%s\n", ++ static_cast(openChamberFloorNegativeControls), ++ static_cast(passageCount), ++ openChamberFloorNegativeControls==passageCount?"PASS":"FAIL"); + const bool shellAuditPassed=shellAudits==passageCount + &&shellFloorViolations==0&&shellWallViolations==0 + &&shellChamberFloorViolations==0&&shellChamberWallViolations==0 +@@ -5939,6 +6107,7 @@ namespace StandaloneStreamBenchmark + &&mouthProfilePasses==passageCount&&l23TerraceControlDetected + &&ripplePasses==passageCount + &&shellAuditPassed ++ &&openChamberFloorNegativeControls==passageCount + &&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings + &&routeBindingFailures==0 + &&tunnelNetworkRoutes==tunnelNetworkBlockedControls