""" Blender Python script for rendering a GLB file to PNG. Targets Blender 5.0+ (EEVEE / Cycles). Called by Blender: blender --background --python blender_render.py -- \ [engine] [samples] engine: "cycles" (default) | "eevee" Features: - OCC-generated GLB: one mesh per STEP part, already in metres. - Bounding-box-aware camera: object fills ~85 % of the frame. - Isometric-style angle (elevation 28°, azimuth 40°). - Dynamic clip planes. - Standard (non-Filmic) colour management → no grey tint. """ import sys import os import time as _time os.environ["PYTHONUNBUFFERED"] = "1" if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(line_buffering=True) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import bpy # type: ignore[import] from _blender_gpu import activate_gpu from _blender_args import parse_args from _blender_scene_setup import setup_scene from _blender_render_config import configure_and_render # ── Parse arguments ──────────────────────────────────────────────────────────── args = parse_args() print(f"[blender_render] engine={args.engine}, samples={args.samples}, size={args.width}x{args.height}, smooth_angle={args.smooth_angle}°, device={args.cycles_device}, transparent={args.transparent_bg}") print(f"[blender_render] part_names_ordered: {len(args.part_names_ordered)} entries") print(f"[blender_render] {'template='+args.template_path+', collection='+args.target_collection+', lighting_only='+str(args.lighting_only) if args.use_template else 'no template — Mode A'}") if args.material_library_path: print(f"[blender_render] material_library={args.material_library_path}, material_map keys={list(args.material_map.keys())}") if args.template_inputs: print(f"[blender_render] template_inputs={args.template_inputs}") # ── Early GPU activation (must happen BEFORE open_mainfile / Cycles init) ───── _early_gpu_type = activate_gpu(args.cycles_device) # ── Timing harness ───────────────────────────────────────────────────────────── _t0 = _time.monotonic() _timings: dict = {} def _lap(label: str) -> None: now = _time.monotonic() if not hasattr(_lap, "_last"): _lap._last = _t0 delta = now - _lap._last _timings[label] = round(delta, 3) print(f"[blender_render] TIMING {label}={delta:.2f}s (total={now - _t0:.2f}s)", flush=True) _lap._last = now # ── Scene setup + render ─────────────────────────────────────────────────────── setup_scene(args, _lap) configure_and_render(args, _early_gpu_type, args.use_template, _lap) _total = _time.monotonic() - _t0 print(f"[blender_render] TIMING_SUMMARY total={_total:.2f}s | " + " | ".join(f"{k}={v:.2f}s" for k, v in _timings.items()), flush=True) print("[blender_render] Done.")