"""Study only: the dock room's deck with its well, the aperture rim and the lamps, as geometry. blender -b --factory-startup models/M_Ship_v2_interior_season2.blend --python build/study_deck.py """ import collections import json import os import sys import bmesh import bpy HERE = os.path.dirname(os.path.abspath(__file__)) out = {} for name in ("Moonpool | deck around open well", "Moonpool | titanium aperture rim", "Moonpool | curved dry basin channel", "Moonpool | recessed docking lamp housing", "Moonpool | docking lamp", "Moonpool | hull lining"): ob = bpy.data.objects.get(name) if ob is None: continue me = ob.data bm = bmesh.new() bm.from_mesh(me) bm.transform(ob.matrix_world) zs = collections.Counter(round(v.co.z, 2) for v in bm.verts) rec = {"verts": len(bm.verts), "faces": len(bm.faces), "tris": sum(1 for f in bm.faces if len(f.verts) == 3), "ngons": sum(1 for f in bm.faces if len(f.verts) > 4), "open_edges": sum(1 for e in bm.edges if len(e.link_faces) == 1), "z_levels": sorted(zs.items())[:40], "uv_layers": [u.name for u in me.uv_layers], "materials": [s.material.name if s.material else None for s in ob.material_slots], "modifiers": [(m.name, m.type) for m in ob.modifiers], "parent": ob.parent.name if ob.parent else None, "collections": [c.name for c in ob.users_collection], "custom": {k: str(ob[k])[:60] for k in ob.keys()}, "location": [round(v, 3) for v in ob.location], "scale": [round(v, 3) for v in ob.scale]} if name.startswith("Moonpool | deck"): # top face outline of the well: vertices at the top level that are not on the outer rectangle ztop = max(v.co.z for v in bm.verts) topv = [v for v in bm.verts if abs(v.co.z - ztop) < 1e-3] inner = [v for v in topv if abs(abs(v.co.y) - 9.03) > 0.02 and abs(v.co.x + 9.99) > 0.02 and abs(v.co.x - 15.51) > 0.02] rec["top_verts"] = len(topv) rec["inner_top_points"] = sorted([[round(v.co.x, 3), round(v.co.y, 3)] for v in inner]) uv = bm.loops.layers.uv.active if uv: f = max((f for f in bm.faces if f.normal.z > 0.9), key=lambda f: f.calc_area()) rec["uv_sample"] = [[[round(l.vert.co.x, 3), round(l.vert.co.y, 3)], [round(l[uv].uv.x, 4), round(l[uv].uv.y, 4)]] for l in f.loops] rec["face_sizes"] = collections.Counter(len(f.verts) for f in bm.faces) nz = collections.Counter(("up" if f.normal.z > 0.9 else "down" if f.normal.z < -0.9 else "side") for f in bm.faces) rec["normals"] = dict(nz) rec["smooth_faces"] = sum(1 for f in bm.faces if f.smooth) if name.startswith("Moonpool | titanium"): pts = sorted([[round(v.co.x, 2), round(v.co.y, 2), round(v.co.z, 2)] for v in bm.verts], key=lambda p: p[2]) rec["highest"] = pts[-12:] rec["lowest"] = pts[:6] out[name] = rec bm.free() mat = bpy.data.materials.get("PBR | Graphite lower hull") if mat and mat.node_tree: out["deck material nodes"] = [(n.bl_idname, n.label or n.name) for n in mat.node_tree.nodes] with open(os.path.join(HERE, "study_deck.json"), "w", encoding="utf-8") as f: json.dump(out, f, indent=1, default=str) print("STUDY written")