"""find_blender / run_blender_script:model_decimate 与 model_bake 共享的 headless Blender 运行器。""" import json import os import shutil import subprocess import sys DEFAULT_BLENDER = r"D:\tools\Blender\blender-5.0.0-windows-x64\blender.exe" HERE = os.path.dirname(os.path.abspath(__file__)) def find_blender(cli_arg): """Resolve blender.exe from --blender / BLENDER_EXE / BLENDER_PATH / default / PATH.""" candidates = ( cli_arg, os.environ.get("BLENDER_EXE"), os.environ.get("BLENDER_PATH"), DEFAULT_BLENDER, shutil.which("blender"), ) for cand in candidates: if not cand: continue path = cand # Allow BLENDER_PATH to point at a directory containing blender.exe if os.path.isdir(path): nested = os.path.join(path, "blender.exe") if os.path.isfile(nested): return nested continue if os.path.isfile(path): return path sys.exit( "找不到 blender.exe,请用 --blender 指定,或设置环境变量 BLENDER_EXE / BLENDER_PATH" ) def run_blender_script(blender, script, script_args, blender_args=()): """跑 HERE 下的 bl_*.py,返回 MT_SUMMARY JSON;失败或 summary 带 error 则退出。""" cmd = [ blender, "-b", *blender_args, "--factory-startup", "--python", os.path.join(HERE, script), "--", ] + list(script_args) proc = subprocess.run( cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", ) summary = None for line in (proc.stdout or "").splitlines(): if line.startswith("MT_SUMMARY "): summary = json.loads(line[len("MT_SUMMARY ") :]) if proc.returncode != 0 or summary is None or "error" in summary: sys.stderr.write( (proc.stdout or "")[-2000:] + "\n" + (proc.stderr or "")[-2000:] + "\n" ) err = summary["error"] if summary and "error" in summary else script sys.exit("Blender 执行失败:%s" % err) return summary