"""Pictures of the kit: the sheet view (orthographic top view framed like the user's reference: 4 x 2 m = the sheet's 2000 x 1000 px at 2 mm per px) and a three-quarter view. The studio (floor, lights, cameras) lives in the collection _STUDIO, which the FBX export leaves out. --preview renders in Workbench for a quick shape check.""" import os import math import bpy from mathutils import Vector def studio(): sc = bpy.context.scene st = bpy.data.collections.get("_STUDIO") if st is None: st = bpy.data.collections.new("_STUDIO") sc.collection.children.link(st) def obj(name, data): o = bpy.data.objects.get(name) if o is None: o = bpy.data.objects.new(name, data) st.objects.link(o) return o if "_floor" not in bpy.data.objects: me = bpy.data.meshes.new("_floor") s = 12.0 me.from_pydata([(-s, -s, 0), (s, -s, 0), (s, s, 0), (-s, s, 0)], [], [(0, 1, 2, 3)]) m = bpy.data.materials.new("_floor") m.use_nodes = True b = m.node_tree.nodes["Principled BSDF"] b.inputs["Base Color"].default_value = (0.62, 0.62, 0.64, 1) b.inputs["Roughness"].default_value = 0.85 me.materials.append(m) obj("_floor", me) if "_key" not in bpy.data.objects: L = bpy.data.lights.new("_key", 'AREA') L.energy, L.size, L.shape = 520.0, 3.0, 'SQUARE' o = obj("_key", L) o.location = (-2.0, 1.6, 4.2) o.rotation_euler = (math.radians(28), math.radians(-25), 0) L2 = bpy.data.lights.new("_fill", 'AREA') L2.energy, L2.size = 160.0, 6.0 o2 = obj("_fill", L2) o2.location = (2.5, -2.5, 5.0) o2.rotation_euler = (math.radians(-25), math.radians(22), 0) if "_cam_sheet" not in bpy.data.objects: # the reference is looked at from the front and above (its parts show their -Y ends): 25 degrees off vertical c = bpy.data.cameras.new("_cam_sheet") c.lens, c.clip_end = 85, 100 o = obj("_cam_sheet", c) o.location = (0, -3.99, 8.56) o.rotation_euler = (math.radians(25), 0, 0) c2 = bpy.data.cameras.new("_cam_34") c2.lens = 50 c2.clip_end = 100 o2 = obj("_cam_34", c2) o2.location = (0.0, -4.4, 3.4) o2.rotation_euler = (math.radians(52), 0, 0) w = sc.world or bpy.data.worlds.new("World") sc.world = w w.use_nodes = True w.node_tree.nodes["Background"].inputs["Color"].default_value = (0.55, 0.56, 0.58, 1) w.node_tree.nodes["Background"].inputs["Strength"].default_value = 0.35 return st def run(out, preview=False): sc = bpy.context.scene studio() relight() folder = os.path.join(out, "renders") os.makedirs(folder, exist_ok=True) ucx = bpy.data.collections.get("UCX | collision") if ucx: ucx.hide_render = True sc.render.resolution_x, sc.render.resolution_y = 2000, 1000 sc.render.resolution_percentage = 100 if preview: sc.render.engine = 'BLENDER_WORKBENCH' sh = sc.display.shading sh.light, sh.color_type = 'STUDIO', 'MATERIAL' sh.show_cavity, sh.cavity_type = True, 'BOTH' sh.show_shadows = True tag = "preview" else: sc.render.engine = 'CYCLES' try: import surfaces surfaces.use_gpu() except Exception as ex: print("gpu:", ex) sc.cycles.samples = 160 sc.cycles.use_denoising = True sc.view_settings.view_transform = 'AgX' tag = "cycles" for cam, res in (("_cam_sheet", (2000, 1000)), ("_cam_34", (2400, 1200))): sc.camera = bpy.data.objects[cam] sc.render.resolution_x, sc.render.resolution_y = res sc.render.filepath = os.path.join(folder, "kit_%s_%s.png" % (cam[5:], tag)) bpy.ops.render.render(write_still=True) print("RENDER", sc.render.filepath) def relight(): """the studio as written above, for a file whose studio was made with older values""" for n, e in (("_key", 520.0), ("_fill", 160.0)): if n in bpy.data.objects: bpy.data.objects[n].data.energy = e if bpy.context.scene.world: bpy.context.scene.world.node_tree.nodes["Background"].inputs["Strength"].default_value = 0.35