"""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("地表:", data["ground"]["targetTriangles"], "tris -> Ground/.fbx") for item in data["components"]: status = item.get("generationStatus", "pending") source = item.get("existingPrefab", "new model") if status == "reused" else "new model" print(f"{item['type']}: {status} -> {source} -> --faces 50k -> --tris {item.get('targetTriangles',700)} -> Components/{item['type']}/") 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()