7a2436a612
- analyze_meshes: 移除失败的 LineTrace 和六面分析,只保留基础属性 (包围盒、锚点、复杂度、Socket、材质槽位、自动Tag) - build_database: 新增离线汇总脚本,将 meshes/*.json 扁平化为 统一的 asset_database.json - ai_inference: 新增 GLM API 批量推理脚本,按类别分批推断语义信息 (朝向、表面高度、功能描述、交互属性、降级方案) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
148 lines
4.5 KiB
Python
148 lines
4.5 KiB
Python
"""资产数据库汇总脚本。
|
|
|
|
读取 asset-catalog 的扫描结果(meshes/*.json),汇总为统一的单文件
|
|
asset_database.json,以资产名为 key 扁平化存储。
|
|
|
|
此脚本不依赖 UE5,可离线运行。
|
|
|
|
用法:
|
|
python build_database.py [--input DIR] [--output FILE]
|
|
|
|
--input 扫描输出目录,默认 output/asset-catalog
|
|
--output 输出文件路径,默认 <input>/asset_database.json
|
|
"""
|
|
|
|
import sys
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
_SCRIPT_DIR = Path(__file__).resolve().parent
|
|
if str(_SCRIPT_DIR) not in sys.path:
|
|
sys.path.insert(0, str(_SCRIPT_DIR))
|
|
|
|
from _common import read_json, write_json, iso_now
|
|
|
|
TAG = "[BuildDB]"
|
|
|
|
|
|
def build_database(input_dir: Path, output_file: Path) -> dict:
|
|
"""汇总 meshes/*.json 为统一数据库。
|
|
|
|
Args:
|
|
input_dir: 包含 meshes/ 子目录的扫描输出根目录
|
|
output_file: 输出的 asset_database.json 路径
|
|
|
|
Returns:
|
|
统计信息 dict
|
|
"""
|
|
meshes_dir = input_dir / "meshes"
|
|
if not meshes_dir.exists():
|
|
print(f"{TAG} 错误: {meshes_dir} 不存在,请先运行 analyze_meshes")
|
|
return {"status": "error", "reason": "no meshes dir"}
|
|
|
|
# 收集所有子目录的扫描文件
|
|
mesh_files = sorted(meshes_dir.glob("*.json"))
|
|
if not mesh_files:
|
|
print(f"{TAG} 错误: {meshes_dir} 下无 JSON 文件")
|
|
return {"status": "error", "reason": "no mesh files"}
|
|
|
|
print(f"{TAG} 发现 {len(mesh_files)} 个扫描文件")
|
|
|
|
# 扁平化合并
|
|
assets = {}
|
|
total = 0
|
|
skipped = 0
|
|
|
|
for mf in mesh_files:
|
|
try:
|
|
data = read_json(mf)
|
|
except Exception as e:
|
|
print(f"{TAG} 警告: 读取 {mf.name} 失败: {e}")
|
|
continue
|
|
|
|
subfolder = data.get("subfolder", mf.stem)
|
|
meshes = data.get("meshes", {})
|
|
if not meshes:
|
|
continue
|
|
|
|
for name, mesh_data in meshes.items():
|
|
total += 1
|
|
|
|
# 跳过无包围盒的(加载失败的)
|
|
if mesh_data.get("bounding_box") is None:
|
|
skipped += 1
|
|
continue
|
|
|
|
# 构建统一条目
|
|
entry = {
|
|
"ue_path": mesh_data.get("ue_path", ""),
|
|
"subfolder": subfolder,
|
|
"bounding_box": mesh_data.get("bounding_box"),
|
|
"pivot": mesh_data.get("pivot"),
|
|
"mesh_complexity": mesh_data.get("mesh_complexity", {}),
|
|
"material_slots": mesh_data.get("material_slots", []),
|
|
"sockets": mesh_data.get("sockets", []),
|
|
"tags": {
|
|
"style": mesh_data.get("style_tags", []),
|
|
"scene": mesh_data.get("scene_tags", []),
|
|
},
|
|
"ai_inference": None,
|
|
}
|
|
|
|
# 如果资产名重复(不同子目录下同名),加后缀
|
|
key = name
|
|
if key in assets:
|
|
safe_sub = subfolder.replace("/", "_").replace("\\", "_")
|
|
key = f"{name}_{safe_sub}"
|
|
print(f"{TAG} 注意: 资产名重复 {name},使用 {key}")
|
|
|
|
assets[key] = entry
|
|
|
|
# 写入数据库
|
|
db = {
|
|
"metadata": {
|
|
"generated_at": iso_now(),
|
|
"source": "asset_catalog_v1",
|
|
"total_assets": len(assets),
|
|
"skipped_no_bbox": skipped,
|
|
"input_files": [mf.name for mf in mesh_files],
|
|
},
|
|
"assets": assets,
|
|
}
|
|
|
|
write_json(db, output_file)
|
|
print(f"{TAG} ✅ 汇总完成: {len(assets)} 个资产 → {output_file}")
|
|
print(f"{TAG} 跳过 {skipped} 个无包围盒资产 / 总计 {total}")
|
|
|
|
return {
|
|
"status": "ok",
|
|
"total": total,
|
|
"assets": len(assets),
|
|
"skipped": skipped,
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="汇总资产扫描数据为统一 JSON 数据库")
|
|
parser.add_argument(
|
|
"--input", type=str,
|
|
default=str(_SCRIPT_DIR.parent.parent / "output" / "asset-catalog"),
|
|
help="扫描输出目录 (默认: output/asset-catalog)",
|
|
)
|
|
parser.add_argument(
|
|
"--output", type=str, default=None,
|
|
help="输出文件路径 (默认: <input>/asset_database.json)",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
input_dir = Path(args.input)
|
|
output_file = Path(args.output) if args.output else input_dir / "asset_database.json"
|
|
|
|
result = build_database(input_dir, output_file)
|
|
if result.get("status") != "ok":
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|