#!/usr/bin/env python3 """P0 enemy (or companion, --kind Animal) static stage: Hunyuan high GLB -> 2000-tri XPbr bake -> Bundles/Characters/Enemy/{Name} -> static Prefab -> static audit. Usage: python Tools/enemy_static_pipeline.py Dashlet 0.5 # bake + install + SetupCharacter + AuditCharacterVisual python Tools/enemy_static_pipeline.py Dashlet 0.5 --source Tools/output/enemy_3d/Dashlet/dashlet_forward.glb New models must pass the project's +Z front check in Unity. Check rendered face/feet, not just FBX axis_forward. Legacy assets can request --forward-z=-1 for audit views. """ from __future__ import annotations import argparse import json import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] PROJECT = ROOT / "AIClient" def run(cmd: list[str], cwd: Path = ROOT) -> str: print("$", " ".join(cmd), flush=True) done = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, encoding="utf-8", errors="replace") if done.returncode != 0: raise SystemExit(f"failed ({done.returncode}): {' '.join(cmd)}\n{done.stdout[-2000:]}\n{done.stderr[-2000:]}") return done.stdout def unity(file: str, entry: str, args: list) -> str: out = run(["unity", "command", "run_script", "--project-path", str(PROJECT), "--file", file, "--entry", entry, "--args", json.dumps(args), "--timeout_ms", "90000", "--timeout", "120"], PROJECT) if '"success":true' not in out.replace(" ", ""): raise SystemExit(f"Unity {entry} failed:\n{out[-2000:]}") return out def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("name") ap.add_argument("height", type=float) ap.add_argument("--source", type=Path) ap.add_argument("--forward-z",type=int,choices=(-1,1),default=1,help="+1 is project +Z front; -1 audits an explicitly legacy -Z asset") ap.add_argument("--kind", default="Enemy", choices=("Enemy", "Animal"), help="Animal installs a companion under Characters/Animal") a = ap.parse_args() low = a.name.lower() tag = "enemy" if a.kind == "Enemy" else "companion" source = a.source or Path(f"Tools/output/{tag}_3d/{a.name}/{low}.glb") game = Path(f"Tools/output/{tag}_game/{low}_game") stem = source.stem run([sys.executable, "Tools/model_tools/model_lowpoly_bake.py", str(source), "-o", str(game), "--tris", "2000", "--size", "1024", "--callback-file", str(game / "bake_status.json")]) run([sys.executable, "Tools/install_character_bundles.py", "-a", a.name, "--kind", a.kind, "-b", str(game / f"{stem}_xpbr")]) unity("AgentScripts/SetupCharacter.cs", "SetupCharacter.Run", [a.kind, a.name, a.height]) audit = ROOT / (f"Tools/output/enemy_characters/{a.name}/static_audit" if a.kind == "Enemy" else f"Tools/output/companions/{a.name}/static_audit") unity("AgentScripts/AuditCharacterVisual.cs", "AuditCharacterVisual.Capture", [f"Assets/Bundles/Characters/{a.kind}/{a.name}/{a.name}.prefab", str(audit).replace("\\", "/"), a.height,"",0,a.forward_z]) print((audit / "static_verification.txt").read_text(encoding="utf-8")) if __name__ == "__main__": main()