feat(tools): 添加UE5资产探索脚本系统asset-catalog
- 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枚举发现)
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user