#!/usr/bin/env python3 """Render cave, motif, and lattice features from meshes streamed at LOD4-8.""" from __future__ import annotations import argparse import hashlib import importlib.util import json import math import os from pathlib import Path import re import struct import sys ROOT = Path(__file__).resolve().parents[4] CAPTURE54 = ROOT / "Standalone/Research/WorldAssemblerM3/Linux54/CaptureNativeCavesL54.py" spec = importlib.util.spec_from_file_location("capture_native_caves_l54", CAPTURE54) capture54 = importlib.util.module_from_spec(spec) assert spec and spec.loader spec.loader.exec_module(capture54) run_step = capture54.run_step camera_angles = capture54.camera_angles fmt = capture54.fmt QUALITY = ROOT / "Standalone/Tools/verify/capture_quality.py" Q = 1_000_000.0 def sha256(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as stream: for block in iter(lambda: stream.read(1024 * 1024), b""): digest.update(block) return digest.hexdigest() def midpoint(points: list[tuple[float, float, float]]) -> tuple[float, float, float]: return tuple(sum(point[axis] for point in points) / len(points) for axis in range(3)) def read_feature(name: str, layout_path: Path): layout = json.loads(layout_path.read_text(encoding="utf-8")) if name == "caves": edges = sorted(layout.get("cavern_edges", []), key=lambda row: row["stable_id"]) if len(edges) != 2: raise ValueError("cave layout must have two resolved spine edges") points = [[value / Q for value in point] for point in edges[0]["control_points_um"]] target = tuple(points[-1][axis] for axis in range(3)) return target, target[2] + 2.0, 28.0, "cave mouth" if name == "motifs": monolith = next(row for row in layout.get("stamps", []) if row.get("kind") == 5) target = tuple(value / Q for value in monolith["position_um"]) floor = monolith["floor_um"] / Q return (target[0], target[1], floor + 12.0), floor + 12.0, 32.0, "monolith" edges = layout.get("cavern_edges", []) helix = next((edge for edge in edges if edge.get("stable_id") == "lattice/spine/01-helix-ramp-v1"), None) if helix is None: raise ValueError("lattice layout has no resolved helix") points = [tuple(value / Q for value in point) for point in helix["polyline_um"]] target = midpoint(points) return target, target[2] + 4.0, 36.0, "helix" def audit_roi_lods(mesh: Path, center: tuple[float, float, float], radius: float): roi_counts: dict[int, int] = {} roi_skirts: dict[int, int] = {} with mesh.open("rb") as stream: prefix = stream.read(60) if len(prefix) != 60 or prefix[:8] != b"VFMSH001": raise ValueError("far-LOD audit requires a VFMSH001 Core mesh") version, voxel_um, tile_count = struct.unpack_from("<3I", prefix, 8) if version != 1 or voxel_um <= 0: raise ValueError("mesh version or voxel size is invalid") for _ in range(tile_count): header = stream.read(36) if len(header) != 36: raise ValueError("truncated far-LOD tile header") key = struct.unpack_from("<4i", header, 0) surface_count, skirt_count, vertex_count, index_count, mask_count = \ struct.unpack_from("<5I", header, 16) positions = stream.read(vertex_count * 12) normals = stream.read(vertex_count * 12) index_bytes = stream.read(index_count * 4) masks = stream.read(mask_count) if (len(positions) != vertex_count * 12 or len(normals) != vertex_count * 12 or len(index_bytes) != index_count * 4 or len(masks) != mask_count): raise ValueError("truncated far-LOD tile payload") lod = key[3] coords = list(struct.iter_unpack("<3f", positions)) indices = struct.unpack(f"<{index_count}I", index_bytes) if index_count else () for triangle in range(index_count // 3): ids = indices[triangle * 3:triangle * 3 + 3] if max(ids) >= vertex_count: raise ValueError("far-LOD triangle references an invalid vertex") is_skirt = bool(masks and triangle < len(masks) and masks[triangle]) if is_skirt: continue centroid = tuple(sum(coords[index][axis] for index in ids) / 3.0 for axis in range(3)) distance_squared = sum((centroid[axis] - center[axis]) ** 2 for axis in range(3)) if distance_squared <= radius * radius: roi_counts[lod] = roi_counts.get(lod, 0) + 1 if skirt_count: roi_skirts[lod] = roi_skirts.get(lod, 0) + skirt_count if stream.read(1): raise ValueError("far-LOD mesh has trailing bytes") return roi_counts, roi_skirts def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--out", type=Path, required=True) parser.add_argument("--fixture-dir", type=Path, required=True) parser.add_argument("--layout-dir", type=Path, required=True) parser.add_argument("--headless", type=Path, required=True) parser.add_argument("--renderer", type=Path, required=True) parser.add_argument("--artifact-dir", type=Path, required=True) args = parser.parse_args() out = args.out.resolve() fixture_dir, layout_dir = args.fixture_dir.resolve(), args.layout_dir.resolve() artifact_dir = args.artifact_dir.resolve() mesh_dir, png_dir = out / "far-lod-meshes", out / "far-lod-png" for directory in (mesh_dir, png_dir, artifact_dir): directory.mkdir(parents=True, exist_ok=True) env = os.environ.copy() env["LP_NUM_THREADS"] = "12" env["XDG_CACHE_HOME"] = str(out / "xdg-cache") cases = ( ("caves", fixture_dir / "caves.json", layout_dir / "native-caves-field-clang.json", "caves", []), ("motifs", fixture_dir / "style-override-motifs-slice1.json", layout_dir / "motif-slice1-capsule-reachability-clang.json", "surface-world", []), ("lattice", fixture_dir / "lattice-l58.json", layout_dir / "native-lattice-field-clang.json", "lattice", ["--lattice-strate=1"]), ) quality_rows = [] for name, fixture, layout, recipe, extra_flags in cases: if not fixture.is_file() or not layout.is_file(): raise RuntimeError(f"missing {name} fixture or layout") target, target_height, radius, label = read_feature(name, layout) stream_camera = (target[0] + 1024.0, target[1], target[2], 0.0, 0.0) stream_yaw, stream_pitch = camera_angles(stream_camera[:3], target) stream_camera = (*stream_camera[:3], stream_yaw, stream_pitch) mesh = mesh_dir / f"{name}-lod4-8.vfmesh" mesh_layout = layout_dir / f"{name}-far-lod-layout.json" command = [args.headless, fixture, "--workers=12", "--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=3", "--max-clip-level=8", "--max-level=-1", "--render-distance-chunks=0", f"--recipe={recipe}", f"--camera={fmt(stream_camera)}", *extra_flags, f"--resolved-layout-output={mesh_layout}", f"--mesh-export={mesh}"] output = run_step(f"{name}-far-lod-export", command, out / f"capture-{name}-far-lod-export.log", env=env, stamped=True) worker_rows = {int(match.group(1)): (int(match.group(2)), int(match.group(3))) for match in re.finditer( r"^\[WorldAssemblerLODWorker\] lod=(\d+) tiles=(\d+) .*?skirt_triangles=(\d+)", output, re.M)} if (set(worker_rows) != set(range(9)) or any(worker_rows[level][0] <= 0 for level in range(9)) or sum(worker_rows[level][1] for level in range(4, 9)) <= 0): raise RuntimeError(f"{name} far view lacks LOD0-8 tile/skirt work: {worker_rows}") if not mesh.is_file() or mesh.stat().st_size == 0: raise RuntimeError(f"{name} far LOD export produced no mesh") roi, roi_skirts = audit_roi_lods(mesh, target, radius) feature_lods = sorted(level for level, count in roi.items() if count > 0) far_feature_lods = [level for level in feature_lods if level >= 4] if not far_feature_lods: raise RuntimeError(f"{name} {label} has no non-skirt surface triangles in LOD4-8 ROI: {roi}") camera = (target[0] + 18.0, target[1] - 28.0, target_height + 10.0) yaw, pitch = camera_angles(camera, target) png = png_dir / f"{name}-far-lod.png" image = artifact_dir / f"{name}-far-lod.jpg" render = [args.renderer, mesh, png, "--size=1280x720", "--fov-deg=76", f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}", "--tile-skirts=on", "--shade=readable", "--headlamp=on", "--height-tint=on", f"--height-center-m={target_height:.6f}", "--height-range-m=42", "--tile-edges=off", f"--focus-region={fmt((*target, radius))}"] run_step(f"{name}-far-lod-render", render, out / f"capture-{name}-far-lod-render.log", env=env, stamped=True) run_step(f"{name}-far-lod-jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y", "-i", png, "-frames:v", "1", "-q:v", "5", image], out / f"capture-{name}-far-lod-jpeg.log", env=env) quality_output = run_step(f"{name}-far-lod-quality", [sys.executable, QUALITY, image], out / f"capture-{name}-far-lod-quality.log", env=env) quality = [line for line in quality_output.splitlines() if line.startswith("[CaptureQuality]")] if not quality or not quality[-1].endswith("status=PASS"): raise RuntimeError(f"{name} far LOD capture failed its image quality gate") size = image.stat().st_size if not 0 < size <= 300_000: raise RuntimeError(f"{name} far LOD capture exceeds 300 KB: {size}") print(f"[L60FarLODCapture] generator={name} feature={label} file={image} " f"bytes={size} sha256={sha256(image)} feature_lods={feature_lods} " f"feature_triangles={sum(roi.values())} far_skirt_triangles=" f"{sum(value for lod, value in roi_skirts.items() if lod >= 4)} status=PASS", flush=True) quality_rows.append({"generator": name, "feature": label, "file": str(image), "bytes": size, "sha256": sha256(image), "feature_triangles_by_lod": roi, "feature_skirts_by_lod": roi_skirts}) (out / "far-lod-captures.json").write_text(json.dumps(quality_rows, indent=2) + "\n", encoding="utf-8") return 0 if __name__ == "__main__": try: raise SystemExit(main()) except Exception as error: print(f"[L60FarLODCapture] status=FAIL error={error}", file=sys.stderr, flush=True) raise SystemExit(1)