wip(assembler): M2 round 2 -- same-LOD borders watertight for MC/hybrid/DC; LOD0/1 transitions still open

Shared halo + deterministic border-quad ownership + T-junction conformance: 625/625 same-LOD for all three.
LOD 2:1 prototype (collar zipper) fails MC 5 / hybrid 38 / DC 40 cases (corners, overhangs).
Hybrid is bit-identical to MC on a sampled native SurfaceWorld cliff; DC rougher. Standalone only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-10 03:56:28 +02:00
1 parent 6953254349
commit a3b7d2015e
34 files changed
+5028 -2048

No files matched your search

@@ -19,6 +19,7 @@ PIECES = (
("riser1m", "one metre riser"),
("cliff_overhang", "cliff ledge / overhang"),
("organic_cave_chamber", "organic cave chamber wall"),
("native_surface_cliff", "sampled SurfaceWorld cliff"),
)
WIDTH, HEIGHT = 288, 216
BG = (239, 242, 244)
@@ -56,14 +57,26 @@ def read_obj(path: Path):
return vertices, normals, faces
def project_mesh(vertices, faces, seam=False):
def project_mesh(vertices, faces, seam=False, target=None):
# One view is reused for every candidate in a row: azimuth 38°, elevation 27°.
target = (0.0, 0.0, 0.0)
if target is None:
if vertices:
bounds_min = tuple(min(v[i] for v in vertices) for i in range(3))
bounds_max = tuple(max(v[i] for v in vertices) for i in range(3))
target = tuple((bounds_min[i] + bounds_max[i]) * 0.5 for i in range(3))
else:
target = (0.0, 0.0, 0.0)
view_dir = normalize((8.0, -10.0, 7.0))
forward = tuple(-c for c in view_dir)
right = normalize(cross(forward, (0.0, 0.0, 1.0)))
up = normalize(cross(right, forward))
scale = 20.2
projected_bounds = []
for point in vertices:
rel = tuple(point[i] - target[i] for i in range(3))
projected_bounds.append((dot(rel, right), dot(rel, up)))
span_x = max((point[0] for point in projected_bounds), default=0.0) - min((point[0] for point in projected_bounds), default=0.0)
span_y = max((point[1] for point in projected_bounds), default=0.0) - min((point[1] for point in projected_bounds), default=0.0)
scale = min(20.2, 0.84 * min((WIDTH - 24) / max(span_x, 1.0e-6), (HEIGHT - 24) / max(span_y, 1.0e-6)))
projected = []
for p in vertices:
rel = tuple(p[i] - target[i] for i in range(3))
@@ -102,7 +115,10 @@ def raster(path: Path, color_by_x=False):
for i in range(1, len(clipped) - 1):
clipped_faces.append(((first, first), (first + i, first + i), (first + i + 1, first + i + 1)))
vertices, normals, faces = clipped_vertices, clipped_normals, clipped_faces
projected = project_mesh(vertices, faces, color_by_x)
bounds_min = tuple(min(v[i] for v in vertices) for i in range(3))
bounds_max = tuple(max(v[i] for v in vertices) for i in range(3))
target = tuple((bounds_min[i] + bounds_max[i]) * 0.5 for i in range(3))
projected = project_mesh(vertices, faces, color_by_x, target)
pixels = Image.new("RGB", (WIDTH, HEIGHT), BG)
zbuf = [float("inf")] * (WIDTH * HEIGHT)
px = pixels.load()
@@ -146,7 +162,7 @@ def raster(path: Path, color_by_x=False):
draw = ImageDraw.Draw(pixels)
# Project a visible segment on the LOD boundary plane, x=0.
seam_world = [(-0.0, y, z) for y, z in ((-4, -4), (4, -4), (4, 4), (-4, 4), (-4, -4))]
seam_points = project_mesh(seam_world, [], True)
seam_points = project_mesh(seam_world, [], True, target)
draw.line([(p[0], p[1]) for p in seam_points], fill=(181, 55, 49), width=2)
return pixels
@@ -174,14 +190,14 @@ def main():
captures.append(row)
seam_row = []
for method in METHODS:
obj = RESULTS / "meshes" / f"lodseam_cliff_overhang_{method}.obj"
image = capture_title(raster(obj, color_by_x=True), "LOD0 to LOD1 cliff seam", f"{method} | fine left, coarse right, x=0 seam")
path = CAPTURES / f"lodseam_{method}.jpg"
obj = RESULTS / "meshes" / f"lodseam_cliff_crossing_{method}.obj"
image = capture_title(raster(obj, color_by_x=True), "Sharp cliff across LOD border", f"{method} | LOD0 left, LOD1 right, x=0")
path = CAPTURES / f"lodseam_cliff_crossing_{method}.jpg"
image.save(path, quality=86, optimize=True)
seam_row.append(image)
captures.append(seam_row)
sheet = Image.new("RGB", (WIDTH * 3, (HEIGHT + 30) * 6), (221, 227, 230))
sheet = Image.new("RGB", (WIDTH * 3, (HEIGHT + 30) * len(captures)), (221, 227, 230))
draw = ImageDraw.Draw(sheet)
for row_index, row in enumerate(captures):
for col_index, image in enumerate(row):