"""Clay-render each OBJ in a directory, 3 angles, framed on its bbox. Only files matching *_hand_*.obj are picked up, and outdir MUST be absolute — a relative one makes Blender write outside the tree and silently produce nothing. usage: blender --background --factory-startup --python render_objs.py -- objdir outdir [res] [dist] res square render resolution in px (default 900) dist camera distance as a multiple of the mesh radius (default 2.6; lower = tighter)""" import bpy, sys, math, glob, os from mathutils import Vector argv = sys.argv[sys.argv.index("--") + 1:] objdir, outdir = argv[0], argv[1] RES = int(argv[2]) if len(argv) > 2 else 900 DIST = float(argv[3]) if len(argv) > 3 else 2.6 bpy.ops.wm.read_factory_settings(use_empty=True) scn = bpy.context.scene scn.render.engine = 'BLENDER_EEVEE' if bpy.app.version >= (4, 2) else 'BLENDER_EEVEE_NEXT' scn.render.resolution_x = scn.render.resolution_y = RES mat = bpy.data.materials.new("Clay") mat.use_nodes = True bsdf = mat.node_tree.nodes["Principled BSDF"] bsdf.inputs["Base Color"].default_value = (0.72, 0.55, 0.45, 1.0) bsdf.inputs["Roughness"].default_value = 0.65 for rot, energy in (((50, 0, 30), 3.0), ((-40, 0, -140), 1.2), ((10, 0, 180), 0.8)): sun = bpy.data.objects.new("Sun", bpy.data.lights.new("Sun", 'SUN')) sun.data.energy = energy sun.rotation_euler = tuple(math.radians(a) for a in rot) scn.collection.objects.link(sun) cam = bpy.data.objects.new("Cam", bpy.data.cameras.new("Cam")) cam.data.lens = 60 scn.collection.objects.link(cam) scn.camera = cam for path in sorted(glob.glob(os.path.join(objdir, "*_hand_*.obj"))): bpy.ops.wm.obj_import(filepath=path) obj = bpy.context.selected_objects[0] obj.data.materials.clear() obj.data.materials.append(mat) for p in obj.data.polygons: p.use_smooth = True bb = [obj.matrix_world @ Vector(c) for c in obj.bound_box] ctr = sum(bb, Vector()) / 8 rad = max((v - ctr).length for v in bb) tag = os.path.splitext(os.path.basename(path))[0] # OBJ import is -Z forward +Y up by default: gltf Y-up mesh arrives Z-up in Blender for label, direction in (("palm", Vector((0, -1, -0.25))), ("back", Vector((0, 1, 0.35))), ("side", Vector((-1, -0.3, 0.1)))): d = direction.normalized() cam.location = ctr - d * (rad * DIST) cam.rotation_euler = d.to_track_quat('-Z', 'Y').to_euler() scn.render.filepath = os.path.join(outdir, f"{tag}_{label}.png") bpy.ops.render.render(write_still=True) print("[objr] wrote", scn.render.filepath) bpy.data.objects.remove(obj, do_unlink=True) print("[objr] DONE")