"""Slice render meshes at the play plane into 2D wall rectangles.

Some level geometry has no 2D collider in Unity yet must be solid for gameplay here: the
perimeter wall ring of the origin game was plain scene decoration, and a
Rigidbody2D ship flies straight through it. This runs inside Blender: it loads the built arena
GLB, samples every triangle of the named families that crosses the slab around the play plane,
rasterises the hits into a 0.5-unit occupancy grid and merges runs into axis-aligned rectangles.

    blender -b --python tools/mesh_walls.py -- build/glb/room-for-9.glb tools/render_walls.json

Coordinates: the GLB is (ux, uy, -uz); Blender imports glTF Y-up as Z-up, so Blender x = Unity x,
Blender z = Unity y (the map's vertical axis), Blender y = Unity z (depth, negative toward the
camera). The play plane is `planeZ` in unity-import.json.
"""
import sys, os, json
import bpy, mathutils

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from import_config import CFG as _CFG  # noqa: E402
argv = sys.argv[sys.argv.index('--') + 1:] if '--' in sys.argv else []
GLB = argv[0] if argv else _CFG['glb']
OUT = argv[1] if len(argv) > 1 else 'tools/render_walls.json'
FAMILIES = tuple(_CFG['wallFamilies'])
PLANE = float(_CFG['planeZ'])
HALF = 0.45           # slab half-depth: the tunnel floor sits at depth 0, keep it out
CELL = 0.5
LIMIT = 72.0

bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.gltf(filepath=GLB)
N = int(2 * LIMIT / CELL)
grid = [[False] * N for _ in range(N)]

def mark(x, y):
    i = int((x + LIMIT) / CELL); j = int((y + LIMIT) / CELL)
    if 0 <= i < N and 0 <= j < N: grid[j][i] = True

count = 0
for o in bpy.data.objects:
    if o.type != 'MESH' or not any(f in o.name for f in FAMILIES): continue
    count += 1
    m = o.matrix_world; me = o.data
    vs = [m @ v.co for v in me.vertices]
    for poly in me.polygons:
        pts = [vs[i] for i in poly.vertices]
        ds = [p.y for p in pts]                    # Blender y = Unity depth
        if max(ds) < PLANE - HALF or min(ds) > PLANE + HALF: continue
        for k in range(1, len(pts) - 1):           # fan-triangulate
            a, b, c = pts[0], pts[k], pts[k + 1]
            steps = 14
            for ui in range(steps + 1):
                for vi in range(steps + 1 - ui):
                    u, v = ui / steps, vi / steps
                    q = a + (b - a) * u + (c - a) * v
                    if PLANE - HALF <= q.y <= PLANE + HALF: mark(q.x, q.z)

# close one-cell sampling holes (dilate then erode), then greedy maximal rectangles
def closed(g):
    d = [[any(g[jj][ii] for jj in range(max(0, j - 1), min(N, j + 2)) for ii in range(max(0, i - 1), min(N, i + 2))) for i in range(N)] for j in range(N)]
    return [[all(d[jj][ii] for jj in range(max(0, j - 1), min(N, j + 2)) for ii in range(max(0, i - 1), min(N, i + 2))) or g[j][i] for i in range(N)] for j in range(N)]
grid = closed(grid)
rects = []
free = [row[:] for row in grid]
for j in range(N):
    for i in range(N):
        if not free[j][i]: continue
        i1 = i
        while i1 < N and free[j][i1]: i1 += 1
        j1 = j + 1
        while j1 < N and all(free[j1][ii] for ii in range(i, i1)): j1 += 1
        for jj in range(j, j1):
            for ii in range(i, i1): free[jj][ii] = False
        rects.append([i, j, i1, j1])
walls = [dict(x0=round(r[0] * CELL - LIMIT, 2), y0=round(r[1] * CELL - LIMIT, 2),
              x1=round(r[2] * CELL - LIMIT, 2), y1=round(r[3] * CELL - LIMIT, 2), name='+'.join(FAMILIES) or 'sliced', tag='Sliced')
         for r in rects]
json.dump(walls, open(OUT, 'w'), indent=1)
print(f"MESH_WALLS meshes {count} cells {sum(map(sum, grid))} rects {len(walls)} -> {OUT}")
