feat(core): slab + sparse motifs, slice 1 -- flat field, monolith, platform at 0.52 s (L56); research R25
Motif vocabulary v3: version-1 records keyed by (seed, strate, stable ID) with route keepouts, band clearance and pairwise spacing. A rounded capped-cylinder monolith (r 1.4 m, 24 m tall) and a rounded-box plinth, smooth-unioned into a flatField floor with a 0.55 m geometric rounding (measured fillets 0.73-1.17 m). Exact radial/box interval bounds, a BVH, isTileShapeFree aware (0 brute-force misses, false-proof controls rejected). Final-field route passes; whole-surface audit covers foot, cap and platform edges. Cold whole view 0.524 s. Owner world unchanged. fast/medium/full PASS (Clang and GCC). Research R25 (self-checked): FloatingIslands LOD0 column cache conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
aee1487410
commit
4e64b75044
15 files changed
+1560
-12
No files matched your search
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit LOD0 triangles in the resolved monolith and platform surface ROIs."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import array
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
def solve3(matrix, vector):
|
||||
rows = [list(matrix[i]) + [vector[i]] for i in range(3)]
|
||||
for column in range(3):
|
||||
pivot = max(range(column, 3), key=lambda row: abs(rows[row][column]))
|
||||
if abs(rows[pivot][column]) < 1.0e-10:
|
||||
return None
|
||||
rows[column], rows[pivot] = rows[pivot], rows[column]
|
||||
scale = rows[column][column]
|
||||
rows[column] = [value / scale for value in rows[column]]
|
||||
for row in range(3):
|
||||
if row == column:
|
||||
continue
|
||||
scale = rows[row][column]
|
||||
rows[row] = [rows[row][i] - scale * rows[column][i] for i in range(4)]
|
||||
return [rows[row][3] for row in range(3)]
|
||||
|
||||
|
||||
def fit_radius(points, center_x, center_z, center_y, window=1.05, y_window=1.3):
|
||||
selected = sorted({(round(x, 4), round(z, 4)) for x, y, z in points
|
||||
if abs(y - center_y) <= y_window
|
||||
and abs(x - center_x) <= window and abs(z - center_z) <= window})
|
||||
if len(selected) < 8:
|
||||
return None, len(selected)
|
||||
sums = [0.0] * 9
|
||||
rhs = [0.0] * 3
|
||||
for x, z in selected:
|
||||
row = (x, z, 1.0)
|
||||
target = -(x * x + z * z)
|
||||
for i in range(3):
|
||||
rhs[i] += row[i] * target
|
||||
for j in range(3):
|
||||
sums[i * 3 + j] += row[i] * row[j]
|
||||
solution = solve3([sums[0:3], sums[3:6], [sums[6], sums[7], sums[8]]], rhs)
|
||||
if solution is None:
|
||||
return None, len(selected)
|
||||
a, b, c = solution
|
||||
radius_squared = (a * a + b * b) * 0.25 - c
|
||||
if radius_squared <= 0.0:
|
||||
return None, len(selected)
|
||||
return math.sqrt(radius_squared), len(selected)
|
||||
|
||||
|
||||
def read_mesh(path: Path):
|
||||
triangles = []
|
||||
vertices_all = []
|
||||
selected_tiles = 0
|
||||
with path.open("rb") as stream:
|
||||
prefix = stream.read(60)
|
||||
if len(prefix) != 60 or prefix[:8] != b"VFMSH001":
|
||||
raise ValueError(f"{path}: invalid VFMSH001 header")
|
||||
version, voxel_um, tile_count = struct.unpack_from("<3I", prefix, 8)
|
||||
if version != 1 or voxel_um <= 0 or tile_count > 1_000_000:
|
||||
raise ValueError(f"{path}: invalid mesh version or size")
|
||||
for _ in range(tile_count):
|
||||
header = stream.read(36)
|
||||
if len(header) != 36:
|
||||
raise ValueError(f"{path}: truncated tile header")
|
||||
key = struct.unpack_from("<4i", header, 0)
|
||||
surface_count, _aux_count, vertex_count, index_count, mask_count = \
|
||||
struct.unpack_from("<5I", header, 16)
|
||||
position_bytes = stream.read(vertex_count * 12)
|
||||
normal_bytes = stream.read(vertex_count * 12)
|
||||
index_bytes = stream.read(index_count * 4)
|
||||
masks = stream.read(mask_count)
|
||||
if (len(position_bytes) != vertex_count * 12
|
||||
or len(normal_bytes) != vertex_count * 12
|
||||
or len(index_bytes) != index_count * 4
|
||||
or len(masks) != mask_count):
|
||||
raise ValueError(f"{path}: truncated tile payload")
|
||||
if key[3] != 0:
|
||||
continue
|
||||
selected_tiles += 1
|
||||
positions = array.array("f")
|
||||
positions.frombytes(position_bytes)
|
||||
indices = array.array("I")
|
||||
indices.frombytes(index_bytes)
|
||||
if sys.byteorder != "little":
|
||||
positions.byteswap()
|
||||
indices.byteswap()
|
||||
points = [tuple(float(positions[index * 3 + axis])
|
||||
for axis in range(3)) for index in range(vertex_count)]
|
||||
vertices_all.extend(points)
|
||||
for offset in range(0, index_count - 2, 3):
|
||||
if masks and masks[offset // 3]:
|
||||
continue
|
||||
ids = indices[offset:offset + 3]
|
||||
if max(ids) >= surface_count:
|
||||
continue
|
||||
verts = tuple(points[index] for index in ids)
|
||||
edge_a = tuple(verts[1][axis] - verts[0][axis] for axis in range(3))
|
||||
edge_b = tuple(verts[2][axis] - verts[0][axis] for axis in range(3))
|
||||
cross = (edge_a[1] * edge_b[2] - edge_a[2] * edge_b[1],
|
||||
edge_a[2] * edge_b[0] - edge_a[0] * edge_b[2],
|
||||
edge_a[0] * edge_b[1] - edge_a[1] * edge_b[0])
|
||||
area_twice = math.sqrt(sum(value * value for value in cross))
|
||||
if area_twice <= 1.0e-10:
|
||||
continue
|
||||
center = tuple(sum(vertex[axis] for vertex in verts) / 3.0
|
||||
for axis in range(3))
|
||||
triangles.append((center, area_twice * 0.5, verts))
|
||||
if stream.read(1):
|
||||
raise ValueError(f"{path}: trailing mesh data")
|
||||
return triangles, vertices_all, selected_tiles
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("meshes", type=Path, nargs="+")
|
||||
parser.add_argument("layout", type=Path)
|
||||
parser.add_argument("--view", required=True, choices=("combined",))
|
||||
args = parser.parse_args()
|
||||
layout = json.loads(args.layout.read_text(encoding="utf-8"))
|
||||
motifs = layout.get("stamps", [])
|
||||
monolith = next(row for row in motifs if row["kind"] == 5)
|
||||
platform = next(row for row in motifs if row["kind"] == 6)
|
||||
mesh_rows = [read_mesh(path) for path in args.meshes]
|
||||
triangles = [triangle for row in mesh_rows for triangle in row[0]]
|
||||
vertices = [vertex for row in mesh_rows for vertex in row[1]]
|
||||
tiles = sum(row[2] for row in mesh_rows)
|
||||
if not triangles:
|
||||
raise ValueError("meshes have no LOD0 surface triangles")
|
||||
|
||||
def metres(point):
|
||||
return tuple(value / 1_000_000.0 for value in point)
|
||||
|
||||
mono = metres(monolith["position_um"])
|
||||
floor = monolith["floor_um"] / 1_000_000.0
|
||||
radius = monolith["half_extents_um"][0] / 1_000_000.0
|
||||
half_height = monolith["half_extents_um"][2] / 1_000_000.0
|
||||
mono_round = monolith["rounding_radius_um"] / 1_000_000.0
|
||||
plat = metres(platform["position_um"])
|
||||
half = tuple(value / 1_000_000.0 for value in platform["half_extents_um"])
|
||||
plat_round = platform["rounding_radius_um"] / 1_000_000.0
|
||||
top = floor + 2.0 * half_height
|
||||
|
||||
rois = {
|
||||
"monolith_foot": lambda p: math.hypot(p[0] - mono[0], p[1] - mono[1]) <= radius + 1.4
|
||||
and floor - 1.0 <= p[2] <= floor + 2.1,
|
||||
"monolith_cap": lambda p: math.hypot(p[0] - mono[0], p[1] - mono[1]) <= radius + 1.6
|
||||
and top - 2.0 <= p[2] <= top + 1.1,
|
||||
"platform_edges": lambda p: abs(p[0] - plat[0]) <= half[0] + 1.2
|
||||
and abs(p[1] - plat[1]) <= half[1] + 1.2
|
||||
and floor - 0.8 <= p[2] <= floor + 2.4 * half[2] + 0.8,
|
||||
}
|
||||
roi_counts = {name: sum(1 for center, _area, _verts in triangles if predicate(center))
|
||||
for name, predicate in rois.items()}
|
||||
bad_area = sum(1 for _center, area, _verts in triangles if not math.isfinite(area) or area <= 0.0)
|
||||
foot_radius, foot_points = fit_radius(vertices, mono[0] + radius - mono_round,
|
||||
floor + mono_round, mono[1], window=1.1)
|
||||
cap_radius, cap_points = fit_radius(vertices, mono[0] + radius - mono_round,
|
||||
top - mono_round, mono[1], window=1.1)
|
||||
platform_radius, platform_points = fit_radius(vertices,
|
||||
plat[0] + half[0] - plat_round, floor + 2.0 * half[2] - plat_round,
|
||||
plat[1], window=1.1)
|
||||
radii = (foot_radius, cap_radius, platform_radius)
|
||||
fit_counts = (foot_points, cap_points, platform_points)
|
||||
def surface_pass(tile_count, degenerate_count, rois, measured_radii):
|
||||
covered = all(count >= 8 for count in rois.values())
|
||||
rounded = all(value is not None and value >= 0.5 for value in measured_radii)
|
||||
return tile_count > 0 and degenerate_count == 0 and covered and rounded
|
||||
|
||||
radius_ok = all(value is not None and value >= 0.5 for value in radii)
|
||||
coverage_ok = all(count >= 8 for count in roi_counts.values())
|
||||
missing_roi = dict(roi_counts)
|
||||
missing_roi["monolith_foot"] = 0
|
||||
sharp_edge = list(radii)
|
||||
sharp_edge[0] = 0.25
|
||||
controls_rejected = (
|
||||
not surface_pass(tiles, bad_area, missing_roi, radii)
|
||||
and not surface_pass(tiles, bad_area, roi_counts, sharp_edge)
|
||||
and not surface_pass(0, 0, roi_counts, radii)
|
||||
)
|
||||
passed = surface_pass(tiles, bad_area, roi_counts, radii) and controls_rejected
|
||||
for name, count in roi_counts.items():
|
||||
print(f"[MotifSurfaceROI] view={args.view} name={name} triangles={count} "
|
||||
f"whole_surface=LOD0 status={'PASS' if count >= 8 else 'FAIL'}")
|
||||
if all(value is not None for value in radii):
|
||||
print(f"[MotifFilletMeasure] view={args.view} foot_radius_m={foot_radius:.3f} "
|
||||
f"cap_radius_m={cap_radius:.3f} platform_radius_m={platform_radius:.3f} "
|
||||
f"fit_points={fit_counts} target_min_m=0.500")
|
||||
else:
|
||||
print(f"[MotifFilletMeasure] view={args.view} foot_radius_m={foot_radius} "
|
||||
f"cap_radius_m={cap_radius} platform_radius_m={platform_radius} "
|
||||
f"fit_points={fit_counts} target_min_m=0.500")
|
||||
print(f"[MotifSurfaceAudit] view={args.view} meshes={len(mesh_rows)} lod0_tiles={tiles} triangles={len(triangles)} "
|
||||
f"degenerate={bad_area} feature_rois={len(roi_counts)} covered={coverage_ok} "
|
||||
f"fillets_ge_0.5m={radius_ok} missing_roi_control={'REJECTED' if not surface_pass(tiles, bad_area, missing_roi, radii) else 'FAILED'} "
|
||||
f"sharp_fillet_control={'REJECTED' if not surface_pass(tiles, bad_area, roi_counts, sharp_edge) else 'FAILED'} "
|
||||
f"empty_mesh_control={'REJECTED' if not surface_pass(0, 0, roi_counts, radii) else 'FAILED'} "
|
||||
f"negative_controls={'REJECTED' if controls_rejected else 'FAILED'} "
|
||||
f"status={'PASS' if passed else 'FAIL'}")
|
||||
return 0 if passed else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as error:
|
||||
print(f"[MotifSurfaceAudit] status=FAIL error={error}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the versioned L56 flat-field and sparse-motif fixture."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OWNER = ROOT / "Standalone/data/owner_world.json"
|
||||
OUT_DEFAULT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l56"))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--out", type=Path, default=OUT_DEFAULT / "fixtures")
|
||||
args = parser.parse_args()
|
||||
args.out.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
fixture = json.loads(OWNER.read_text(encoding="utf-8"))
|
||||
fixture["surface_profile_override"] = {
|
||||
"version": 1,
|
||||
"kind": "flatField",
|
||||
"minimum_arena_clearance_m": 0.0,
|
||||
}
|
||||
fixture["motif_family_override"] = {
|
||||
"version": 1,
|
||||
"strate_index": 1,
|
||||
"placement_policy": "seed,strate,stable_id",
|
||||
"motifs": [
|
||||
{
|
||||
"stable_id": "motif/monolith-01-v1",
|
||||
"kind": "cappedCylinder",
|
||||
"radius_m": 1.4,
|
||||
"height_m": 24.0,
|
||||
"route_side_offset_m": 28.0,
|
||||
"placement_radius_m": 4.0,
|
||||
"fillet_radius_m": 0.55,
|
||||
"blend_width_m": 1.6,
|
||||
"minimum_spacing_m": 15.0,
|
||||
"yaw_quarter_turns": 0,
|
||||
"attachment": "seated_floor",
|
||||
},
|
||||
{
|
||||
"stable_id": "motif/platform-01-v1",
|
||||
"kind": "roundedBox",
|
||||
"half_extents_m": [3.2, 2.8, 0.75],
|
||||
"route_side_offset_m": -35.0,
|
||||
"placement_radius_m": 4.0,
|
||||
"fillet_radius_m": 0.55,
|
||||
"blend_width_m": 1.6,
|
||||
"minimum_spacing_m": 15.0,
|
||||
"yaw_quarter_turns": 0,
|
||||
"attachment": "seated_floor",
|
||||
},
|
||||
],
|
||||
}
|
||||
path = args.out / "style-override-motifs-slice1.json"
|
||||
path.write_text(json.dumps(fixture, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L56MotifFixture] path={path} profile=flatField motifs=2 "
|
||||
"placement=seed,strate,stable_id status=PASS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture two checked views of the R24 flat field and sparse motifs."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT_DEFAULT = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l56"))
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
Q_PER_METRE = 1024.0
|
||||
|
||||
sys.path.insert(0, str(ROOT / "Standalone/Tools/verify"))
|
||||
from runner import bounded_run, current_stamp, first_stamp
|
||||
|
||||
|
||||
def fmt(values):
|
||||
return ",".join(f"{value:.6f}" for value in values)
|
||||
|
||||
|
||||
def camera_angles(camera, target):
|
||||
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 read_scene(layout_path):
|
||||
layout = json.loads(layout_path.read_text(encoding="utf-8"))
|
||||
motifs = layout["stamps"]
|
||||
monolith = next(row for row in motifs if row["kind"] == 5)
|
||||
platform = next(row for row in motifs if row["kind"] == 6)
|
||||
route = [tuple(float(value) / Q_PER_METRE for value in anchor["values"][:3])
|
||||
for strate in layout["strates"] for anchor in strate.get("anchors", [])
|
||||
if anchor.get("kind") == "route_point" and len(anchor.get("values", [])) >= 3]
|
||||
if len(route) < 2:
|
||||
raise RuntimeError("motif fixture did not resolve a surface route")
|
||||
mono = tuple(value / 1_000_000.0 for value in monolith["position_um"])
|
||||
dais = tuple(value / 1_000_000.0 for value in platform["position_um"])
|
||||
floor = monolith["floor_um"] / 1_000_000.0
|
||||
center = tuple(sum(point[axis] for point in route) / len(route) for axis in range(3))
|
||||
dx, dy = route[-1][0] - route[0][0], route[-1][1] - route[0][1]
|
||||
length = math.hypot(dx, dy)
|
||||
if length <= 1.0e-6:
|
||||
raise RuntimeError("resolved surface route tangent is degenerate")
|
||||
tx, ty = dx / length, dy / length
|
||||
eye_camera = (mono[0] - tx * 30.0, mono[1] - ty * 30.0, floor + 1.65)
|
||||
eye_target = (mono[0] + tx * 1.5, mono[1] + ty * 1.5, floor + 12.0)
|
||||
scene_x = (center[0] + mono[0] + dais[0]) / 3.0
|
||||
scene_y = (center[1] + mono[1] + dais[1]) / 3.0
|
||||
overview_camera = (scene_x + tx * 45.0, scene_y + ty * 45.0, floor + 43.0)
|
||||
overview_target = (scene_x, scene_y, floor + 5.0)
|
||||
return layout, {
|
||||
"eye": (eye_camera, eye_target, 5),
|
||||
"overview": (overview_camera, overview_target, 7),
|
||||
}
|
||||
|
||||
|
||||
def run_step(name, command, log, *, env, stamped=False):
|
||||
expected = current_stamp() if stamped else None
|
||||
if stamped:
|
||||
probe = bounded_run([str(command[0]), "--print-source-stamp"], timeout=15)
|
||||
if probe is None or probe.returncode != 0 or first_stamp(probe.stdout or "") != expected:
|
||||
raise RuntimeError(f"{name} refused a stale executable: {command[0]}")
|
||||
started = time.monotonic()
|
||||
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)
|
||||
elapsed = time.monotonic() - started
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
print(f"[L56CaptureRun] step={name} elapsed={elapsed:.1f}s "
|
||||
f"exit={result.returncode} log={log}", flush=True)
|
||||
if stamped and (first_stamp(output) != expected or current_stamp() != expected):
|
||||
raise RuntimeError(f"{name} source stamp mismatch; see {log}")
|
||||
if name.endswith("-quality") or name.endswith("-surface-audit"):
|
||||
for line in output.splitlines():
|
||||
if line.startswith(("[CaptureQuality]", "[MotifSurfaceROI]",
|
||||
"[MotifFilletMeasure]", "[MotifSurfaceAudit]")):
|
||||
print(line, flush=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"{name} failed; see {log}")
|
||||
return output
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--out", type=Path, default=OUT_DEFAULT)
|
||||
parser.add_argument("--fixture-dir", type=Path)
|
||||
parser.add_argument("--artifact-dir", type=Path, required=True)
|
||||
parser.add_argument("--headless", type=Path, required=True)
|
||||
parser.add_argument("--renderer", type=Path, required=True)
|
||||
parser.add_argument("--auditor", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
out = args.out.resolve()
|
||||
fixture_dir = (args.fixture_dir or out / "fixtures").resolve()
|
||||
artifact_dir = args.artifact_dir.resolve()
|
||||
png_dir, mesh_dir = out / "capture-png", out / "motif-meshes"
|
||||
for directory in (out, png_dir, mesh_dir, artifact_dir):
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
fixture = fixture_dir / "style-override-motifs-slice1.json"
|
||||
layout = out / "layouts" / "motif-slice1-capsule-reachability-clang.json"
|
||||
if not fixture.is_file() or not layout.is_file():
|
||||
raise RuntimeError("missing fixture or resolved motif records")
|
||||
encoded, views = read_scene(layout)
|
||||
floor = next(row["floor_um"] for row in encoded["stamps"] if row["kind"] == 5) / 1_000_000.0
|
||||
env = os.environ.copy()
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(out / "xdg-cache")
|
||||
|
||||
mesh_paths = []
|
||||
for view, (camera, target, clip_radius) in views.items():
|
||||
yaw, pitch = camera_angles(camera, target)
|
||||
camera_spec = (*camera, yaw, pitch)
|
||||
stem = f"motifs-slice1-{view}"
|
||||
mesh = mesh_dir / f"{stem}.vfmesh"
|
||||
png = png_dir / f"{stem}.png"
|
||||
image = artifact_dir / f"{stem}.jpg"
|
||||
manifest = fixture_dir / f"{stem}-manifest.json"
|
||||
headless = [args.headless, fixture, "--workers=12",
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--force-generate", f"--clip-radius={clip_radius}",
|
||||
"--max-clip-level=0", "--max-level=0", "--render-distance-chunks=0",
|
||||
f"--camera={fmt(camera_spec)}", f"--mesh-export={mesh}",
|
||||
f"--world-assembler-manifest-output={manifest}"]
|
||||
run_step(f"{stem}-export", headless, out / f"capture-{stem}-export.log",
|
||||
env=env, stamped=True)
|
||||
if not mesh.is_file() or mesh.stat().st_size == 0:
|
||||
raise RuntimeError(f"headless export produced no mesh for {view}")
|
||||
focus = (*target, 42.0 if view == "overview" else 34.0)
|
||||
height_center = floor + 12.0 if view == "overview" else camera[2]
|
||||
renderer = [args.renderer, mesh, png, "--size=1600x900", "--fov-deg=72",
|
||||
f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
|
||||
f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable",
|
||||
"--headlamp=on", "--height-tint=on", f"--height-center-m={height_center:.6f}",
|
||||
"--height-range-m=34", "--tile-edges=off", f"--focus-region={fmt(focus)}"]
|
||||
run_step(f"{stem}-render", renderer, out / f"capture-{stem}-render.log",
|
||||
env=env, stamped=True)
|
||||
run_step(f"{stem}-jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel",
|
||||
"error", "-y", "-i", png, "-frames:v", "1", "-q:v", "5", image],
|
||||
out / f"capture-{stem}-jpeg.log", env=env)
|
||||
run_step(f"{stem}-quality", [sys.executable,
|
||||
ROOT / "Standalone/Tools/verify/capture_quality.py", image],
|
||||
out / f"capture-{stem}-quality.log", env=env)
|
||||
mesh_paths.append(mesh)
|
||||
if not image.is_file() or not 0 < image.stat().st_size <= 300_000:
|
||||
raise RuntimeError(f"capture size must be <=300 KB: {image}")
|
||||
print(f"[L56MotifCapture] view={view} file={image} bytes={image.stat().st_size} "
|
||||
f"camera_m=({fmt(camera)}) target_m=({fmt(target)}) status=PASS", flush=True)
|
||||
audit_output = run_step("motifs-surface-audit",
|
||||
[sys.executable, args.auditor, *mesh_paths, layout, "--view=combined"],
|
||||
out / "capture-motifs-surface-audit.log", env=env)
|
||||
if "[MotifSurfaceAudit] view=combined" not in audit_output:
|
||||
raise RuntimeError("surface audit produced no whole-surface summary")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as error:
|
||||
print(f"[L56MotifCapture] status=FAIL error={error}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,56 @@
|
||||
# L56 — R24 slice 1: slab + sparse motifs
|
||||
|
||||
**Result: PASS.** The native family adds one `flatField` stratum with one monument and one low platform. Fins remain on hold.
|
||||
|
||||
## Records and field
|
||||
|
||||
Motif vocabulary v3 resolves two version-1 records with placement keyed by `(seed, strate, stable_id)`, route keepouts, band clearance, and 15 m pairwise spacing. Fixture seed is 0; both records attach to the floor at Z = -529.600 m.
|
||||
|
||||
- `motif/monolith-01-v1`: rounded capped cylinder, radius 1.4 m (within R20 profile 0.8–2 m), height 24 m; route offset +28 m; placement radius 4 m. Resolved center `(3.922, -33.622, -517.600)` m; record seed `1544480482218982`.
|
||||
- `motif/platform-01-v1`: rounded-box plinth, half extents `(3.2, 2.8, 0.75)` m; route offset -35 m; placement radius 4 m. Resolved center `(-37.031, 9.940, -528.850)` m; record seed `2227196533846249`.
|
||||
|
||||
Both use a 0.55 m geometric rounding radius and 1.6 m cubic smooth-union width (`2√2 × 0.55 = 1.556 m`). The monolith uses a rounded capped-cylinder SDF; the plinth uses a rounded-box SDF. Their fills compose into the flat field with smooth min. Resolved AABBs include the smooth-union padding (`W/6 = 0.267 m`):
|
||||
|
||||
- Monolith: `[2.255, 5.588] × [-35.289, -31.956] × [-529.866, -505.333]` m.
|
||||
- Platform: `[-40.498, -33.564] × [6.873, 13.007] × [-529.866, -527.833]` m.
|
||||
|
||||
Cylinder interval bounds use exact radial minima/maxima over each XY box plus the vertical interval; platform bounds use its rounded-box intervals. The BVH contains these bounds and `isTileShapeFree` blocks motif-overlapping tiles. Brute-force checks: 8 boxes, 512 samples, zero misses; 2 all-air and 2 all-solid proofs; both false-proof controls rejected.
|
||||
|
||||
## Reachability and surface audit
|
||||
|
||||
The final-field passage route passed with 33 points, 5,733 capsule samples, 0 blocked samples, 477 support probes, 0 unsupported probes, and 0 grade failures. Motif route and band keepouts passed.
|
||||
|
||||
Whole-surface LOD0 audit covered the monolith foot and cap and platform edges (4,706 tiles; 1,847,639 triangles; 0 degenerate). Fitted radii: foot **0.856 m**, cap **0.728 m**, platform **1.165 m**. Each exceeds 0.5 m. Missing-ROI, sharp-fillet, and empty-mesh controls were rejected.
|
||||
|
||||
## Timing, tiers, and determinism
|
||||
|
||||
Motif cold timing: first tile **0.076 s**, near ring **0.144 s**, whole view **0.524 s** against the 1.75 s target. Final `verify.py` results: fast **PASS, 167.9 s**; medium **PASS, 961.1 s**; full **PASS, 1,135.3 s**. Full included the 30-cell check and five paired cost checks. Clang and GCC builds used FP contraction off; motif hashes matched at W1/W8/W12 for the monolith and both random cameras, including the camera at the monolith.
|
||||
|
||||
## Captures
|
||||
|
||||
Both checked visually; each is a 1600×900 JPEG below 300 KB.
|
||||
|
||||
- Eye-level monument and cap: [motifs-slice1-eye.jpg](captures/motifs-slice1-eye.jpg) — 27,946 bytes.
|
||||
- High oblique overview with platform, monolith, spacing, and route: [motifs-slice1-overview.jpg](captures/motifs-slice1-overview.jpg) — 37,756 bytes.
|
||||
|
||||
## Hashes and diagnostics
|
||||
|
||||
- Source stamp: `5960b761be0110f5d47fea4f56df938e5d70ddba7355d1e2a6d11f15514b217e`.
|
||||
- Fixture SHA-256: `e9a118f1ce3fe247572899075d31ee421f070566ebd6cef4bd1616da9e84e12c`.
|
||||
- Resolved layout: `resolved-layout-v3-fnv64:d8c2af75e948357f`.
|
||||
- Clang binary SHA-256: `c350591d88e706e6e02891428ddb7dd30dc01307fd9ee301798261e462f88129`.
|
||||
- GCC binary SHA-256: `1fa592d7a7300662981fe42ab40345075f901c965d4a3f3811fc79e8e124f0ae`.
|
||||
- Owner world SHA-256: `7b095f9bcc6b90133d5a463f99f0b571133b2c549ce1ecf2d64fe9175e41e183`; diff diagnostic clean.
|
||||
- Eye capture SHA-256: `0266e54e52fcf5eb57b03986fc1dc093437ab36582aec8b2113bc8a43a51bfbe`.
|
||||
- Overview capture SHA-256: `c2998da1570f587b7ce8598993211098da6e4e7d24cf0284345577246def77b4`.
|
||||
|
||||
## Trials and failed runs
|
||||
|
||||
**9/15 trials used**: seven evidence/capture attempts failed and two passed. Failures were limited to the initial medium audit missing the platform ROI, the clip-radius safety guard, camera crops that omitted the platform, an overly restrictive fillet upper bound, and one temporary capture-helper `floor` name error. Logs are under `Saved/Linux/l56/`; the final fast, medium, and full tiers passed.
|
||||
|
||||
## Orchestrator review (Claude)
|
||||
|
||||
Accepted. The stamp matches the final Clang and GCC builds (`5960b761…`); 9 of 15 trials. The monolith reads as an
|
||||
intentional, cleanly rounded monument; the motif stratum runs at **0.52 s** whole view. **Logged for later:** the
|
||||
overview shows a dark crescent where the large arrival shaft (the passage structure from the strate above) meets the
|
||||
floor. Check whether that's a real gap in the field or a render artifact; it isn't part of this round's motifs.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
Reference in new issue
Block a user