import os import sys import time import json from pathlib import Path import mimetypes import requests BASE = "http://127.0.0.1:8008" OUTPUT_DIR = Path("Tools/output/explorer_3d") OUTPUT_DIR.mkdir(parents=True, exist_ok=True) REF_IMAGE = OUTPUT_DIR / "explorer_ref.png" session = requests.Session() def wait_job(url_path, timeout=3600, interval=3): deadline = time.monotonic() + timeout while time.monotonic() < deadline: r = session.get(f"{BASE}{url_path}", timeout=30) r.raise_for_status() job = r.json() status = job.get("status") stage = job.get("stage", "") progress = job.get("progress", 0) print(f"[{status}] stage={stage} progress={progress}%", flush=True) if status in ("completed", "failed"): if status != "completed" or job.get("error"): raise RuntimeError(f"Job failed: {job.get('error') or job}") return job time.sleep(interval) raise TimeoutError(f"Job timeout: {url_path}") def download(url, out_path): r = session.get(f"{BASE}{url}", timeout=300) r.raise_for_status() out = Path(out_path) if r.content[:4] == b"PK\x03\x04": out = out.with_suffix(".zip") out.parent.mkdir(parents=True, exist_ok=True) out.write_bytes(r.content) print(f"Downloaded: {out} ({len(r.content)} bytes)") return out # 1. Mesh Generation print("=== Step 1: DGX Hunyuan3D Mesh Generation ===") mime = mimetypes.guess_type(REF_IMAGE.name)[0] or "image/png" with REF_IMAGE.open("rb") as f: resp = session.post(f"{BASE}/api/jobs", timeout=120, data={ "engine": "dgx", "mode": "image", "character_type": "humanoid", "prompt": "chibi 3D game character boy adventurer in A-pose", "resolution": 256, "source_faces": 50000, "target_faces": 5000, "texture_size": 1024, "output_format": "fbx" }, files={"image": (REF_IMAGE.name, f, mime)}) if resp.status_code not in (200, 201, 202): raise RuntimeError(f"Create job failed: {resp.status_code} {resp.text}") job = resp.json() job_id = job["id"] print(f"Created Mesh Job ID: {job_id}") mesh_job = wait_job(f"/api/jobs/{job_id}", timeout=1800) print(f"Mesh completed. Ready for motion: {mesh_job.get('ready_for_motion')}") (OUTPUT_DIR / "mesh_job.json").write_text(json.dumps(mesh_job, ensure_ascii=False, indent=2), encoding="utf-8") # 2. MIA Humanoid Rigging print("\n=== Step 2: Make-It-Animatable (MIA) Humanoid Rigging ===") rig_resp = session.post(f"{BASE}/api/jobs/{job_id}/rig", timeout=120, data={ "character_type": "humanoid", "skeleton_template": "auto" }) if rig_resp.status_code not in (200, 201, 202): raise RuntimeError(f"Trigger rig failed: {rig_resp.status_code} {rig_resp.text}") print("Rig triggered:", rig_resp.json()) rig_job = wait_job(f"/api/jobs/{job_id}", timeout=1800) print("Rigging completed!") (OUTPUT_DIR / "rig_job.json").write_text(json.dumps(rig_job, ensure_ascii=False, indent=2), encoding="utf-8") # Download character assets print("\n=== Downloading Character Assets ===") for key, url in rig_job.get("downloads", {}).items(): suffix = ".fbx" if "fbx" in key else ".glb" if "glb" in key else ".bin" download(url, OUTPUT_DIR / f"{key}{suffix}") print("Mesh & Rig completed successfully!")