9f6572600b
- discover_assets: 枚举Content资产,按UE5类型分类,输出index.json - analyze_meshes: 包围盒/锚点/bbox_faces(area+orientation)/Line Trace细节包围盒/规则Tags - analyze_materials: 动态参数提取/base_color_hex自动转换/规则Tags - analyze_blueprints: generated_class API Actor检测/组件提取/interaction推断Tags - run_all编排器: 断点续跑/步骤控制/错误traceback/UE5 JSON序列化兼容 - probe脚本: UE5.7 API探测(TraceTypeQuery/DrawDebugTrace枚举发现)
214 lines
6.1 KiB
Python
214 lines
6.1 KiB
Python
"""UE5 资产探索编排器。
|
|
|
|
在单次 UE5 编辑器会话内顺序执行所有分析步骤。
|
|
|
|
步骤:
|
|
1. discover — 资产发现
|
|
2. mesh — 模型几何提取
|
|
3. material — 材质参数提取
|
|
4. blueprint — 蓝图结构提取
|
|
|
|
通过 AC_STEPS 环境变量控制执行哪些步骤(逗号分隔)。
|
|
默认全部执行。
|
|
|
|
环境变量(由 run_all.bat 设置):
|
|
AC_SCAN_ROOT — 扫描路径
|
|
AC_OUTPUT_DIR — 输出目录
|
|
AC_NO_QUIT — 是否不退出
|
|
AC_STEPS — 步骤列表
|
|
AC_PROJECT_ROOT — 项目根目录(用于 sys.path)
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
# ---- sys.path 设置 ----
|
|
_SCRIPT_DIR = Path(__file__).resolve().parent
|
|
if str(_SCRIPT_DIR) not in sys.path:
|
|
sys.path.insert(0, str(_SCRIPT_DIR))
|
|
|
|
# 项目根目录
|
|
_project_root = os.environ.get("AC_PROJECT_ROOT", "")
|
|
if _project_root:
|
|
PROJECT_ROOT = Path(_project_root)
|
|
else:
|
|
PROJECT_ROOT = _SCRIPT_DIR.parent.parent
|
|
|
|
# ---- Unreal import ----
|
|
try:
|
|
import unreal
|
|
HAS_UNREAL = True
|
|
except ImportError:
|
|
HAS_UNREAL = False
|
|
print("[AssetCatalog] 错误: unreal 模块不可用,必须在 UE5 环境中运行")
|
|
sys.exit(1)
|
|
|
|
from _common import (
|
|
get_scan_root, get_output_dir, get_steps, should_no_quit,
|
|
write_json, wait_for_editor, quit_editor, iso_now,
|
|
)
|
|
|
|
TAG = "[AssetCatalog]"
|
|
|
|
|
|
def run():
|
|
"""主流程:顺序执行所有启用的步骤。"""
|
|
print(f"{TAG} {'=' * 50}")
|
|
print(f"{TAG} UE5 资产探索工具")
|
|
print(f"{TAG} {'=' * 50}")
|
|
|
|
scan_root = get_scan_root()
|
|
output_dir = get_output_dir()
|
|
steps = get_steps()
|
|
|
|
print(f"{TAG} 扫描路径: {scan_root}")
|
|
print(f"{TAG} 输出目录: {output_dir}")
|
|
print(f"{TAG} 执行步骤: {', '.join(steps)}")
|
|
print(f"{TAG}")
|
|
|
|
# 确保输出目录存在
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# 等待编辑器就绪
|
|
if not wait_for_editor():
|
|
print(f"{TAG} 错误: 编辑器初始化超时")
|
|
return
|
|
|
|
log = {
|
|
"started_at": iso_now(),
|
|
"scan_root": scan_root,
|
|
"output_dir": str(output_dir),
|
|
"steps": [],
|
|
}
|
|
|
|
index_data = None
|
|
|
|
# ---- Step 0: Discover ----
|
|
if "discover" in steps:
|
|
print(f"\n{TAG} {'-' * 40}")
|
|
print(f"{TAG} Step 0: Discover")
|
|
print(f"{TAG} {'-' * 40}")
|
|
|
|
t0 = time.time()
|
|
from discover_assets import run as run_discover
|
|
result = run_discover()
|
|
elapsed = time.time() - t0
|
|
|
|
result["elapsed_sec"] = round(elapsed, 1)
|
|
log["steps"].append({"name": "discover", **result})
|
|
|
|
if result.get("status") == "ok":
|
|
# load index.json for subsequent steps
|
|
try:
|
|
index_data = json.loads(
|
|
(output_dir / "index.json").read_text(encoding="utf-8")
|
|
)
|
|
except Exception as e:
|
|
print(f"{TAG} WARN: load index.json failed: {e}")
|
|
else:
|
|
print(f"{TAG} Discover failed, skipping remaining steps")
|
|
log["completed_at"] = iso_now()
|
|
log["status"] = "discover_failed"
|
|
_save_log(log, output_dir)
|
|
_maybe_quit()
|
|
return
|
|
|
|
# ---- Step 1: Meshes ----
|
|
if "mesh" in steps:
|
|
print(f"\n{TAG} {'-' * 40}")
|
|
print(f"{TAG} Step 1: Mesh Analysis")
|
|
print(f"{TAG} {'-' * 40}")
|
|
|
|
t0 = time.time()
|
|
try:
|
|
from analyze_meshes import run as run_mesh
|
|
result = run_mesh(index_data)
|
|
except Exception as e:
|
|
print(f"{TAG} ERROR in mesh analysis: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
result = {"status": "error", "reason": str(e)}
|
|
elapsed = time.time() - t0
|
|
|
|
result["elapsed_sec"] = round(elapsed, 1)
|
|
log["steps"].append({"name": "mesh", **result})
|
|
|
|
# ---- Step 2: Materials ----
|
|
if "material" in steps:
|
|
print(f"\n{TAG} {'-' * 40}")
|
|
print(f"{TAG} Step 2: Material Analysis")
|
|
print(f"{TAG} {'-' * 40}")
|
|
|
|
t0 = time.time()
|
|
try:
|
|
from analyze_materials import run as run_material
|
|
result = run_material(index_data)
|
|
except Exception as e:
|
|
print(f"{TAG} ERROR in material analysis: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
result = {"status": "error", "reason": str(e)}
|
|
elapsed = time.time() - t0
|
|
|
|
result["elapsed_sec"] = round(elapsed, 1)
|
|
log["steps"].append({"name": "material", **result})
|
|
|
|
# ---- Step 3: Blueprints ----
|
|
if "blueprint" in steps:
|
|
print(f"\n{TAG} {'-' * 40}")
|
|
print(f"{TAG} Step 3: Blueprint Analysis")
|
|
print(f"{TAG} {'-' * 40}")
|
|
|
|
t0 = time.time()
|
|
try:
|
|
from analyze_blueprints import run as run_blueprint
|
|
result = run_blueprint(index_data)
|
|
except Exception as e:
|
|
print(f"{TAG} ERROR in blueprint analysis: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
result = {"status": "error", "reason": str(e)}
|
|
elapsed = time.time() - t0
|
|
|
|
result["elapsed_sec"] = round(elapsed, 1)
|
|
log["steps"].append({"name": "blueprint", **result})
|
|
|
|
# ---- 汇总 ----
|
|
log["completed_at"] = iso_now()
|
|
log["status"] = "ok"
|
|
_save_log(log, output_dir)
|
|
|
|
print(f"\n{TAG} {'=' * 50}")
|
|
print(f"{TAG} 全部完成")
|
|
print(f"{TAG} {'=' * 50}")
|
|
for step in log["steps"]:
|
|
print(f"{TAG} {step['name']}: {step.get('status', '?')} "
|
|
f"({step.get('analyzed', step.get('total', '?'))} 成功, "
|
|
f"{step.get('elapsed_sec', '?')}s)")
|
|
|
|
_maybe_quit()
|
|
|
|
|
|
def _save_log(log: dict, output_dir: Path):
|
|
"""保存运行日志。"""
|
|
log_path = output_dir / "catalog_log.json"
|
|
write_json(log, log_path)
|
|
print(f"{TAG} 日志已保存: {log_path}")
|
|
|
|
|
|
def _maybe_quit():
|
|
"""根据 AC_NO_QUIT 决定是否退出编辑器。"""
|
|
if should_no_quit():
|
|
print(f"{TAG} AC_NO_QUIT=1,不退出编辑器")
|
|
else:
|
|
print(f"{TAG} 退出编辑器...")
|
|
quit_editor()
|
|
|
|
|
|
# ---- 入口 ----
|
|
if __name__ == "__main__":
|
|
run()
|