112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
"""Generate battlefield concept boards with the project image generation flow.
|
|
|
|
This script delegates image creation to ``Tools/gpt_image_generate.py`` with
|
|
the project-standard ``gpt-image-2.5`` model. It deliberately does not draw
|
|
placeholder diagrams with PIL. Use ``--dry-run`` to inspect requests without
|
|
calling the remote service.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
OUT = ROOT / "Doc/Design/Scene/Battlefield"
|
|
PROMPT_FILE = OUT / "battlefield_design_prompts.json"
|
|
IMAGE_TOOL = ROOT / "Tools/gpt_image_generate.py"
|
|
|
|
|
|
def load_prompts() -> dict:
|
|
return json.loads(PROMPT_FILE.read_text(encoding="utf-8-sig"))
|
|
|
|
|
|
def mark_generated(design_id: str, output: Path, status_file: Path) -> None:
|
|
manifest_path = OUT / "battlefield_scene_manifest.json"
|
|
if not manifest_path.is_file():
|
|
return
|
|
manifest = json.loads(manifest_path.read_text(encoding="utf-8-sig"))
|
|
for option in manifest.get("designOptions", []):
|
|
if option.get("id") == design_id:
|
|
option.setdefault("generation", {}).update(
|
|
{
|
|
"tool": "Tools/gpt_image_generate.py",
|
|
"model": "gpt-image-2.5",
|
|
"status": "generated",
|
|
"output": str(output.relative_to(ROOT)).replace("\\", "/"),
|
|
"statusFile": str(status_file.relative_to(ROOT)).replace("\\", "/"),
|
|
"generatedAtUtc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
}
|
|
)
|
|
if manifest.get("requestedDesign") == design_id:
|
|
manifest["selectedDesign"] = design_id
|
|
manifest.setdefault("designGeneration", {}).update(
|
|
{
|
|
"tool": "Tools/generate_battlefield_designs.py",
|
|
"imageTool": "Tools/gpt_image_generate.py",
|
|
"model": "gpt-image-2.5",
|
|
"status": "generated",
|
|
"generatedAtUtc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
"note": "已生成并覆盖总览图。",
|
|
}
|
|
)
|
|
manifest_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Use gpt-image-2.5 to generate the three battlefield design boards."
|
|
)
|
|
parser.add_argument("--design", choices=["A", "B", "C", "all"], default="all")
|
|
parser.add_argument("--dry-run", action="store_true", help="print requests without calling the image API")
|
|
parser.add_argument("--quality", default="auto", choices=["auto", "low", "medium", "high"])
|
|
args = parser.parse_args()
|
|
|
|
data = load_prompts()
|
|
selected = data["designs"] if args.design == "all" else [next(x for x in data["designs"] if x["id"] == args.design)]
|
|
OUT.mkdir(parents=True, exist_ok=True)
|
|
|
|
for item in selected:
|
|
output = OUT / item["file"]
|
|
callback = OUT / f"{output.stem}_gpt_image_status.json"
|
|
command = [
|
|
sys.executable,
|
|
str(IMAGE_TOOL),
|
|
"--prompt",
|
|
item["prompt"],
|
|
"--model",
|
|
"gpt-image-2.5",
|
|
"--aspect-ratio",
|
|
"1024x1024",
|
|
"--quality",
|
|
args.quality,
|
|
"--output",
|
|
str(output),
|
|
"--callback-file",
|
|
str(callback),
|
|
"--result-only",
|
|
]
|
|
print(f"[{item['id']}] {item['name']}")
|
|
print(" ".join(json.dumps(part, ensure_ascii=False) for part in command))
|
|
if args.dry_run:
|
|
continue
|
|
try:
|
|
subprocess.run(command, cwd=ROOT, check=True)
|
|
except subprocess.CalledProcessError as exc:
|
|
print(
|
|
"image generation failed; configure GRSAI_API_KEY or "
|
|
"GPT_IMAGE_API_KEY and rerun this command",
|
|
file=sys.stderr,
|
|
)
|
|
return exc.returncode or 1
|
|
mark_generated(item["id"], output, callback)
|
|
print(f"saved: {output}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|