"""Run the four authored stages of the battlefield scene workflow. The command is deliberately an orchestrator: it owns stage ordering and status checks, while image/model/Unity work remains in the existing tools. Existing Unity component prefabs are reused by default during stage 2. """ from __future__ import annotations import argparse import json import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] MANIFEST = ROOT / "Doc/Design/Scene/Battlefield/battlefield_scene_manifest.json" def load_manifest() -> dict: return json.loads(MANIFEST.read_text(encoding="utf-8-sig")) def design_ready(data: dict, design_id: str) -> bool: option = next((x for x in data.get("designOptions", []) if x.get("id") == design_id), None) generation = (option or {}).get("generation", {}) return generation.get("model") == "gpt-image-2.5" and generation.get("status") == "generated" def run(command: list[str], dry_run: bool) -> None: print("$", " ".join(f'"{part}"' if " " in part else part for part in command)) if not dry_run: subprocess.run(command, cwd=ROOT, check=True) def unity_command(entry: str) -> list[str]: return [ "unity", "command", "--project-path", str(ROOT / "AIClient"), "run_script", "--file", "AgentScripts/BuildSceneReview.cs", "--entry", entry, ] def main() -> int: parser = argparse.ArgumentParser(description="Orchestrate design -> component sheets -> model plan -> Unity scene review") parser.add_argument("--design", choices=["A", "B", "C"], required=True) parser.add_argument("--dry-run", action="store_true", help="只打印阶段命令,不调用图像服务或 Unity") parser.add_argument("--force-design", action="store_true", help="即使总览图已存在也重新生成") parser.add_argument("--force-new-components", action="store_true", help="不复用已有组件,重新生成组件设计图") parser.add_argument("--run-unity", action="store_true", help="阶段 4 直接调用已运行的 Unity Editor") args = parser.parse_args() data = load_manifest() print("[1/4] 生成关卡总览设计图并等待选择") if args.force_design or not design_ready(data, args.design): command = [sys.executable, str(ROOT / "Tools/generate_battlefield_designs.py"), "--design", args.design] if args.dry_run: command.append("--dry-run") run(command, args.dry_run) else: print(f"reuse overview: design {args.design} is already generated") if not args.dry_run: run([sys.executable, str(ROOT / "Tools/battlefield_scene_pipeline.py"), "select", args.design], False) print("[2/4] 按总览图拆分地表和组件设计图(已有组件默认复用)") command = [sys.executable, str(ROOT / "Tools/generate_battlefield_components.py"), "--design", args.design] if args.dry_run: command.append("--dry-run") if args.force_new_components: command.append("--force-new-components") run(command, args.dry_run) print("[3/4] 3D 模型生成与低模烘焙") print("完成组件图审核后,按 manifest 中每个组件执行 Hunyuan 3D -> Blender 减面/烘焙 -> Assets/Bundles 导入。") print("模型未登记到 manifest 前不会进入 Unity 场景阶段。") print("[4/4] Unity 场景生成与人工审核") print("$", " ".join(unity_command("BuildSceneReview.BuildAndOpenReview"))) if args.run_unity: run(unity_command("BuildSceneReview.BuildAndOpenReview"), False) else: print("use --run-unity after the model stage, with Unity Editor and Pipeline running") return 0 if __name__ == "__main__": raise SystemExit(main())