"""Mako: the model beside the user's sheet, view for view (v03, after "this isnt look like the referance picture"). Cycles renders in a bright, soft studio light (the sheet is a lit illustration on white), framed exactly like the sheet's TOP, SIDE, FRONT and REAR panels (as mk_views.py frames them), then laid next to the sheet's panels: previews/compare/lookalike.png. Also studio perspectives (previews/_studio_*.png). Headless, nothing saved: blender.exe -b Tender_Mako.blend --python build/mk_lookalike.py -- [TAG] [samples]""" import bpy, math, os, sys from mathutils import Vector, Matrix HERE = r"D:\HADAL - The Descent\models\tender-mako\build" OUTD = os.path.join(os.path.dirname(HERE), "previews") CMP = os.path.join(OUTD, "compare") os.makedirs(CMP, exist_ok=True) args = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] TAG = args[0] if args else "v04" SAMPLES = int(args[1]) if len(args) > 1 else 64 exec(open(os.path.join(HERE, "mk_measure.py"), encoding="utf-8").read()) for ob in bpy.data.objects: if ob.animation_data: for fc in ob.animation_data.drivers: fc.driver.expression = fc.driver.expression fc.driver.is_valid = True sc = bpy.context.scene sc.render.engine = 'CYCLES' sc.cycles.samples = SAMPLES sc.cycles.use_denoising = True try: prefs = bpy.context.preferences.addons['cycles'].preferences for kind in ('OPTIX', 'CUDA', 'HIP', 'ONEAPI'): try: prefs.compute_device_type = kind except Exception: continue prefs.refresh_devices() if any(d.type == kind for d in prefs.devices): for d in prefs.devices: d.use = d.type == kind sc.cycles.device = 'GPU' break except Exception as ex: print("GPU not set", ex) sc.view_settings.view_transform = 'Standard' # the paint as it is: AgX washes the sheet's orange out to salmon sc.view_settings.look = 'None' sc.view_settings.exposure = -0.22 sc.render.image_settings.file_format = 'PNG' # studio: a bright grey world with a soft gradient, a big key light from front-top, fill and rim w = bpy.data.worlds.new("_studio") w.use_nodes = True nt = w.node_tree bg = nt.nodes["Background"] tc = nt.nodes.new("ShaderNodeTexCoord"); sx = nt.nodes.new("ShaderNodeSeparateXYZ"); mr = nt.nodes.new("ShaderNodeMapRange") cr = nt.nodes.new("ShaderNodeValToRGB") nt.links.new(tc.outputs["Generated"], sx.inputs[0]); nt.links.new(sx.outputs["Z"], mr.inputs["Value"]) mr.inputs["From Min"].default_value = -1.0; mr.inputs["From Max"].default_value = 1.0 nt.links.new(mr.outputs["Result"], cr.inputs["Fac"]); nt.links.new(cr.outputs["Color"], bg.inputs[0]) cr.color_ramp.elements[0].color = (0.30, 0.32, 0.34, 1); cr.color_ramp.elements[1].color = (0.95, 0.97, 1.0, 1) bg.inputs[1].default_value = 0.9 sc.world = w coll = bpy.data.collections.new("_studio"); sc.collection.children.link(coll) def light(name, kind, energy, rot, loc, size=4.0, color=(1, 1, 1)): ld = bpy.data.lights.new(name, kind); ld.energy = energy; ld.color = color if kind == 'AREA': ld.shape = 'DISK'; ld.size = size else: ld.angle = math.radians(8) o = bpy.data.objects.new(name, ld); coll.objects.link(o); o.rotation_euler = rot; o.location = loc return o light("_key", 'SUN', 3.5, (math.radians(35), math.radians(-25), math.radians(-30)), (0, 0, 10)) light("_fill", 'AREA', 480, (math.radians(65), 0, math.radians(140)), (-6, 6, 3), size=8) light("_low", 'AREA', 170, (math.radians(150), 0, 0), (0, 0, -6), size=10) light("_side", 'AREA', 260, (0, math.radians(-90), 0), (9, 0.8, 0.6), size=8) # v04 round 2: the side the SIDE camera sees cl = light("_cockpit", 'AREA', 110, (math.radians(180), 0, 0), (0, Yi(240.0), 0.62), size=0.9, color=(0.92, 0.97, 1.0)) # v04: the cockpit lit, as the sheet shows it cl.visible_camera = False; cl.visible_glossy = False; cl.visible_transmission = False for c in ("MAKO | Collision (UCX)", "MAKO | Sockets"): if bpy.data.collections.get(c): bpy.data.collections[c].hide_render = True cam = bpy.data.objects.new("_lk_cam", bpy.data.cameras.new("_lk_cam")); coll.objects.link(cam) sc.camera = cam cam.data.clip_start = 0.01; cam.data.clip_end = 100 FS = 0.0070 VIEWS = { "top": ((30, 40, 970, 520), S, lambda bx, by: Vector(((by - TOP_CY) * S, Yi(bx), 0)), (0, 1, 0), (-1, 0, 0), (0, 0, -1)), "side": ((30, 530, 970, 1065), S, lambda bx, by: Vector((0, Yi(bx), Zi(by))), (0, 1, 0), (0, 0, 1), (-1, 0, 0)), "front": ((970, 30, 1410, 530), FS, lambda bx, by: Vector(((bx - 1185) * FS, 0, 1.13 + (150 - by) * FS)), (1, 0, 0), (0, 0, 1), (0, 1, 0)), "rear": ((960, 545, 1410, 1030), FS, lambda bx, by: Vector((-(bx - 1188) * FS, 0, 1.13 + (645 - by) * FS)), (-1, 0, 0), (0, 0, 1), (0, -1, 0)), } K = 1 sc.render.film_transparent = True sc.render.image_settings.color_mode = 'RGBA' cam.data.type = 'ORTHO' for name, (box, mpp, cfun, right, up, fwd) in VIEWS.items(): x0, y0, x1, y1 = box wpx, hpx = (x1 - x0), (y1 - y0) c = cfun((x0 + x1) / 2, (y0 + y1) / 2) R, U, F = Vector(right), Vector(up), Vector(fwd) m = Matrix((R, U, -F)).transposed().to_4x4() m.translation = c - F * 20 cam.matrix_world = m sc.render.resolution_x, sc.render.resolution_y = wpx * 2, hpx * 2 sc.render.resolution_percentage = 100 cam.data.ortho_scale = max(wpx, hpx) * mpp sc.render.filepath = os.path.join(CMP, "lk_%s.png" % name) bpy.ops.render.render(write_still=True) # perspectives in the studio light (left out with the argument 'views': a quick shape check) if 'views' in args: print('LOOKALIKE RENDERS OK (views only)') raise SystemExit(0) sc.render.film_transparent = False sc.render.image_settings.color_mode = 'RGB' cam.data.type = 'PERSP' SHOTS = {"hero": ((6.6, -7.4, 3.6), (0.0, 0.2, 0.25), 50), "ref": ((-6.4, -7.0, 4.4), (0.0, 0.3, 0.25), 50), "side": ((12.0, 1.4, 0.35), (0.0, 1.4, 0.2), 50), "front": ((1.0, -10.0, 0.9), (0.0, -1.0, 0.3), 50), "stern": ((-5.4, 9.0, 2.6), (0.0, 2.2, 0.2), 50), "clips": ((1.9, -2.3, 2.6), (0.0, -0.1, 1.05), 50), "cockpit": ((1.35, -3.3, 1.05), (0.0, -0.7, 0.0), 30)} sc.render.resolution_x, sc.render.resolution_y = 1400, 900 for name, (loc, at, lens) in SHOTS.items(): cam.location = loc cam.rotation_euler = (Vector(at) - Vector(loc)).to_track_quat('-Z', 'Y').to_euler() cam.data.lens = lens sc.render.filepath = os.path.join(OUTD, "%s_studio_%s.png" % (TAG, name)) bpy.ops.render.render(write_still=True) print("LOOKALIKE RENDERS OK")