"""Argument parsing for blender_render.py. Parses positional and named CLI arguments passed after the '--' separator when Blender is invoked as: blender --background --python blender_render.py -- ... """ import json as _json import os import sys from types import SimpleNamespace def parse_args() -> SimpleNamespace: """Parse CLI arguments and return a SimpleNamespace of all render options.""" argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] if len(argv) < 4: print("Usage: blender --background --python blender_render.py -- " " ...") sys.exit(1) def _arg(n, default="", transform=str): return transform(argv[n]) if len(argv) > n and argv[n] else default glb_path = argv[0] output_path = argv[1] width = int(argv[2]) height = int(argv[3]) engine = _arg(4, "cycles", str.lower) samples = _arg(5, None, int) smooth_angle = _arg(6, 30, int) cycles_device = _arg(7, "auto", str.lower) transparent_bg = argv[8] == "1" if len(argv) > 8 else False template_path = _arg(9, "") target_collection = _arg(10, "Product") material_library_path = _arg(11, "") material_map = _json.loads(_arg(12, "{}")) if _arg(12, "{}") else {} part_names_ordered = _json.loads(_arg(13, "[]")) if _arg(13, "[]") else [] lighting_only = argv[14] == "1" if len(argv) > 14 else False shadow_catcher = argv[15] == "1" if len(argv) > 15 else False rotation_x = _arg(16, 0.0, float) rotation_y = _arg(17, 0.0, float) rotation_z = _arg(18, 0.0, float) noise_threshold = _arg(19, "") denoiser = _arg(20, "") denoising_input_passes = _arg(21, "") denoising_prefilter = _arg(22, "") denoising_quality = _arg(23, "") denoising_use_gpu = _arg(24, "") if samples is None: default_samples = os.environ.get("BLENDER_DEFAULT_SAMPLES", "").strip() if default_samples: try: samples = int(default_samples) except ValueError: samples = None if samples is None: samples = 64 if engine == "eevee" else 256 mesh_attributes: dict = {} if "--mesh-attributes" in sys.argv: _idx = sys.argv.index("--mesh-attributes") try: mesh_attributes = _json.loads(sys.argv[_idx + 1]) except Exception: pass usd_path = "" if "--usd-path" in sys.argv: _usd_idx = sys.argv.index("--usd-path") usd_path = sys.argv[_usd_idx + 1] if _usd_idx + 1 < len(sys.argv) else "" focal_length_mm = None if "--focal-length" in sys.argv: _fl_idx = sys.argv.index("--focal-length") focal_length_mm = float(sys.argv[_fl_idx + 1]) if _fl_idx + 1 < len(sys.argv) else None sensor_width_mm_override = None if "--sensor-width" in sys.argv: _sw_idx = sys.argv.index("--sensor-width") sensor_width_mm_override = float(sys.argv[_sw_idx + 1]) if _sw_idx + 1 < len(sys.argv) else None material_override = None if "--material-override" in sys.argv: _mo_idx = sys.argv.index("--material-override") material_override = sys.argv[_mo_idx + 1] if _mo_idx + 1 < len(sys.argv) else None template_inputs: dict = {} if "--template-inputs" in sys.argv: _ti_idx = sys.argv.index("--template-inputs") try: template_inputs = _json.loads(sys.argv[_ti_idx + 1]) if _ti_idx + 1 < len(sys.argv) else {} except Exception: template_inputs = {} if template_path and not os.path.isfile(template_path): print(f"[blender_render] ERROR: template not found: {template_path}") sys.exit(1) return SimpleNamespace( glb_path=glb_path, output_path=output_path, width=width, height=height, engine=engine, samples=samples, smooth_angle=smooth_angle, cycles_device=cycles_device, transparent_bg=transparent_bg, template_path=template_path, target_collection=target_collection, material_library_path=material_library_path, material_map=material_map, part_names_ordered=part_names_ordered, lighting_only=lighting_only, shadow_catcher=shadow_catcher, rotation_x=rotation_x, rotation_y=rotation_y, rotation_z=rotation_z, noise_threshold=noise_threshold, denoiser=denoiser, denoising_input_passes=denoising_input_passes, denoising_prefilter=denoising_prefilter, denoising_quality=denoising_quality, denoising_use_gpu=denoising_use_gpu, mesh_attributes=mesh_attributes, usd_path=usd_path, use_template=bool(template_path), focal_length_mm=focal_length_mm, sensor_width_mm=sensor_width_mm_override, material_override=material_override, template_inputs=template_inputs, )