modelbeast/server/operators/blender_convert/run.py
2026-07-12 21:05:22 +10:00

84 lines
2.9 KiB
Python

import argparse
import json
import subprocess
import sys
from pathlib import Path
BLENDER = "/Applications/Blender.app/Contents/MacOS/Blender"
ap = argparse.ArgumentParser()
ap.add_argument("--input", required=True)
ap.add_argument("--outdir", required=True)
ap.add_argument("--params", default="{}")
args = ap.parse_args()
p = json.loads(args.params)
target = p.get("target", "glb")
outdir = Path(args.outdir)
out_name = Path(args.input).stem + "." + target
out_path = outdir / out_name
bpy_script = outdir / "_convert.py"
bpy_script.write_text(f"""
import bpy, json, sys
src = {json.dumps(args.input)}
dst = {json.dumps(str(out_path))}
params = json.loads({json.dumps(json.dumps(p))})
ext = src.rsplit('.', 1)[-1].lower()
bpy.ops.wm.read_factory_settings(use_empty=True)
importers = {{
'glb': lambda: bpy.ops.import_scene.gltf(filepath=src),
'gltf': lambda: bpy.ops.import_scene.gltf(filepath=src),
'obj': lambda: bpy.ops.wm.obj_import(filepath=src),
'fbx': lambda: bpy.ops.import_scene.fbx(filepath=src),
'stl': lambda: bpy.ops.wm.stl_import(filepath=src),
'ply': lambda: bpy.ops.wm.ply_import(filepath=src),
'usd': lambda: bpy.ops.wm.usd_import(filepath=src),
'usdz': lambda: bpy.ops.wm.usd_import(filepath=src),
'usda': lambda: bpy.ops.wm.usd_import(filepath=src),
'dae': lambda: bpy.ops.wm.collada_import(filepath=src),
'abc': lambda: bpy.ops.wm.alembic_import(filepath=src),
}}
if ext == 'blend':
bpy.ops.wm.open_mainfile(filepath=src)
else:
importers[ext]()
scale = params.get('scale', 1.0)
if scale != 1.0:
for ob in bpy.context.scene.objects:
ob.scale = [s * scale for s in ob.scale]
if params.get('apply_transforms', True):
for ob in bpy.context.scene.objects:
ob.select_set(True)
if bpy.context.scene.objects:
bpy.context.view_layer.objects.active = bpy.context.scene.objects[0]
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
tgt = dst.rsplit('.', 1)[-1].lower()
exporters = {{
'glb': lambda: bpy.ops.export_scene.gltf(filepath=dst, export_format='GLB'),
'fbx': lambda: bpy.ops.export_scene.fbx(filepath=dst),
'obj': lambda: bpy.ops.wm.obj_export(filepath=dst),
'stl': lambda: bpy.ops.wm.stl_export(filepath=dst),
'ply': lambda: bpy.ops.wm.ply_export(filepath=dst),
'usd': lambda: bpy.ops.wm.usd_export(filepath=dst),
'usdz': lambda: bpy.ops.wm.usd_export(filepath=dst),
'blend': lambda: bpy.ops.wm.save_as_mainfile(filepath=dst),
}}
exporters[tgt]()
print('exported', dst)
""")
cmd = [BLENDER, "-b", "--python", str(bpy_script)]
print("+", " ".join(cmd), flush=True)
res = subprocess.run(cmd, stdout=sys.stdout, stderr=subprocess.STDOUT)
bpy_script.unlink(missing_ok=True)
if res.returncode != 0 or not out_path.exists():
sys.exit(res.returncode or 1)
(outdir / "result.json").write_text(json.dumps(
{"outputs": [{"path": out_name}]}))
print("done", flush=True)