import argparse import json import os import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[3] VENDOR = ROOT / "vendor" / "hunyuan3d-mlx" ap = argparse.ArgumentParser() ap.add_argument("--input", action="append", default=[]) ap.add_argument("--outdir", required=True) ap.add_argument("--params", default="{}") a = ap.parse_args() p = json.loads(a.params) if not a.input: print("ERROR: no input image") sys.exit(1) if not (VENDOR / "generate_e2e.py").exists(): print(f"ERROR: hunyuan3d-mlx not installed at {VENDOR}. Run scripts/install_hunyuan3d_mlx.sh") sys.exit(1) outdir = Path(a.outdir) out_stem = outdir / f"hunyuan3d_{Path(a.input[0]).stem}" cmd = [ sys.executable, "generate_e2e.py", str(Path(a.input[0]).resolve()), "--output", str(out_stem.resolve()), "--steps", str(p.get("steps", 50)), "--guidance", str(p.get("guidance_scale", 7.5)), "--octree-resolution", str(p.get("octree_resolution", 384)), "--texture-size", str(p.get("texture_size", 4096)), "--remesh-faces", str(p.get("remesh_faces", 120000)), "--max-num-view", str(p.get("max_num_view", 6)), "--seed", str(p.get("seed", 42)), ] if p.get("no_texture"): cmd.append("--no-texture") env = os.environ.copy() # xet chunked downloader fails on some HF repos — force the reliable HTTP path # (same fix as trellis_mac / flux_local). env["HF_HUB_DISABLE_XET"] = "1" # guard the torch + compiled-kernel OpenMP collision like sf3d / trellis_mac. env["KMP_DUPLICATE_LIB_OK"] = "TRUE" env.setdefault("OMP_NUM_THREADS", "1") env.setdefault("MKL_NUM_THREADS", "1") print("+", " ".join(cmd), flush=True) print("(first run downloads ~15GB MLX weights: dgrauet/hunyuan3d-2.1-mlx)", flush=True) res = subprocess.run(cmd, cwd=str(VENDOR), stdout=sys.stdout, stderr=subprocess.STDOUT, env=env) if res.returncode != 0: sys.exit(res.returncode) # generate_e2e.py writes .glb (textured, or geometry if --no-texture) and # optionally _shape.glb. Prefer the primary .glb. final = out_stem.with_suffix(".glb") if final.exists(): chosen = final else: glbs = sorted(outdir.rglob("*.glb")) if not glbs: print("ERROR: hunyuan3d_mlx produced no .glb") sys.exit(1) chosen = glbs[0] outputs = [{"path": str(chosen), "name": f"hunyuan3d_{Path(a.input[0]).stem}.glb", "meta": {"tool": "hunyuan3d_mlx"}}] (outdir / "result.json").write_text(json.dumps({"outputs": outputs})) print("done:", chosen, flush=True)