modelbeast/server/operators/trellis2_mlx/run.py
m3ultra ac817a9a05 feat(trellis2_mlx): metal PBR baker by default — the fast lane now ships meshgod-grade textures
Fork lane (72s vertex) gains the trellis_mac 2026-07-22 quality fixes:
metal UV-PBR bake with explicit RAM-sized face cap (the fork bakes FULL
density when no target passed — 75GB peak; always pass one), forced-
opaque alpha, texture_size/max_bake_faces/alpha_mode params. Verified
m3ultra: 156s e2e, 26.2GB peak, 391,782-tri candidate + 2.19M-tri raw
master. Vendor checkout synced to fork ba4e2ad.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 19:36:38 +10:00

87 lines
3.0 KiB
Python

import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
# fork checkout resolution: vendored path first, then the per-box conventional
# clones (m3 staging, m1 home) so existing installs work without re-vendoring.
CANDIDATES = [
ROOT / "vendor" / "trellis2-mlx",
Path.home() / "Documents" / "trellis2-mlx-staging" / "trellis2-jourloy",
Path.home() / "trellis2-mlx",
]
FORK = next((c for c in CANDIDATES if (c / "scripts" / "generate_asset.py").exists()), None)
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 FORK is None:
print("ERROR: trellis-2-mrp-mlx not found. Run scripts/install_trellis2_mlx.sh")
sys.exit(1)
py = FORK / ".venv" / "bin" / "python"
if not py.exists():
print(f"ERROR: no venv at {FORK}/.venv — run its scripts/setup_macos.sh")
sys.exit(1)
outdir = Path(a.outdir)
# Meshgod-grade PBR by default (metal baker + capped bake), matching the
# trellis_mac lane's 2026-07-22 quality fixes. The fork's schedule bakes the
# FULL-density mesh when no target is passed (75GB peak, 131MB GLB) — always
# pass an explicit cap; RAM-sized default like trellis_mac (500k ≈ fal parity
# on ≥96GB Studios, 200k mtlbvh-safe elsewhere).
faces = p.get("max_bake_faces")
if faces is None:
mem_gb = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1e9
faces = 500000 if mem_gb >= 96 else 200000
cmd = [
str(py), str(FORK / "scripts" / "generate_asset.py"),
str(Path(a.input[0]).resolve()),
"--output-dir", str(outdir.resolve()),
"--backend", "mlx-experimental",
"--baker", str(p.get("baker", "metal")),
"--pipeline-type", str(p.get("pipeline_type", "1024_cascade")),
"--seed", str(p.get("seed", 0)),
"--texture-size", str(p.get("texture_size", 2048)),
"--pbr-decimation-target", str(faces),
"--force",
]
if p.get("steps"):
cmd += ["--steps", str(p["steps"])]
env = os.environ.copy()
env["HF_HUB_DISABLE_XET"] = "1"
# "auto" keeps o_voxel's baked-alpha BLEND detection (glass etc.); default
# opaque — noisy baked alpha renders solid hair as a speckle veil.
if p.get("alpha_mode"):
env["TRELLIS2_ALPHA_MODE"] = str(p["alpha_mode"])
print("+", " ".join(cmd), flush=True)
res = subprocess.run(cmd, cwd=str(FORK), stdout=sys.stdout,
stderr=subprocess.STDOUT, env=env)
if res.returncode != 0:
sys.exit(res.returncode)
glbs = sorted(outdir.rglob("candidate_pbr.glb")) or sorted(outdir.rglob("*.glb"))
if not glbs:
print("ERROR: no GLB produced")
sys.exit(1)
stem = Path(a.input[0]).stem
outputs = [{"path": str(glbs[0]), "name": f"trellis2mlx_{stem}.glb",
"meta": {"tool": "trellis2_mlx", "baker": p.get("baker", "vertex")}}]
(outdir / "result.json").write_text(json.dumps({"outputs": outputs}))
print("done:", glbs[0], flush=True)