# Spearfish: renders framed exactly like the user's two screenshots of 2026-10-20 (the fits in canopy_user_2026-10-20.json), # and a look from each seat's eye. Exec'd in Blender. ENGINE may be set before exec ('BLENDER_WORKBENCH' default). # Writes previews/user_views/r_side.png, r_front.png, seat_L.png, seat_R.png; build/compare_user_drawing.py lays the user's # red lines over the first two. import bpy, math, os, json from mathutils import Vector, Matrix HERE = r"D:\HADAL - The Descent\models\tender-spearfish\build" OUTD = os.path.join(os.path.dirname(HERE), "previews", "user_views") os.makedirs(OUTD, exist_ok=True) CU = json.load(open(os.path.join(HERE, "canopy_user_2026-10-20.json"))) eng = globals().get("ENGINE", "BLENDER_WORKBENCH") sc = bpy.context.scene sc.render.engine = eng if eng == 'BLENDER_WORKBENCH': sh = sc.display.shading sh.light = 'STUDIO'; sh.color_type = 'MATERIAL'; sh.show_object_outline = True; sh.show_cavity = True sc.render.film_transparent = False sc.render.image_settings.file_format = 'PNG'; sc.render.image_settings.color_mode = 'RGB' cam = bpy.data.objects.get("_uv_cam") if cam is None: cam = bpy.data.objects.new("_uv_cam", bpy.data.cameras.new("_uv_cam")) pc = bpy.data.collections.get("_preview") or sc.collection pc.objects.link(cam) prev = sc.camera sc.camera = cam done = [] def shoot(name, w, h, loc, right, up, fwd, ortho=None, lens=None): R, U, F = Vector(right), Vector(up), Vector(fwd) m = Matrix((R, U, -F)).transposed().to_4x4() m.translation = loc cam.matrix_world = m sc.render.resolution_x, sc.render.resolution_y, sc.render.resolution_percentage = w, h, 100 if ortho: cam.data.type = 'ORTHO'; cam.data.ortho_scale = ortho else: cam.data.type = 'PERSP'; cam.data.lens = lens; cam.data.sensor_fit = 'HORIZONTAL'; cam.data.sensor_width = 36 cam.data.clip_start = 0.02; cam.data.clip_end = 100 sc.render.filepath = os.path.join(OUTD, name + ".png") bpy.ops.render.render(write_still=True) done.append(sc.render.filepath) for o in bpy.data.objects: if o.name.startswith("UCX_"): o.hide_render = True # the side screenshot: Right Orthographic, 2000 x 728, u = a*y + bu, v = -a*z + bv (seen from +X) f = CU["side_fit"]; a, bu, bv = f["a_px_per_m"], f["bu"], f["bv"] W, H = 2000, 728 yc, zc = (W / 2 - bu) / a, (bv - H / 2) / a shoot("r_side", W, H, Vector((20.0, yc, zc)), (0, 1, 0), (0, 0, 1), (-1, 0, 0), ortho=W / a) # the front screenshot: Front Orthographic, 1684 x 890, u = a*x + bu, v = -a*z + bv (seen from -Y) f = CU["front_fit"]; a, bu, bv = f["a_px_per_m"], f["bu"], f["bv"] W, H = 1684, 890 xc, zc = (W / 2 - bu) / a, (bv - H / 2) / a shoot("r_front", W, H, Vector((xc, -20.0, zc)), (1, 0, 0), (0, 0, 1), (0, 1, 0), ortho=W / a) # each seat's eye, looking straight ahead (an 18 mm lens: about a 90 deg wide view) for nm, tag in (("SOCKET_Seat_L_Eye", "seat_L"), ("SOCKET_Seat_R_Eye", "seat_R")): eye = bpy.data.objects[nm].matrix_world.translation.copy() shoot(tag, 1600, 900, eye, (-1, 0, 0), (0, 0, 1), (0, -1, 0), lens=18) sc.camera = prev result = done