Files
AIGame/Tools/battlefield_scene_pipeline.py

105 lines
5.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Validate and plan the authored battlefield scene pipeline.
No network or model generation is performed by this command.
"""
from __future__ import annotations
import argparse, json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MANIFEST = ROOT / "Doc/Design/Scene/Battlefield/battlefield_scene_manifest.json"
def load():
with MANIFEST.open(encoding="utf-8-sig") as f:
return json.load(f)
def validate(data, require_selection=False):
errors=[]
selected=data.get("selectedDesign", "")
ids={x.get("id") for x in data.get("designOptions", [])}
if require_selection and selected not in ids: errors.append("selectedDesign 必须先选择 A/B/C")
if selected and selected not in ids: errors.append(f"未知方案: {selected}")
limit=int(data.get("componentTypeLimit", 0))
allowed=set(data.get("allowedComponentTypes", []))
components=data.get("components", [])
types=[x.get("type") for x in components]
if len(types)!=len(set(types)): errors.append("components 存在重复 type")
if len(types)>limit or len(types)>15: errors.append(f"组件类型 {len(types)} 超过上限")
for t in types:
if t not in allowed: errors.append(f"未登记组件类型: {t}")
ground=data.get("ground", {})
if not ground.get("type")=="ground": errors.append("ground 必须独立登记为 type=ground")
if any(x.get("type")=="ground" for x in components): errors.append("ground 不得放入 components")
if not data.get("assembly", {}).get("outputPrefab"): errors.append("缺少 assembly.outputPrefab")
return errors
def design_ready(data, design_id):
option = next((x for x in data.get("designOptions", []) if x.get("id") == design_id), None)
generation = (option or {}).get("generation", {})
return (
option is not None
and generation.get("tool") == "Tools/gpt_image_generate.py"
and generation.get("model") == "gpt-image-2.5"
and generation.get("status") == "generated"
)
def components_ready(data):
ground = data.get("ground", {})
if ground.get("designGenerationStatus") != "generated":
return False
return bool(data.get("components")) and all(
item.get("generationStatus") in {"generated", "reused"} for item in data.get("components", [])
)
def plan(data):
selected=data.get("selectedDesign")
if not selected:
if data.get("requestedDesign"):
raise SystemExit(f"已记录用户选择 {data['requestedDesign']},但总览图尚未生成;请先运行 Tools/generate_battlefield_designs.py --design {data['requestedDesign']}")
raise SystemExit("请先选择方案:python Tools/battlefield_scene_pipeline.py select A")
if not design_ready(data, selected):
raise SystemExit("方案总览图尚未由 gpt-image-2.5 生成并登记,请先运行 Tools/generate_battlefield_designs.py")
if not components_ready(data):
raise SystemExit("地表或组件设计图尚未由 gpt-image-2.5 生成并登记,请先运行 Tools/generate_battlefield_components.py")
errs=validate(data, True)
if errs: raise SystemExit("\n".join(errs))
print(f"方案 {selected}:{next(x['name'] for x in data['designOptions'] if x['id']==selected)}")
print("新组件默认:分件图 -> 混元 50k -> Studio 低模 -> Studio UV -> Blender 烘焙 -> 超目标 130% 时保留 UV 减面")
for item in [data["ground"], *data["components"]]:
status = item.get("generationStatus", "pending")
if status == "reused":
print(f"{item['type']}: reuse -> {item.get('prefab') or item.get('existingPrefab')}; skip model generation")
continue
print(f"{item['type']}: target {item['targetTriangles']} tris; keep existing production receipts when already baked/imported")
print(f" python Tools/scene_component_pipeline.py --manifest {MANIFEST.relative_to(ROOT).as_posix()} --part {item['type']} --dry-run")
print("Prefab:", data["assembly"]["outputPrefab"])
def main():
p=argparse.ArgumentParser(); sub=p.add_subparsers(dest="cmd", required=True)
sub.add_parser("status")
sel=sub.add_parser("select"); sel.add_argument("design", choices=["A","B","C"])
sub.add_parser("validate"); sub.add_parser("plan")
args=p.parse_args(); data=load()
if args.cmd=="status":
components = data.get("components", [])
print(json.dumps({
"requestedDesign": data.get("requestedDesign"),
"selectedDesign": data.get("selectedDesign"),
"overviewGeneration": data.get("designGeneration", {}).get("status"),
"components": len(components),
"reusedComponents": [x.get("type") for x in components if x.get("generationStatus") == "reused"],
"newComponents": [x.get("type") for x in components if x.get("generationStatus") == "generated"],
"ground": bool(data.get("ground")),
"componentDesignGeneration": data.get("componentDesignGeneration", {}).get("status"),
"modelComponents": [x.get("type") for x in components if x.get("modelGenerationStatus") in {"baked", "imported"}],
"assemblyPrefab": data.get("assembly", {}).get("outputPrefab"),
},ensure_ascii=False))
elif args.cmd=="select":
if not design_ready(data, args.design):
raise SystemExit(f"方案 {args.design} 的设计图尚未由 gpt-image-2.5 生成,请先运行 Tools/generate_battlefield_designs.py --design {args.design}")
data["selectedDesign"]=args.design; MANIFEST.write_text(json.dumps(data,ensure_ascii=False,indent=2)+"\n",encoding="utf-8"); print("selected",args.design)
elif args.cmd=="validate":
errs=validate(data, False); print("PASS" if not errs else "FAIL\n"+"\n".join(errs)); raise SystemExit(1 if errs else 0)
else: plan(data)
if __name__=="__main__": main()