wip(assembler): M3 round 1 -- native FloatingIslands recipe in Core (standalone)
Bounds 517 boxes, 0 missed, negative control caught; capsule route 1044 centers, raised-route control fails; W1/W8/W16 stable (field bf3ce7da876431e2); 39 ms native vs 64 ms legacy. My review: islands read as thin flat pads with a radial pinch under them, not rock; next round fixes the look. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
4661a445be
commit
53a1e69af7
16 files changed
+2971
-1
No files matched your search
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Small CPU-only matched views of the legacy and native FloatingIslands fields."""
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import json
|
||||
import math
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
RESULTS = ROOT / "results"
|
||||
CAPTURES = RESULTS / "captures"
|
||||
CAPTURES.mkdir(parents=True, exist_ok=True)
|
||||
WIDTH, HEIGHT = 320, 226
|
||||
BACKGROUND = (236, 240, 242)
|
||||
LEGACY_COLOR = (133, 127, 105)
|
||||
NATIVE_COLOR = (78, 135, 151)
|
||||
ROUTE_COLOR = (223, 72, 45)
|
||||
TILE_COLOR = (178, 60, 52)
|
||||
LOD_COLOR = (211, 136, 45)
|
||||
Q = 256.0
|
||||
|
||||
|
||||
def lower_priority() -> None:
|
||||
if hasattr(ctypes, "windll"):
|
||||
ctypes.windll.kernel32.SetPriorityClass(ctypes.windll.kernel32.GetCurrentProcess(), 0x00004000)
|
||||
|
||||
|
||||
def read_mesh(path: Path):
|
||||
raw = path.read_bytes()
|
||||
if len(raw) < 8 or raw[:4] != b"VFMI":
|
||||
raise RuntimeError(f"invalid field mesh: {path}")
|
||||
count = struct.unpack_from("<I", raw, 4)[0]
|
||||
stride = struct.calcsize("<12f")
|
||||
if len(raw) != 8 + count * stride:
|
||||
raise RuntimeError(f"truncated field mesh: {path}")
|
||||
triangles = [struct.unpack_from("<12f", raw, 8 + i * stride) for i in range(count)]
|
||||
vertices = [tri[0:3] for tri in triangles]
|
||||
vertices.extend(tri[3:6] for tri in triangles)
|
||||
vertices.extend(tri[6:9] for tri in triangles)
|
||||
return triangles, vertices
|
||||
|
||||
|
||||
def normalize(vector):
|
||||
size = math.sqrt(sum(value * value for value in vector))
|
||||
return tuple(value / size for value in vector) if size > 1.0e-12 else (0.0, 0.0, 1.0)
|
||||
|
||||
|
||||
def dot(a, b):
|
||||
return sum(x * y for x, y in zip(a, b))
|
||||
|
||||
|
||||
def cross(a, b):
|
||||
return (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0])
|
||||
|
||||
|
||||
def project(points, target, view_dir, scale=None):
|
||||
direction = normalize(view_dir)
|
||||
forward = tuple(-value for value in direction)
|
||||
right = normalize(cross(forward, (0.0, 0.0, 1.0)))
|
||||
up = normalize(cross(right, forward))
|
||||
camera_points = []
|
||||
for point in points:
|
||||
delta = tuple(point[i] - target[i] for i in range(3))
|
||||
camera_points.append((dot(delta, right), dot(delta, up), dot(delta, forward)))
|
||||
if scale is None:
|
||||
span_x = max((p[0] for p in camera_points), default=1.0) - min((p[0] for p in camera_points), default=0.0)
|
||||
span_y = max((p[1] for p in camera_points), default=1.0) - min((p[1] for p in camera_points), default=0.0)
|
||||
scale = 0.86 * min((WIDTH - 30) / max(span_x, 1.0e-6), (HEIGHT - 30) / max(span_y, 1.0e-6))
|
||||
pixels = [
|
||||
(WIDTH * 0.5 + point[0] * scale, 31 + (HEIGHT - 31) * 0.5 - point[1] * scale, point[2])
|
||||
for point in camera_points
|
||||
]
|
||||
return pixels, scale, right, up, forward
|
||||
|
||||
|
||||
def common_scale(mesh_pair, target, view_dir):
|
||||
vertices = mesh_pair[0][1] + mesh_pair[1][1]
|
||||
_, scale, _, _, _ = project(vertices, target, view_dir)
|
||||
return scale
|
||||
|
||||
|
||||
def projected_line(a, b, target, basis, scale):
|
||||
right, up, forward = basis
|
||||
points = []
|
||||
for point in (a, b):
|
||||
delta = tuple(point[i] - target[i] for i in range(3))
|
||||
points.append((WIDTH * 0.5 + dot(delta, right) * scale,
|
||||
31 + (HEIGHT - 31) * 0.5 - dot(delta, up) * scale))
|
||||
return points
|
||||
|
||||
|
||||
def render(mesh, mesh_pair, method, target, view_dir, title, subtitle, *, route=None, guide=None):
|
||||
triangles, vertices = mesh
|
||||
scale = common_scale(mesh_pair, target, view_dir)
|
||||
_, _, right, up, forward = project((), target, view_dir, scale)
|
||||
background = Image.new("RGB", (WIDTH, HEIGHT + 32), BACKGROUND)
|
||||
draw = ImageDraw.Draw(background)
|
||||
draw.rectangle((0, 0, WIDTH - 1, 31), fill=(34, 46, 54))
|
||||
draw.text((8, 4), title, fill=(250, 252, 253))
|
||||
draw.text((8, 17), subtitle, fill=(190, 207, 215))
|
||||
pixels = background.load()
|
||||
zbuffer = [float("inf")] * (WIDTH * (HEIGHT - 31))
|
||||
camera_fill = normalize(view_dir)
|
||||
light = normalize((-0.25 + 1.2 * camera_fill[0],
|
||||
-0.35 + 1.2 * camera_fill[1],
|
||||
0.45 + 1.2 * camera_fill[2]))
|
||||
base = LEGACY_COLOR if method == "legacy" else NATIVE_COLOR
|
||||
for tri in triangles:
|
||||
points = []
|
||||
for offset in (0, 3, 6):
|
||||
index = {0: 0, 3: 1, 6: 2}[offset]
|
||||
vertex = tri[offset:offset + 3]
|
||||
delta = tuple(vertex[i] - target[i] for i in range(3))
|
||||
points.append((WIDTH * 0.5 + dot(delta, right) * scale,
|
||||
31 + (HEIGHT - 31) * 0.5 - dot(delta, up) * scale,
|
||||
dot(delta, forward)))
|
||||
x0 = max(0, int(math.floor(min(p[0] for p in points))))
|
||||
x1 = min(WIDTH - 1, int(math.ceil(max(p[0] for p in points))))
|
||||
y0 = max(31, int(math.floor(min(p[1] for p in points))))
|
||||
y1 = min(HEIGHT, int(math.ceil(max(p[1] for p in points))))
|
||||
if x0 > x1 or y0 > y1:
|
||||
continue
|
||||
(ax, ay, az), (bx, by, bz), (cx, cy, cz) = points
|
||||
denominator = (by - cy) * (ax - cx) + (cx - bx) * (ay - cy)
|
||||
if abs(denominator) < 1.0e-10:
|
||||
continue
|
||||
normal = normalize(tri[9:12])
|
||||
shade = 0.50 + 0.50 * max(0.0, dot(normal, light))
|
||||
color = tuple(max(0, min(255, int(channel * shade))) for channel in base)
|
||||
for y in range(y0, y1 + 1):
|
||||
row = (y - 31) * WIDTH
|
||||
for x in range(x0, x1 + 1):
|
||||
sx, sy = x + 0.5, y + 0.5
|
||||
u = ((by - cy) * (sx - cx) + (cx - bx) * (sy - cy)) / denominator
|
||||
v = ((cy - ay) * (sx - cx) + (ax - cx) * (sy - cy)) / denominator
|
||||
w = 1.0 - u - v
|
||||
if u < -1e-8 or v < -1e-8 or w < -1e-8:
|
||||
continue
|
||||
depth = u * az + v * bz + w * cz
|
||||
index = row + x
|
||||
if depth < zbuffer[index]:
|
||||
zbuffer[index] = depth
|
||||
pixels[x, y] = color
|
||||
|
||||
basis = (right, up, forward)
|
||||
if guide:
|
||||
origin = guide["origin_voxels"]
|
||||
top = guide["top_voxels"]
|
||||
for x_delta, color, label in ((32.0, TILE_COLOR, "32v tile"), (64.0, LOD_COLOR, "64v LOD1")):
|
||||
line = projected_line((origin[0] + x_delta, origin[1] - 190.0, top - 65.0),
|
||||
(origin[0] + x_delta, origin[1] + 190.0, top + 14.0),
|
||||
target, basis, scale)
|
||||
draw.line(line, fill=color, width=2)
|
||||
draw.text((max(2, min(WIDTH - 55, int(line[1][0]) + 2)),
|
||||
max(33, min(HEIGHT - 11, int(line[1][1]) - 12))), label, fill=color)
|
||||
if route:
|
||||
points = []
|
||||
for point in route:
|
||||
delta = tuple(point[i] - target[i] for i in range(3))
|
||||
points.append((WIDTH * 0.5 + dot(delta, right) * scale,
|
||||
31 + (HEIGHT - 31) * 0.5 - dot(delta, up) * scale))
|
||||
if len(points) >= 2:
|
||||
draw.line(points, fill=(250, 248, 239), width=5, joint="curve")
|
||||
draw.line(points, fill=ROUTE_COLOR, width=3, joint="curve")
|
||||
for index in (0, len(points) - 1):
|
||||
x, y = points[index]
|
||||
draw.ellipse((x - 4, y - 4, x + 4, y + 4), fill=(250, 248, 239), outline=ROUTE_COLOR, width=2)
|
||||
return background
|
||||
|
||||
|
||||
def titled_pair(left, right, category):
|
||||
pair = Image.new("RGB", (WIDTH * 2, HEIGHT + 32), (220, 226, 229))
|
||||
pair.paste(left, (0, 0))
|
||||
pair.paste(right, (WIDTH, 0))
|
||||
draw = ImageDraw.Draw(pair)
|
||||
draw.rectangle((0, 0, WIDTH * 2 - 1, HEIGHT + 31), outline=(72, 84, 91), width=1)
|
||||
draw.text((8, HEIGHT + 14), f"{category} | same target, camera and scale", fill=(45, 55, 61))
|
||||
return pair
|
||||
|
||||
|
||||
def main():
|
||||
lower_priority()
|
||||
data = json.loads((RESULTS / "audit.json").read_text(encoding="utf-8"))
|
||||
anchor = data["legacy_anchor_voxels"]
|
||||
route = data["route_points_voxels"]
|
||||
guide = {"origin_voxels": anchor[:2], "top_voxels": anchor[2]}
|
||||
cluster = {
|
||||
"legacy": read_mesh(RESULTS / "cluster_legacy.vfmesh"),
|
||||
"native": read_mesh(RESULTS / "cluster_native.vfmesh"),
|
||||
}
|
||||
detail = {
|
||||
"legacy": read_mesh(RESULTS / "detail_legacy.vfmesh"),
|
||||
"native": read_mesh(RESULTS / "detail_native.vfmesh"),
|
||||
}
|
||||
a = anchor
|
||||
scenes = (
|
||||
("overview", "Cluster overview", cluster, (a[0], a[1], a[2] - 28.0), (8.0, -10.0, 7.0), None, None),
|
||||
("underside", "Island underside", detail, (a[0], a[1], a[2] - 25.0), (8.0, -10.0, -16.0), None, None),
|
||||
("edge_cliff", "Lobed island edge / cliff", detail, (a[0], a[1] + 35.0, a[2] - 8.0), (0.0, 12.0, 2.0), None, None),
|
||||
("tile_lod_seam", "Tile / LOD reference planes", cluster, (a[0], a[1], a[2] - 28.0), (8.0, -10.0, 7.0), None, guide),
|
||||
("route", "Planned route over final field", cluster, (a[0], a[1], a[2] - 26.0), (8.0, -10.0, 7.0), route, None),
|
||||
)
|
||||
pairs = []
|
||||
for key, title, meshes, target, camera, path, grid in scenes:
|
||||
left = render(meshes["legacy"], (meshes["legacy"], meshes["native"]), "legacy",
|
||||
target, camera, title, "Legacy bridge | exact owner field", route=None, guide=grid)
|
||||
route_caption = "Core native | planner route" if path else "Core native | matched field view"
|
||||
right = render(meshes["native"], (meshes["legacy"], meshes["native"]), "native",
|
||||
target, camera, title, route_caption, route=path, guide=grid)
|
||||
pair = titled_pair(left, right, title)
|
||||
out = CAPTURES / f"{key}_matched.jpg"
|
||||
pair.save(out, quality=82, optimize=True)
|
||||
pairs.append(pair)
|
||||
print(f"capture={out.name} bytes={out.stat().st_size} size={pair.size[0]}x{pair.size[1]}")
|
||||
|
||||
sheet = Image.new("RGB", (WIDTH * 2, (HEIGHT + 32) * len(pairs)), (218, 225, 228))
|
||||
for row, pair in enumerate(pairs):
|
||||
sheet.paste(pair, (0, row * (HEIGHT + 32)))
|
||||
sheet_path = CAPTURES / "contact_sheet.jpg"
|
||||
sheet.save(sheet_path, quality=76, optimize=True)
|
||||
oversized = [(path.name, path.stat().st_size) for path in sorted(CAPTURES.glob("*.jpg"))
|
||||
if path.stat().st_size >= 300_000]
|
||||
for path in sorted(CAPTURES.glob("*.jpg")):
|
||||
print(f"capture_size={path.name} bytes={path.stat().st_size}")
|
||||
if oversized:
|
||||
raise RuntimeError(f"captures exceed the 300 KB limit: {oversized}")
|
||||
print(f"contact_sheet={sheet_path.name} bytes={sheet_path.stat().st_size} views=5 matched_pairs=5")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user