Framework:
- server/settings.py: key/value settings + secrets, env-injected into operator
subprocesses, secret values masked in API and redacted from job logs
- runner: gpu/cpu/net concurrency lanes, job cancel/retry/delete, multi-input,
graceful 'not installed' error when a tool venv is missing
- db: settings table, asset_ids column (migrated), MODELBEAST_DATA test override
- main: settings + job-action endpoints, inbox watch folder auto-ingest
- store: operators can tag output asset kind (splat, colmap_dataset)
Operators (11 total):
- fal_trellis/trellis2/hunyuan3d/rodin via shared _lib/fal_common.py (verified
params + endpoint ids; recursive result-URL extractor handles per-endpoint keys)
- sf3d, trellis_mac: local MPS image-to-3D, installed with Metal kernels built,
gated on owner HuggingFace auth
- colmap_poses (COLMAP 4.x + GLOMAP global mapper), brush_train (native Metal 3DGS)
- Scan pipeline validated end-to-end through the UI: frames -> colmap (48/48
registered, 0.6px) -> brush -> splat.ply -> in-app SplatViewer
Frontend:
- Settings modal, operator gating (lock + disabled run when requires_env unmet),
job cancel/retry/delete, Compare grid (multi-select side-by-side viewers),
SplatViewer (gaussian-splats-3d, Ply format forced for extensionless URLs)
Tooling: scripts/install_{colmap,brush,sf3d,trellis_mac}.sh; vendor/ + venvs/
gitignored; tests/smoke.sh (12 checks passing); BENCHMARKS.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
1.6 KiB
Python
51 lines
1.6 KiB
Python
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
VENDOR = ROOT / "vendor" / "trellis-mac"
|
|
|
|
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.py").exists():
|
|
print(f"ERROR: trellis-mac not installed at {VENDOR}. Run scripts/install_trellis_mac.sh")
|
|
sys.exit(1)
|
|
|
|
outdir = Path(a.outdir)
|
|
out_stem = outdir / f"trellis2_{Path(a.input[0]).stem}"
|
|
|
|
cmd = [
|
|
sys.executable, "generate.py", str(Path(a.input[0]).resolve()),
|
|
"--pipeline-type", str(p.get("pipeline_type", "1024")),
|
|
"--texture-size", str(p.get("texture_size", 2048)),
|
|
"--seed", str(p.get("seed", 0)),
|
|
"--output", str(out_stem.resolve()),
|
|
]
|
|
if p.get("no_texture"):
|
|
cmd.append("--no-texture")
|
|
|
|
print("+", " ".join(cmd), flush=True)
|
|
res = subprocess.run(cmd, cwd=str(VENDOR), stdout=sys.stdout, stderr=subprocess.STDOUT)
|
|
if res.returncode != 0:
|
|
sys.exit(res.returncode)
|
|
|
|
# generate.py writes <output>.glb (and often a .obj); collect any glb under outdir
|
|
glbs = sorted(outdir.rglob("*.glb"))
|
|
if not glbs:
|
|
print("ERROR: trellis-mac produced no .glb")
|
|
sys.exit(1)
|
|
outputs = [{"path": str(glbs[0]), "name": f"trellis2_{Path(a.input[0]).stem}.glb",
|
|
"meta": {"tool": "trellis_mac"}}]
|
|
(outdir / "result.json").write_text(json.dumps({"outputs": outputs}))
|
|
print("done:", glbs[0], flush=True)
|