captures: readable player-view captures with a coverage gate (L69); R36 native-generator survey

- Capture exports now cover a 90-degree player frustum (radius 3, LOD 0-8, overlap patches);
  a 17x9 ray coverage probe aborts before rendering if tiles are missing (truncated-mesh
  control: 480 uncovered samples, rejected). Clear colour is sky blue, so un-meshed space can't
  pass as dark rock. 16 views of the owner's four-strate plan.
- Explore commandlet: finite XYZ spawn override for traces (Source/, Linux-compiled).
- In-engine four-strate trace NOT obtained: the engine-generated plan fails passage validation
  at a solid centerline voxel (~(-43,-34.5,-448.5) m) while the headless owner_world.json
  plan passes. Engine and headless plans diverge.
- R36 (read-only): order TN -> VS -> FI for native migration; native FI is reachable from the
  adapter but not selected in the owner run, cause unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-27 04:00:49 +01:00
1 parent bd7bba994c
commit 81e2452a3d
24 files changed
+1011 -5

No files matched your search

@@ -0,0 +1,725 @@
#!/usr/bin/env python3
"""Render arrival, departure, and mid-tunnel views for the four-strate owner plan."""
import json
import importlib.util
import argparse
import math
import os
import re
import struct
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l69"
BIN = OUT / "bin"
CAPTURES = Path(__file__).resolve().parent / "captures"
HEADLESS = OUT / "bin/VoxelForgeCoreHeadless-l69"
RENDERER = BIN / "VoxelForgeOffscreenCapture-l69"
PNG_DIR = OUT / "capture-png"
RECIPE_INPUTS = (
("passage-0-1", ROOT / "Standalone/data/owner_world.json",
"surface-world", "SurfaceWorld", "upper"),
("passage-1-2", ROOT / "Standalone/data/owner_world.json",
"surface-world", "FloatingIslands", "upper"),
("passage-2-3", ROOT / "Standalone/data/owner_world.json",
"surface-world", "TunnelNetwork", "upper"),
)
SUBVOXELS_PER_VOXEL = 256.0
RING_AUDIT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py"
spec = importlib.util.spec_from_file_location("ring_profile_l32", RING_AUDIT)
ring_profile = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ring_profile)
for directory in (OUT, BIN, PNG_DIR, CAPTURES):
directory.mkdir(parents=True, exist_ok=True)
ENV = os.environ.copy()
ENV["LP_NUM_THREADS"] = "12"
ENV["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
PARSER = argparse.ArgumentParser(description=__doc__)
PARSER.add_argument("--scene", choices=[value[0] for value in RECIPE_INPUTS])
PARSER.add_argument("--view", choices=("arrival-mouth", "departure-mouth", "mid-tunnel"))
OPTIONS = PARSER.parse_args()
def run(name, command):
log = OUT / f"capture-{name}.log"
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", *map(str, command)]
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, env=ENV, stdout=stream,
stderr=subprocess.STDOUT, timeout=1740, check=False)
elapsed = time.monotonic() - started
print(f"[L69CaptureRun] step={name} elapsed={elapsed:.1f}s exit={result.returncode} log={log}",
flush=True)
if result.returncode != 0:
raise SystemExit(f"{name} failed; see {log}")
return log
def add(a, b):
return tuple(x + y for x, y in zip(a, b))
def sub(a, b):
return tuple(x - y for x, y in zip(a, b))
def mul(a, scale):
return tuple(x * scale for x in a)
def norm(a):
length = math.sqrt(sum(x * x for x in a))
if length <= 1.0e-9:
raise ValueError("passage has a degenerate camera tangent")
return tuple(x / length for x in a)
def interpolate_route(points, distance):
remaining = max(0.0, distance)
for a, b in zip(points, points[1:]):
delta = sub(b[0], a[0])
length = math.sqrt(sum(x * x for x in delta))
if length <= 1.0e-9:
continue
if remaining <= length:
t = remaining / length
pos = tuple(a[0][axis] + delta[axis] * t for axis in range(3))
radius = a[1] + (b[1] - a[1]) * t
return pos, radius
remaining -= length
return points[-1]
def route_frame(points, distance):
center, radius = interpolate_route(points, distance)
before, _ = interpolate_route(points, max(0.0, distance - 0.25))
after, _ = interpolate_route(points, distance + 0.25)
tangent = norm(sub(after, before))
horizontal = norm((tangent[0], tangent[1], 0.0))
side = (-horizontal[1], horizontal[0], 0.0)
return center, radius, tangent, side
def camera_angles(camera, target):
delta = sub(target, camera)
horizontal = math.hypot(delta[0], delta[1])
yaw = math.degrees(math.atan2(delta[0], delta[1]))
pitch = math.degrees(math.atan2(delta[2], horizontal))
return yaw, pitch
def fmt(values):
return ",".join(f"{value:.6f}" for value in values)
def load_reference_cameras():
log = ROOT / "Saved/Linux/l34/run-capture-standard-set-final-radius3.log"
pattern = re.compile(
r"^\[L34CaptureCamera\] view=(\S+).*?camera_m=\(([^)]*)\) "
r"target_m=\(([^)]*)\) clip=(\S+)", re.M)
values = {}
for name, camera, target, clip in pattern.findall(
log.read_text(encoding="utf-8", errors="replace")):
parse = lambda value: tuple(float(part) for part in value.split(","))
values[name] = (parse(camera), parse(target), None if clip == "none" else parse(clip))
return values
REFERENCE_CAMERAS = {}
def terrain_surface_near(mesh, x, y, expected_height):
bounds = (x - 2.0, x + 2.0, y - 2.0, y + 2.0)
_, triangles, _ = ring_profile.read_scene(
str(mesh), None, -1.0e9, 1.0e9, bounds)
buckets = ring_profile.build_buckets(triangles, True, bounds, cell_size=1.0)
hits = []
for dy in range(-4, 5):
for dx in range(-4, 5):
sample_x, sample_y = x + dx * 0.5, y + dy * 0.5
hit = ring_profile.sample_surface(buckets, sample_x, sample_y, True, 1.0,
preferred_height=expected_height)
if hit is not None:
hits.append((math.hypot(dx * 0.5, dy * 0.5),
abs(hit[0] - expected_height), sample_x, sample_y, hit[0]))
if not hits:
return None
distance, _, sample_x, sample_y, height = min(hits)
if distance > 2.0 or abs(height - expected_height) > 8.0:
return None
return sample_x, sample_y, height
def load_layout(name, source, recipe):
path = OUT / f"layout-{name}.json"
run(f"layout-{name}", [HEADLESS, source, "--workers=12", "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=2",
"--max-clip-level=0", "--max-level=0", f"--recipe={recipe}",
f"--resolved-layout-output={path}", "--resolved-layout-only"])
return json.loads(path.read_text(encoding="utf-8"))
def q_to_meters(layout):
return layout["core_voxel_size_um"] / (SUBVOXELS_PER_VOXEL * 1_000_000.0)
def q_position(value, scale):
return tuple(float(component) * scale for component in value)
def q_route(points, scale):
return [(q_position(point["position_q"], scale), point["radius_q"] * scale)
for point in points]
def vector_length(value):
return math.sqrt(sum(component * component for component in value))
def unit(value):
length = vector_length(value)
if length <= 1.0e-9:
raise RuntimeError("capture camera direction is degenerate")
return tuple(component / length for component in value)
def add_scaled(point, direction, scale):
return tuple(point[axis] + direction[axis] * scale for axis in range(3))
def make_player_views(layout):
scale = q_to_meters(layout)
passages = {(int(item["upper_strate_index"]), int(item["lower_strate_index"])): item
for item in layout["passages"]}
p01, p12, p23 = passages[(0, 1)], passages[(1, 2)], passages[(2, 3)]
strate0 = next(item for item in layout["strates"] if int(item["strate_index"]) == 0)
surface_anchors = [item["values"] for item in strate0.get("anchors", [])
if item.get("kind") == "route_point"]
if len(surface_anchors) < 2:
raise RuntimeError("strate 0 has no resolved surface arrival route")
surface_route = [(q_position(value[:3], scale), value[3] * scale)
for value in surface_anchors]
route_specs = [
(0, surface_route, True, surface_route[-1][0], p01, "upper", False),
(1, q_route(p01["control_points"], scale), True,
q_position(p01["lower_mouth_q"], scale), p12, "upper", True),
(2, q_route(p12["control_points"], scale), True,
q_position(p12["lower_mouth_q"], scale), p23, "upper", False),
(3, q_route(p23["control_points"], scale), True,
q_position(p23["lower_mouth_q"], scale), p23, "lower", True),
]
views = {}
for strate, source_route, reverse_route, spawn, departure, departure_end, reverse_departure in route_specs:
route = list(reversed(source_route)) if reverse_route else list(source_route)
if vector_length(sub(route[0][0], spawn)) > 5.0:
raise RuntimeError(f"strate {strate} spawn is {vector_length(sub(route[0][0], spawn)):.2f} m from its arrival route")
path_length = sum(vector_length(sub(b[0], a[0])) for a, b in zip(route, route[1:]))
arrival_target_pos, arrival_target_radius = interpolate_route(route, min(20.0, path_length))
arrival_camera = (spawn[0], spawn[1], spawn[2] + 1.6)
arrival_target = (arrival_target_pos[0], arrival_target_pos[1],
arrival_target_pos[2] - arrival_target_radius + 1.8)
ayaw, apitch = camera_angles(arrival_camera, arrival_target)
views[(strate, "arrival")] = {
"camera": arrival_camera, "target": arrival_target, "yaw": ayaw,
"pitch": apitch, "fov_deg": 90.0,
"description": "player eye at the strate arrival, facing into its route"}
route_distance = min(10.0, path_length * 0.2)
route_center, route_radius, _, _ = route_frame(route, route_distance)
route_camera = (route_center[0], route_center[1],
route_center[2] - route_radius + 1.6)
route_target_pos, route_target_radius = interpolate_route(
route, min(path_length, route_distance + 28.0))
route_target = (route_target_pos[0], route_target_pos[1],
route_target_pos[2] - route_target_radius + 1.8)
ryaw, rpitch = camera_angles(route_camera, route_target)
views[(strate, "route")] = {
"camera": route_camera, "target": route_target, "yaw": ryaw,
"pitch": rpitch, "fov_deg": 90.0,
"description": "player-height view along the passage or surface route"}
mouth_path = q_route(departure["control_points"], scale)
if departure_end == "lower":
mouth_path.reverse()
mouth, mouth_radius, inward, _ = route_frame(mouth_path, 0.0)
departure_distance = min(7.4, sum(vector_length(sub(b[0], a[0]))
for a, b in zip(mouth_path, mouth_path[1:])))
inside, inside_radius, _, _ = route_frame(mouth_path, departure_distance)
departure_camera = (inside[0], inside[1], inside[2] - inside_radius + 1.6)
outward = mul(inward, -1.0)
departure_target = (mouth[0] + outward[0] * 12.0,
mouth[1] + outward[1] * 12.0,
mouth[2] - mouth_radius + 1.8)
dyaw, dpitch = camera_angles(departure_camera, departure_target)
views[(strate, "departure-mouth")] = {
"camera": departure_camera, "target": departure_target,
"yaw": dyaw, "pitch": dpitch, "fov_deg": 90.0,
"description": f"inside the {departure_end} mouth looking out"}
overview_camera = (spawn[0] - 12.0, spawn[1] - 16.0, spawn[2] + 70.0)
overview_target = (spawn[0], spawn[1], spawn[2] - 4.0)
oyaw, opitch = camera_angles(overview_camera, overview_target)
views[(strate, "overview")] = {
"camera": overview_camera, "target": overview_target,
"yaw": oyaw, "pitch": opitch, "fov_deg": 90.0,
"description": "overview from 70 m above the arrival"}
return views
def scene_records(path, include_offsets=False):
records = []
keys = set()
with path.open("rb") as stream:
header = stream.read(60)
if len(header) != 60 or header[:7] != b"VFMSH00":
raise RuntimeError(f"{path} has an invalid mesh header")
version, voxel_um, count = struct.unpack_from("<III", header, 8)
if version not in (1, 2) or voxel_um == 0:
raise RuntimeError(f"{path} has an unsupported mesh header")
for _ in range(count):
start = stream.tell()
key_data = stream.read(16)
count_data = stream.read(20)
if len(key_data) != 16 or len(count_data) != 20:
raise RuntimeError(f"{path} has a truncated tile header")
key = struct.unpack("<iiii", key_data)
_, _, vertices, indices, masks = struct.unpack("<IIIII", count_data)
payload_bytes = vertices * 24 + indices * 4 + masks
if version == 2:
payload_bytes += vertices * 10
stream.seek(payload_bytes, os.SEEK_CUR)
end = stream.tell()
if end > path.stat().st_size:
raise RuntimeError(f"{path} ends inside a tile record")
keys.add(key)
if include_offsets:
records.append((key, start, end))
if stream.read(1):
raise RuntimeError(f"{path} has trailing data after {count} tiles")
return header, voxel_um, keys, records
def tile_keys_at(point, keys, voxel_um):
voxel_m = voxel_um / 1_000_000.0
found = set()
for level in range(9):
extent = 32.0 * (1 << level) * voxel_m
key = (math.floor(point[0] / extent), math.floor(point[1] / extent),
math.floor(point[2] / extent), level)
if key in keys:
found.add(key)
return found
def frame_basis(yaw, pitch):
yaw, pitch = math.radians(yaw), math.radians(pitch)
forward = (math.sin(yaw) * math.cos(pitch),
math.cos(yaw) * math.cos(pitch), math.sin(pitch))
right = (math.cos(yaw), -math.sin(yaw), 0.0)
up = (-math.sin(yaw) * math.sin(pitch),
-math.cos(yaw) * math.sin(pitch), math.cos(pitch))
return unit(forward), unit(right), unit(up)
def coverage_audit(camera, yaw, pitch, fov, keys, voxel_um):
forward, right, up = frame_basis(yaw, pitch)
tan_h = math.tan(math.radians(fov) * 0.5)
tan_v = tan_h * 900.0 / 1600.0
uncovered = []
missing_points = []
rays = 0
distance_samples = tuple(range(1, 200, 4))
for row in range(9):
sy = (row / 8.0) * 2.0 - 1.0
for column in range(17):
sx = (column / 16.0) * 2.0 - 1.0
ray = unit(tuple(forward[axis] + right[axis] * sx * tan_h
+ up[axis] * sy * tan_v for axis in range(3)))
rays += 1
for distance in distance_samples:
point = add_scaled(camera, ray, distance)
if not tile_keys_at(point, keys, voxel_um):
uncovered.append((row, column, distance))
missing_points.append(point)
return {"rays": rays, "samples_per_ray": len(distance_samples),
"missing_samples": len(uncovered), "first_missing": uncovered[:8],
"missing_points": missing_points}
def merge_scene_exports(destination, sources):
unique = {}
reference_header = None
reference_version = None
reference_voxel_um = None
for source in sources:
header, voxel_um, _, records = scene_records(source, include_offsets=True)
version = struct.unpack_from("<I", header, 8)[0]
if reference_header is None:
reference_header, reference_version, reference_voxel_um = header, version, voxel_um
elif version != reference_version or voxel_um != reference_voxel_um:
raise RuntimeError("mesh export fragments use incompatible voxel formats")
for record in records:
unique.setdefault(record[0], (source, record[1], record[2]))
merged_header = bytearray(reference_header)
struct.pack_into("<I", merged_header, 16, len(unique))
temporary = destination.with_suffix(destination.suffix + ".merge-tmp")
with temporary.open("wb") as output_stream:
output_stream.write(merged_header)
for source, start, end in unique.values():
with source.open("rb") as input_stream:
input_stream.seek(start)
remaining = end - start
while remaining:
block = input_stream.read(min(1024 * 1024, remaining))
if not block:
raise RuntimeError(f"{source} changed while merging mesh exports")
output_stream.write(block)
remaining -= len(block)
os.replace(temporary, destination)
return len(unique)
def write_truncated_scene(source, destination, header, keys, records, remove_keys):
retained = [record for record in records if record[0] not in remove_keys]
new_header = bytearray(header)
struct.pack_into("<I", new_header, 16, len(retained))
with source.open("rb") as input_stream, destination.open("wb") as output_stream:
output_stream.write(new_header)
for _, start, end in retained:
input_stream.seek(start)
remaining = end - start
while remaining:
block = input_stream.read(min(1024 * 1024, remaining))
if not block:
raise RuntimeError("source mesh changed while writing the negative control")
output_stream.write(block)
remaining -= len(block)
return {key for key in keys if key not in remove_keys}
def run_negative_control(mesh, stem, view, header, voxel_um, keys, records):
camera = view["camera"]
forward, right, up = frame_basis(view["yaw"], view["pitch"])
remove = set()
for distance in range(89, 110, 4):
center = add_scaled(camera, forward, distance)
for side in (-8.0, 0.0, 8.0):
for vertical in (-8.0, 0.0, 8.0):
point = add_scaled(add_scaled(center, right, side), up, vertical)
remove.update(tile_keys_at(point, keys, voxel_um))
if not remove:
raise RuntimeError("negative-control ray did not intersect any exported tiles")
truncated = OUT / "controls" / f"{stem}.truncated.vfmesh"
truncated.parent.mkdir(parents=True, exist_ok=True)
remaining = write_truncated_scene(mesh, truncated, header, keys, records, remove)
audit = coverage_audit(camera, view["yaw"], view["pitch"], view["fov_deg"],
remaining, voxel_um)
if audit["missing_samples"] == 0:
raise RuntimeError("negative control unexpectedly passed frustum coverage")
print(f"[L69NegativeControl] file={truncated.relative_to(ROOT)} removed_tiles={len(remove)} "
f"missing_samples={audit['missing_samples']} flagged_before_render=1 status=PASS",
flush=True)
def export_capture(strate, label, view, negative_control=False):
stem = f"strate{strate}-{label}"
camera = view["camera"]
yaw, pitch = view["yaw"], view["pitch"]
mesh = OUT / "meshes" / f"{stem}.vfmesh"
png = PNG_DIR / f"{stem}.png"
jpg = CAPTURES / f"{stem}.jpg"
mesh.parent.mkdir(parents=True, exist_ok=True)
source = ROOT / "Standalone/data/owner_world.json"
parts = [mesh]
def export_part(part_name, export_camera):
part = mesh if part_name.endswith("-base") else OUT / "mesh-patches" / f"{part_name}.vfmesh"
part.parent.mkdir(parents=True, exist_ok=True)
run(f"{part_name}-export", [HEADLESS, source, "--workers=12",
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", "--clip-radius=3", "--max-clip-level=8", "--max-level=8",
"--render-distance-chunks=0", "--mesh-coverage", "--capture-skip-walkability",
"--recipe=surface-world",
f"--camera={fmt((*export_camera, yaw, pitch))}", f"--mesh-export={part}"])
return part
export_part(f"{stem}-base", camera)
header, voxel_um, keys, records = scene_records(mesh, include_offsets=negative_control)
audit = coverage_audit(camera, yaw, pitch, view["fov_deg"], keys, voxel_um)
patch_index = 0
while audit["missing_samples"] and patch_index < 3:
patch_index += 1
points = audit["missing_points"]
patch_camera = tuple(sum(point[axis] for point in points) / len(points)
for axis in range(3))
patch_name = f"strate{strate}-{label}-patch{patch_index}"
part = export_part(patch_name, patch_camera)
parts.append(part)
merged_tiles = merge_scene_exports(mesh, parts)
print(f"[L69CoveragePatch] view={stem} patch={patch_index} "
f"camera_m=({fmt(patch_camera)}) merged_tiles={merged_tiles}", flush=True)
header, voxel_um, keys, records = scene_records(mesh, include_offsets=negative_control)
audit = coverage_audit(camera, yaw, pitch, view["fov_deg"], keys, voxel_um)
print(f"[L69Coverage] view={stem} tiles={len(keys)} rays={audit['rays']} "
f"samples_per_ray={audit['samples_per_ray']} distance_m=200 "
f"missing_samples={audit['missing_samples']} "
f"status={'PASS' if audit['missing_samples'] == 0 else 'FAIL'}", flush=True)
if audit["missing_samples"]:
raise SystemExit(f"{stem} lacks tile coverage within the 200 m camera frustum: "
f"{audit['first_missing']}")
if negative_control:
run_negative_control(mesh, stem, view, header, voxel_um, keys, records)
print(f"[L69CaptureCamera] view={stem} camera_m=({fmt(camera)}) "
f"target_m=({fmt(view['target'])}) yaw={yaw:.3f} pitch={pitch:.3f} "
f"fov_deg={view['fov_deg']:.1f} description={view['description']}", flush=True)
render = [RENDERER, mesh, png, "--size=1600x900",
f"--fov-deg={view['fov_deg']:.1f}", f"--camera-m={fmt(camera)}",
f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}",
"--tile-skirts=off", "--shade=readable", "--headlamp=on",
"--height-tint=on", "--tile-edges=off"]
run(f"{stem}-render", render)
run(f"{stem}-jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error",
"-y", "-i", png, "-frames:v", "1", "-q:v", "3", jpg])
size = jpg.stat().st_size if jpg.exists() else 0
if not 0 < size <= 300_000:
raise SystemExit(f"{jpg.name} is missing or exceeds 300 KB (bytes={size})")
print(f"[L69CaptureJPEG] file={jpg.relative_to(ROOT)} bytes={size} status=PASS",
flush=True)
def main():
if not HEADLESS.is_file():
raise SystemExit(f"missing current L68 headless binary: {HEADLESS}")
if not RENDERER.is_file():
raise SystemExit(f"missing L69 offscreen renderer: {RENDERER}")
layout = load_layout("owner-four-strate", ROOT / "Standalone/data/owner_world.json",
"surface-world")
views = make_player_views(layout)
print(f"[L69CapturePlan] strates=4 views={len(views)} frustum_m=200 "
f"clip_radius=3 max_clip_level=8 max_tile_level=8 patches=up-to-3", flush=True)
first = True
for strate in range(4):
for label in ("arrival", "route", "departure-mouth", "overview"):
export_capture(strate, label, views[(strate, label)], negative_control=first)
first = False
print(f"[L69CaptureSet] files={len(list(CAPTURES.glob('*.jpg')))} "
f"directory={CAPTURES.relative_to(ROOT)} status=PASS", flush=True)
if __name__ == "__main__":
main()
def select_passage(layout, mouth_generator, preferred_side):
strata = {int(value["strate_index"]): value for value in layout["strates"]}
preferred_candidates = []
fallback_candidates = []
for passage in layout["passages"]:
upper = strata[int(passage["upper_strate_index"])]
lower = strata[int(passage["lower_strate_index"])]
sides = []
if upper["generator_kind"] == mouth_generator:
sides.append("upper")
if lower["generator_kind"] == mouth_generator:
sides.append("lower")
if preferred_side in sides:
preferred_candidates.append((passage, preferred_side == "lower"))
elif sides:
fallback_candidates.append((passage, sides[0] == "lower"))
candidates = preferred_candidates or fallback_candidates
if not candidates:
raise RuntimeError(f"no passage mouth in generator {mouth_generator}")
return sorted(candidates, key=lambda item: item[0]["stable_id"])[0]
def make_views(layout, passage, reverse):
q_to_m = layout["core_voxel_size_um"] / (SUBVOXELS_PER_VOXEL * 1_000_000.0)
controls = [
(tuple(value * q_to_m for value in point["position_q"]),
point["radius_q"] * q_to_m)
for point in passage["control_points"]
]
lower_mouth = tuple(value * q_to_m for value in passage["lower_mouth_q"])
lower_index = min(range(len(controls)), key=lambda i: sum(
(controls[i][0][axis] - lower_mouth[axis]) ** 2 for axis in range(3)))
path = controls[:lower_index + 1]
if len(path) < 2:
raise RuntimeError(f"{passage['stable_id']} has no mouth-to-mouth route")
if reverse:
path.reverse()
lengths = [math.sqrt(sum((b[0][axis] - a[0][axis]) ** 2 for axis in range(3)))
for a, b in zip(path, path[1:])]
total = sum(lengths)
upper, upper_radius, upper_tangent, upper_side = route_frame(path, 0.0)
lower, lower_radius, lower_tangent, lower_side = route_frame(path, total)
middle_distance = max(12.0, min(total - 12.0, total * 0.4)) if total > 24.0 else total * 0.4
if (int(passage["upper_strate_index"],) == 1
and int(passage["lower_strate_index"]) == 2 and not reverse):
# The old 7 m authored-radius beat sat near the middle of this audit window.
middle_distance = min(total - 12.0, 90.8)
entry_distance = min(6.0, total * 0.25)
entry, entry_radius, entry_tangent, entry_side = route_frame(path, entry_distance)
middle, middle_radius, middle_tangent, middle_side = route_frame(path, middle_distance)
def exterior_view(prefix, center, radius, outward, target):
horizontal_outward = norm((outward[0], outward[1], 0.0))
eye_height = center[2] - radius + 1.6
eye = (center[0] + horizontal_outward[0] * 5.5,
center[1] + horizontal_outward[1] * 5.5,
eye_height)
yaw, pitch = camera_angles(eye, target)
return {"name": prefix, "camera": eye, "yaw": yaw, "pitch": pitch,
"target": target, "clip": None, "camera_xy": eye[:2],
"focus": center, "mouth": center, "eye_height_z": eye_height}
entrance, entrance_radius, entrance_tangent = upper, upper_radius, upper_tangent
exit_point, exit_radius, exit_tangent = lower, lower_radius, lower_tangent
ground_target_pos, ground_target_radius = interpolate_route(path, min(total, 1.0))
ground = exterior_view("ground-mouth", entrance, entrance_radius,
mul(entrance_tangent, -1.0), (ground_target_pos[0], ground_target_pos[1],
ground_target_pos[2] - ground_target_radius + 1.0))
ground["focus"] = entrance
ground["outside_camera"] = True
def cutaway_view(prefix, center, radius, tangent, side, distance, target_distance):
camera = (center[0] - side[0] * 7.0, center[1] - side[1] * 7.0,
center[2] - radius + 1.6)
target_pos, target_radius = interpolate_route(path, target_distance)
target = (target_pos[0], target_pos[1],
target_pos[2] - target_radius + 1.6)
yaw, pitch = camera_angles(camera, target)
plane = (side[0], side[1], 0.0,
-(side[0] * center[0] + side[1] * center[1]))
return {"name": prefix, "camera": camera, "yaw": yaw, "pitch": pitch,
"target": target, "clip": plane, "camera_xy": camera[:2],
"focus": center, "mouth": center}
def inside_mouth_view(prefix, at_start):
endpoint_distance = 0.0 if at_start else total
mouth, _, tangent, mouth_side = route_frame(path, endpoint_distance)
camera_distance = min(total, 7.4) if at_start else max(0.0, total - 5.5)
center, _, _, side = route_frame(path, camera_distance)
camera = (center[0] + side[0] * 2.5,
center[1] + side[1] * 2.5, center[2])
outward = mul(tangent, -1.0) if at_start else tangent
target = (mouth[0] + outward[0] * 8.0 + mouth_side[0] * 2.5,
mouth[1] + outward[1] * 8.0 + mouth_side[1] * 2.5,
mouth[2])
yaw, pitch = camera_angles(camera, target)
return {"name": prefix, "camera": camera, "yaw": yaw, "pitch": pitch,
"target": target, "clip": None, "camera_xy": camera[:2],
"focus": mouth, "mouth": mouth, "outside_camera": False,
"camera_location": "passage-air", "focus_radius": 30.0,
"eye_height_z": camera[2], "fov_deg": 80.0}
# Place each interface view at its true route endpoint. The endpoint camera
# is a side cutaway so the solid terrain in front of an underground mouth
# cannot hide the floor-to-tunnel connection.
arrival_distance = 0.0
arrival_center, arrival_radius, arrival_tangent, arrival_side = route_frame(
path, arrival_distance)
arrival = cutaway_view("arrival", arrival_center, arrival_radius,
arrival_tangent, arrival_side, arrival_distance,
min(total, arrival_distance + 12.0))
arrival["camera_location"] = "passage-air"
arrival["focus_radius"] = 20.0
arrival["yaw"], arrival["pitch"] = camera_angles(
arrival["camera"], arrival["target"])
entry_camera_distance = min(8.0, total * 0.1)
entry_center, entry_radius, entry_tangent, entry_side = route_frame(
path, entry_camera_distance)
# Player-height view from just inside the passage, aimed through its actual
# interface mouth so the floor, bore, and opening share one frame.
entry_camera = (entry_center[0], entry_center[1],
entry_center[2] - entry_radius + 1.6)
entry_target_pos, entry_target_radius = interpolate_route(
path, min(total, entry_distance + 14.0))
entry_target = (entry_target_pos[0], entry_target_pos[1],
entry_camera[2] - 2.5)
entry_yaw, entry_pitch = camera_angles(entry_camera, entry_target)
entry_view = {"name": "rock-entry", "camera": entry_camera,
"yaw": entry_yaw, "pitch": entry_pitch, "target": entry_target,
"clip": None, "camera_xy": entry_camera[:2],
"focus": entry_center, "mouth": entrance, "outside_camera": False,
"camera_location": "passage-air", "focus_radius": 20.0,
"eye_height_z": entry_camera[2]}
middle_view = cutaway_view("mid-tunnel", middle, middle_radius, middle_tangent,
middle_side, middle_distance, min(total, middle_distance + 2.0))
ground["focus_radius"] = 20.0
middle_view["focus_radius"] = 20.0
middle_camera_center, _ = interpolate_route(path, middle_distance)
middle_view["camera"] = (middle_camera_center[0] + middle_side[0] * 0.75,
middle_camera_center[1] + middle_side[1] * 0.75,
middle[2] - middle_radius + 1.6)
middle_target_pos, middle_target_radius = interpolate_route(
path, min(total, middle_distance + 14.0))
middle_view["target"] = (middle_target_pos[0] + middle_side[0] * 2.35,
middle_target_pos[1] + middle_side[1] * 2.35,
middle_target_pos[2] - middle_target_radius + 1.8)
# Keep the camera inside the bore, close to one wall, and look down the
# route so the old beat location is visible at player height.
middle_view["clip"] = None
middle_view["camera_location"] = "passage-air"
middle_view["yaw"], middle_view["pitch"] = camera_angles(
middle_view["camera"], middle_view["target"])
return (ground, entry_view, middle_view, arrival,
inside_mouth_view("inside-mouth-start", True),
inside_mouth_view("inside-mouth-end", False)), total, lower_index
def export_capture(scene_name, source, recipe, passage, endpoint, view):
stem = f"{passage['stable_id'].replace('/', '-')}-{endpoint}"
view["name"] = stem
mesh = OUT / f"{stem}.vfmesh"
png = PNG_DIR / f"{stem}.png"
jpg = CAPTURES / f"{stem}.jpg"
yaw, pitch = view["yaw"], view["pitch"]
camera = view["camera"]
export_camera = view.get("focus", camera)
run(f"{stem}-export", [HEADLESS, source, "--workers=12",
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", "--clip-radius=3", "--max-clip-level=0", "--max-level=0",
"--render-distance-chunks=0", f"--recipe={recipe}",
f"--camera={fmt((*export_camera, yaw, pitch))}", f"--mesh-export={mesh}"])
render_clip = view["clip"]
surface = None
if view.get("outside_camera", False):
surface = terrain_surface_near(mesh, *view["camera_xy"], view["target"][2])
if surface is None:
camera = (view["camera_xy"][0], view["camera_xy"][1], view["eye_height_z"])
else:
camera = (surface[0], surface[1], surface[2] + 1.6)
elif render_clip is not None:
camera = view["camera"]
yaw, pitch = camera_angles(camera, view["target"])
fov_deg = view.get("fov_deg", 80.0)
print(f"[L68CaptureCamera] view={stem} camera_m=({fmt(camera)}) "
f"target_m=({fmt(view['target'])}) "
f"clip={fmt(render_clip) if render_clip is not None else 'none'} fov_deg={fov_deg:.1f}",
flush=True)
render = [RENDERER, mesh, png, "--size=1600x900", f"--fov-deg={fov_deg:.1f}",
f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}",
"--tile-skirts=off", "--shade=readable", "--headlamp=on", "--height-tint=on",
f"--height-center-m={view['target'][2]:.6f}", "--height-range-m=8", "--tile-edges=off"]
if view.get("focus_radius", 24.0) is not None:
render.append(f"--focus-region={fmt((*view['focus'], view.get('focus_radius', 24.0)))}")
if render_clip is not None:
render.append(f"--clip-plane={fmt(render_clip)}")
run(f"{stem}-render", render)
run(f"{stem}-jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error",
"-y", "-i", png, "-frames:v", "1", "-q:v", "4", jpg])
size = jpg.stat().st_size if jpg.exists() else 0
print(f"[L68CaptureJPEG] file={jpg.relative_to(ROOT)} bytes={size} "
f"status={'PASS' if 0 < size <= 300_000 else 'FAIL'}", flush=True)
if not 0 < size <= 300_000:
raise SystemExit(f"{jpg.name} is missing or exceeds 300 KB")
@@ -0,0 +1,54 @@
# Linux69 — owner four-strata captures and spawn trace
## Captures
The L68 exports were limited to a small clip radius and LOD0 (max-clip-level=0, max-level=0). The camera frustum extended beyond those tiles, while the renderer cleared empty pixels to RGB (0.035, 0.055, 0.075). Unmeshed space therefore looked nearly black.
L69 exports use a 90° player-view frustum, radius 3 and LODs through 8. A coverage probe samples a 17×9 ray grid at 50 points per ray, every 4 m from 1 m through 197 m. Nine views needed one overlapping mesh patch. All 16 ended with zero uncovered probe samples. The renderer clear color is now sky blue (RGB (0.30, 0.48, 0.62)). A tile coverage failure aborts before rendering, so a missing export cannot silently become a black capture. This is a sampled coverage check, not a per-pixel proof.
The negative control removes 8 tiles from the strate 0 arrival mesh. It reports 480 uncovered samples and fails before rendering. Control mesh: Saved/Linux/l69/controls/strate0-arrival.truncated.vfmesh.
The coverage probe only answers whether mesh tiles cover its sampled frustum points. No real hole was confirmed in the captures. The engine trace blocker below is a solid passage sample. Blue pixels indicate an unoccluded ray through covered tiles; this round did not add a separate classifier for open sky versus a terrain breach.
All JPEGs are below 300 KB; combined size is 447,288 bytes.
| Strate | View | File | Bytes | What it shows |
|---:|---|---|---:|---|
| 0 | Arrival | [strate0-arrival.jpg](captures/strate0-arrival.jpg) | 40,064 | Player-eye view at the surface arrival, facing into the route. |
| 0 | Route | [strate0-route.jpg](captures/strate0-route.jpg) | 38,567 | Player-height view along the surface route toward the passage. |
| 0 | Departure mouth | [strate0-departure-mouth.jpg](captures/strate0-departure-mouth.jpg) | 35,055 | Inside the upper mouth looking outward. |
| 0 | Overview | [strate0-overview.jpg](captures/strate0-overview.jpg) | 23,395 | Elevated view, 70 m above the arrival, looking down at the route. |
| 1 | Arrival | [strate1-arrival.jpg](captures/strate1-arrival.jpg) | 24,524 | Player-eye view at the strate 1 arrival, facing into its route. |
| 1 | Route | [strate1-route.jpg](captures/strate1-route.jpg) | 29,602 | Player-height view along the passage. |
| 1 | Departure mouth | [strate1-departure-mouth.jpg](captures/strate1-departure-mouth.jpg) | 26,090 | Inside the upper mouth looking outward. |
| 1 | Overview | [strate1-overview.jpg](captures/strate1-overview.jpg) | 25,960 | Elevated view, 70 m above the arrival, looking down at the route. |
| 2 | Arrival | [strate2-arrival.jpg](captures/strate2-arrival.jpg) | 20,339 | Player-eye view at the strate 2 arrival, facing into its route. |
| 2 | Route | [strate2-route.jpg](captures/strate2-route.jpg) | 20,244 | Player-height view along the passage. |
| 2 | Departure mouth | [strate2-departure-mouth.jpg](captures/strate2-departure-mouth.jpg) | 33,125 | Inside the upper mouth looking outward. |
| 2 | Overview | [strate2-overview.jpg](captures/strate2-overview.jpg) | 25,668 | Elevated view, 70 m above the arrival, looking down at the route. |
| 3 | Arrival | [strate3-arrival.jpg](captures/strate3-arrival.jpg) | 24,307 | Player-eye view at the strate 3 arrival, facing into its route. |
| 3 | Route | [strate3-route.jpg](captures/strate3-route.jpg) | 23,966 | Player-height view along the passage. |
| 3 | Departure mouth | [strate3-departure-mouth.jpg](captures/strate3-departure-mouth.jpg) | 35,034 | Inside the lower mouth looking outward. |
| 3 | Overview | [strate3-overview.jpg](captures/strate3-overview.jpg) | 21,348 | Elevated view, 70 m above the arrival, looking down at the route. |
## In-engine spawn trace
The host rebuilt successfully on Linux. The trace harness was prepared for 3 rotated/interleaved trials per strate, spawning at each arrival. No strate 0 attempt produced a valid Core trace; the other strata were not run. Engine metrics are therefore unavailable, including walkable ring, LOD0 ring, full set, worker-seconds, LOD0 p50/p95, and generateTile / mesh-copy / RMC timings.
| Strate | Arrival spawn (m) | Headless whole-view median | Valid engine trials | Walkable ring | LOD0 ring | Full set | Worker-s | LOD0 p50 / p95 | generateTile / mesh copy / RMC |
|---:|---|---:|---:|---:|---:|---:|---:|---|---|
| 0 | (0, 0, -374.939) | 1.074 s | 0/3 (attempts blocked) | N/A | N/A | N/A | N/A | N/A | N/A |
| 1 | (25.837, 3.199, -737.000) | 1.503 s | 0/3 (not run) | N/A | N/A | N/A | N/A | N/A | N/A |
| 2 | (19.166, -14.200, -753.374) | 0.577 s | 0/3 (not run) | N/A | N/A | N/A | N/A | N/A | N/A |
| 3 | (-20.063, -5.068, -900.354) | 0.210 s | 0/3 (not run) | N/A | N/A | N/A | N/A | N/A | N/A |
No engine/headless percentage comparison is available, so no >30% gap can be assessed. The default engine-generated four-strate plan failed passage validation at the solid Core voxel (-172, -138, -1794) (25 cm per voxel; approximately (-43.0, -34.5, -448.5) m). This is a blocked centerline point, not a hole. The saved owner manifest lacked layout_chunk_width_q; regenerating and combining its current SurfaceWorld plan with the saved FloatingIslands plan got past parsing, but UE rejected the island plan bounds. The imported slot is strate 1 chunks [-79, -16], while the live slot resolves to [-93, -64]. A floating-islands-only headless export also failed because it could not resolve a SurfaceWorld strate. I stopped without bypassing the production checks.
## Failed runs and build notes
- Radius 4 exports, with max clip level 8 and then 7, exceeded the 160-million-node shape safety bound. Radius 3 plus overlapping patches covered the views.
- The UE host toolchain failed under the 6 GiB address-space limit during CoreCLR startup, so host build and UE runs used the requested 6 GiB RSS limit. The host build passed after using the local VFHost/PluginDeps plugin mirror; the final incremental build compiled the spawn-coordinate parser.
- The first UE trace launch exposed comma parsing in the new spawn argument. The parser was corrected and the host rebuilt. One RSS-capped launch exited when AndroidFileServer failed to map; disabling that unrelated plugin let the commandlet run. The generated-settings plan hit the solid-centerline failure above. Manifest attempts then encountered the stale schema and stale island bounds; none reached valid spawn tracing.
- No attempted run exceeded 30 minutes. Logs are under Saved/Linux/l69.
No world recipe, geometry, or peer-determinism inputs changed. The only Source/ edit is Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp, adding a finite XYZ spawn override for the trace; it compiled in the Linux host build. Standalone/Source/StreamBenchmarkHeadless.cpp also writes the canonical manifest after plan assembly for trace setup; its Linux rebuild passed. Other capture-only edits are in Standalone/Source/LinuxOffscreenCapture.cpp and Standalone/Source/StreamBenchmarkPipeline.h.
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Run three interleaved UE startup traces at each owner-plan arrival."""
import subprocess
import time
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
HOST = Path("/home/jahni/VFHost")
PLUGIN_SAVED = ROOT / "Saved/Linux/l69"
HOST_PLUGIN_SAVED = HOST / "Plugins/VoxelForge/Saved/Linux/l69"
OWNER_WORLD = ROOT / "Standalone/data/owner_world.json"
OWNER_MANIFEST = HOST_PLUGIN_SAVED / "owner_world_manifest.json"
EDITOR = Path("/home/jahni/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Cmd")
TRACES = PLUGIN_SAVED / "traces"
HOST_SAVED = HOST / "Saved/Linux/l69"
SPAWNS_M = {
0: (0.0, 0.0, -374.939453),
1: (25.836914, 3.199219, -737.0),
2: (19.166016, -14.200195, -753.374023),
3: (-20.0625, -5.068359, -900.353516),
}
ORDER = ((0, 1), (1, 1), (2, 1), (3, 1),
(1, 2), (2, 2), (3, 2), (0, 2),
(2, 3), (3, 3), (0, 3), (1, 3))
def run(strate, trial):
label = f"strate{strate}-trial{trial}"
trace = TRACES / f"{label}.trace.jsonl"
abslog = TRACES / f"{label}.log"
stdout = TRACES / f"{label}.stdout.log"
output = HOST_PLUGIN_SAVED / "output" / label
userdir = HOST_SAVED / label / "User"
zendata = HOST_SAVED / label / "Zen"
ddc = HOST_SAVED / "DDC"
for directory in (output, userdir, zendata, ddc, TRACES):
directory.mkdir(parents=True, exist_ok=True)
spawn_cm = ",".join(f"{value * 100.0:.3f}" for value in SPAWNS_M[strate])
command = [
str(EDITOR), str(HOST / "VFHost.uproject"), "-nullrhi", "-unattended",
"-nop4", "-nosplash", "-NoSound", "-corelimit=12", "-NoZenAutoLaunch",
"-notraceserver", "-nocrashreports", "-ddc=NoZenLocalFallback",
f"-userdir={userdir}", f"-ZenDataPath={zendata}", f"-LocalDataCachePath={ddc}",
"-run=VoxelForgeExplore", "-seed=0", "-archetype=TunnelNetwork", "-slot=0",
"-modes=export", "-gameconfig=1", "-coreworldtest=1", "-ownerfourstrateplan=1",
"-corevoxelsizecm=25", "-coreislandtest=0", "-corefallbacktest=0",
"-coreofftest=0", "-coreplantest=0",
"-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings", "-corehybridmesher=0",
f"-coreworldassembly={OWNER_MANIFEST}",
f"-out={output}",
"-voxel.StreamingCoarseFirst=0", "-voxel.StreamingDynamicNearReserve=1",
"-voxel.StreamingLegacyPendingResultCap=0", "-voxel.CoreRegionConcurrentAdmission=1",
"-voxel.StreamingResultBacklogCap=256", "-voxel.StreamingReadyQueueDepth=8",
"-voxel.StreamingBackgroundWorkerCap=1", "-voxel.CoreColumnCacheDirectMapped=0",
"-voxel.CoreMCGradientMemoization=1", "-voxel.StreamingCollisionLookahead=1",
"-coreworldstreamtrace=1", "-voxel.TestMove=0",
"-voxel.TestMoveSpeedCmPerSecond=0", f"-voxel.StartupTraceSpawnCm={spawn_cm}",
f"-voxel.StartupTraceFile={trace}", "-voxel.StartupTraceUntilEndPlay=1",
"-voxel.StartupTraceMaxTiles=16384", "-voxel.CollisionGate=0",
f"-abslog={abslog}",
]
wrapped = ["taskset", "-c", "0-11", "prlimit", "--rss=6442450944", "--",
"timeout", "1740s", *command]
started = time.monotonic()
with stdout.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, timeout=1740, check=False)
elapsed = time.monotonic() - started
print(f"[L69EngineTrace] label={label} spawn_cm={spawn_cm} elapsed_s={elapsed:.2f} "
f"exit={result.returncode} trace={trace} stdout={stdout}", flush=True)
if result.returncode != 0:
raise SystemExit(f"{label} failed; inspect {stdout} and {abslog}")
if not trace.is_file() or trace.stat().st_size == 0:
raise SystemExit(f"{label} exited successfully but produced no startup trace")
def main():
if not EDITOR.is_file():
raise SystemExit(f"missing UE commandlet executable: {EDITOR}")
HOST_PLUGIN_SAVED.mkdir(parents=True, exist_ok=True)
owner = json.loads(OWNER_WORLD.read_text(encoding="utf-8"))
assembly = owner.get("settings_asset", {}).get("WorldAssemblyManifestJson")
if not isinstance(assembly, str) or not assembly:
raise SystemExit(f"{OWNER_WORLD} has no serialized world-assembly manifest")
OWNER_MANIFEST.write_text(assembly + "\n", encoding="utf-8")
print(f"[L69EngineTracePlan] source={OWNER_WORLD} manifest={OWNER_MANIFEST} "
f"bytes={OWNER_MANIFEST.stat().st_size}", flush=True)
for strate, trial in ORDER:
run(strate, trial)
print("[L69EngineTraceSet] strata=4 trials_each=3 total=12 status=PASS", flush=True)
if __name__ == "__main__":
main()
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB