On the real FloatingIslands recipe, column caps sit inside anchor-island rock and native bedrock; the old check sampled the route foot. The audit now samples each planned cap against the native field (negative control: a cap moved into air is detected). The audit runner records requested vs actual recipe and exits on mismatch. All 16 passage rows (4 styles x 2 recipes x 2 compilers) pass. The readable shader uses a smooth tint. Interim: FI Gradient displacement capped at width/256 (flat floor, no contour rings); restoring real undulation is L32. Evaluator 19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
79 lines
3.4 KiB
Python
79 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Measure surface heights and normals on an eastward FI floor radial line."""
|
|
import array
|
|
import math
|
|
import struct
|
|
import sys
|
|
|
|
|
|
def read_surface_vertices(path):
|
|
vertices = []
|
|
with open(path, "rb") as stream:
|
|
if stream.read(8) != b"VFMSH001":
|
|
raise SystemExit(f"{path}: invalid scene header")
|
|
version, voxel_um, count = struct.unpack("<3I", stream.read(12))
|
|
stream.read(40)
|
|
if version != 1 or count > 100000:
|
|
raise SystemExit("unsupported scene")
|
|
for _ in range(count):
|
|
key = struct.unpack("<4i", stream.read(16))
|
|
surface_count, skirt_count, vertex_count, index_count, mask_count = struct.unpack(
|
|
"<5I", stream.read(20))
|
|
positions = array.array("f")
|
|
positions.frombytes(stream.read(vertex_count * 12))
|
|
normals = array.array("f")
|
|
normals.frombytes(stream.read(vertex_count * 12))
|
|
indices = array.array("I")
|
|
indices.frombytes(stream.read(index_count * 4))
|
|
masks = stream.read(mask_count)
|
|
if sys.byteorder != "little":
|
|
positions.byteswap()
|
|
normals.byteswap()
|
|
indices.byteswap()
|
|
used = set()
|
|
for tri in range(0, index_count, 3):
|
|
if masks and masks[tri // 3]:
|
|
continue
|
|
for index in indices[tri:tri + 3]:
|
|
if index < surface_count:
|
|
used.add(index)
|
|
for index in used:
|
|
offset = index * 3
|
|
vertices.append((positions[offset], positions[offset + 1],
|
|
positions[offset + 2], normals[offset],
|
|
normals[offset + 1], normals[offset + 2]))
|
|
return voxel_um, vertices
|
|
|
|
|
|
def main():
|
|
scene = sys.argv[1]
|
|
center_x, center_y = 64.0, -32.0
|
|
voxel_um, vertices = read_surface_vertices(scene)
|
|
floor = [v for v in vertices if abs(v[2] + 119.5) < 0.05
|
|
and math.hypot(v[0] - center_x, v[1] - center_y) <= 16.5]
|
|
if not floor:
|
|
raise SystemExit("no floor surface vertices near expected FI bedrock top")
|
|
mean = tuple(sum(v[index] for v in floor) / len(floor) for index in (3, 4, 5))
|
|
normal_rms = math.sqrt(sum(sum((v[index] - mean[axis]) ** 2
|
|
for axis, index in enumerate((3, 4, 5)))
|
|
for v in floor) / len(floor))
|
|
min_z = min(v[2] for v in floor)
|
|
max_z = max(v[2] for v in floor)
|
|
print(f"[RingNormalField] voxel_um={voxel_um} center_m=({center_x:.3f},{center_y:.3f}) "
|
|
f"vertices={len(floor)} height_min_m={min_z:.6f} height_max_m={max_z:.6f} "
|
|
f"mean_normal=({mean[0]:.8f},{mean[1]:.8f},{mean[2]:.8f}) "
|
|
f"normal_rms={normal_rms:.9f}")
|
|
print("[RingNormalRadialLine] radius_m sampled_radius_m height_m nx ny nz")
|
|
for radius in range(1, 17):
|
|
x, y = center_x + radius, center_y
|
|
vertex = min(floor, key=lambda v: (v[0] - x) ** 2 + (v[1] - y) ** 2)
|
|
sampled_radius = math.hypot(vertex[0] - center_x, vertex[1] - center_y)
|
|
if math.hypot(vertex[0] - x, vertex[1] - y) > 0.5:
|
|
raise SystemExit(f"missing near vertex at radius {radius} m")
|
|
print(f"{radius:2d} {sampled_radius:.6f} {vertex[2]:.6f} "
|
|
f"{vertex[3]:.8f} {vertex[4]:.8f} {vertex[5]:.8f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|