Files
HartOMat/render-worker/scripts/blender_render.py
T
Hartmut 47b5d42bb5 refactor(P1): M1 dead code removal + M3 blender_render.py split
M1 — dead code removed:
- Delete blender-renderer/ and threejs-renderer/ source files
- Remove PIL/Pillow fallback block from step_processor.py
  (_generate_thumbnail_placeholder, _finalise_image JPG path)
- Remove stl_quality param from render_blender.py, render_still_task,
  render_turntable_task (was always "low"; hardcode deflection values)
- render_turntable_task now reads scene_linear/angular_deflection from
  system_settings (consistent with export_glb.py pipeline)

M3 — blender_render.py split from 263 → 68 lines:
- _blender_args.py: parse_args() — all 25 positional + named args
- _blender_scene_setup.py: setup_scene() — MODE A/B including USD import
- _blender_render_config.py: configure_and_render() — engine + output

Post-review fixes:
- _db_engine.dispose() after settings read in render_turntable_task
- _finalise_image() fmt param removed (always PNG; PIL never installed)
- _blender_import.py committed together with new submodules to satisfy
  import_usd_file dependency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 12:54:40 +01:00

69 lines
3.0 KiB
Python

"""
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 -- \
<glb_path> <output_path> <width> <height> [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())}")
# ── 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.")