From 609788fe00a018ba29616d3b4a2fbae35f6ac699 Mon Sep 17 00:00:00 2001 From: ud18010 Date: Wed, 15 Jul 2026 10:43:01 +0800 Subject: [PATCH] Add ModelTranslator tool design spec Co-Authored-By: Claude Opus 4.8 --- .../2026-07-15-model-translator-design.md | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-15-model-translator-design.md diff --git a/docs/superpowers/specs/2026-07-15-model-translator-design.md b/docs/superpowers/specs/2026-07-15-model-translator-design.md new file mode 100644 index 00000000..4ecf48cc --- /dev/null +++ b/docs/superpowers/specs/2026-07-15-model-translator-design.md @@ -0,0 +1,89 @@ +# ModelTranslator 工具设计文档 + +日期:2026-07-15 +状态:已确认 + +## 目标 + +`Tools/ModelTranslator/` 下的 Python 工具:用 headless Blender 把带 PBR 贴图的 FBX(如 `src/tong.fbx`)转换为: + +1. **纯模型 FBX**(剥离贴图,保留材质名/槽位) +2. **XPbr shader 所需贴图**(每材质一套,≤2048×2048): + - 基础贴图:RGB=颜色,A=透明 > AO > 255(自动判定) + - 混合贴图:RG=球面编码法线,B=金属度,A=粗糙度 +3. **Unity 开箱即用资产**:贴图/材质/FBX 的 `.meta` 与 `.mat` 全部预生成,拷入 `Client/Assets/` 即可直接使用 + +## 环境 + +- Blender:`D:\tools\blender-5.0.0-windows-x64\blender.exe`(5.0),查找顺序:`--blender` 参数 → 环境变量 `BLENDER_EXE` → 上述默认路径 → PATH +- 外层 CLI 用系统 Python 3.12;图像处理在 Blender 内用 bpy + 自带 numpy,不引第三方依赖 + +## 文件结构 + +``` +Tools/ModelTranslator/ + model_translator.py # CLI 入口:找 blender、组命令行、汇报结果 + bl_convert.py # Blender 内运行的转换脚本 + README.md # 用法说明 + src/ # 源 FBX(已存在) + out/<模型名>/ # 输出(git 忽略) +``` + +## CLI + +``` +python model_translator.py src/tong.fbx # 单文件 +python model_translator.py src/ # 目录批量(*.fbx) + [-o out] [--max-size 2048] [--blender ] +``` + +## bl_convert.py 流程(每个 FBX) + +1. 空场景导入 FBX,解包全部内嵌贴图 +2. 逐材质找 Principled BSDF,沿连线识别贴图:Base Color、Alpha、Normal(穿过 Normal Map 节点)、Metallic、Roughness;AO 按贴图名匹配(含 `ao`/`occlusion`/`ambient`,不区分大小写) +3. 目标尺寸 = min(max-size, 各源贴图最大边),所有参与贴图缩放到统一尺寸(`image.scale()`) +4. 基础贴图:RGB=颜色贴图(无则 Principled 常量色);A=透明贴图 > AO 贴图 > 255 +5. 混合贴图: + - RG=法线球面编码:切线法线 `n = rgb*2-1` 归一化后 `enc = n.xy/√(2(1+n.z)) × 0.5 + 0.5`;无法线贴图填 (0.5, 0.5) + - B=金属度贴图(取 R 通道)或 Principled 常量 + - A=粗糙度贴图(取 R 通道)或 Principled 常量 +6. 保存 RGBA PNG 到 `out/<模型名>/` +7. 删除材质中所有贴图节点,`path_mode='NONE'` 导出纯模型 FBX +8. 打印 JSON 摘要(stdout 标记行),runner 转为可读日志 + +## Unity 资产生成(model_translator.py 内完成,纯文本模板) + +每材质: + +| 文件 | 要点 | +|---|---| +| `<模型>_<材质>_base.png.meta` | TextureImporter:sRGB 开,maxTextureSize 2048 | +| `<模型>_<材质>_mix.png.meta` | TextureImporter:**sRGB 关**(线性),maxTextureSize 2048 | +| `<模型>_<材质>.mat` | shader 引用 XPbr GUID(见下);`_MainTex`/`_MixTex` 引用预分配贴图 GUID;透明模式时写 keyword `_ALPHATEST_ON` 且 `_AlphaTest=1`;`_Metallic`/`_Roughness`/`_AO`=1.0 | +| `<模型>_<材质>.mat.meta` | NativeFormatImporter,mainObjectFileID 2100000 | + +- XPbr shader GUID:`cab897163f45445fa34263d6670bfc9e`(`Client/Assets/Game/shader/XShader/XPbr.shader.meta`,若该 meta 变更需同步工具常量) +- 模型 FBX 生成 `.meta`(ModelImporter):`externalObjects` 按材质名重映射到生成的 `.mat` GUID,模型拖进场景直接生效 +- 所有 GUID 用 `uuid4().hex` 预分配;同一模型重复转换时**复用已存在 meta 里的 GUID**(避免拷入 Unity 后再次转换导致引用断裂) + +## 配套 shader 修改 + +`Client/Assets/Game/shader/XShader/XPbr_Base.hlsl` 的 `DecodeSphereMap` 入口加 `encoded = encoded * 2 - 1;`:现编码只能表示 xy≥0 的法线(无符号 rg),重映射后可表示全半球。只影响 XPbr(无存量资源),不动 XTex。 + +## 错误处理 + +- 找不到 Blender → 明确报错并提示 `--blender` +- 材质无 Principled BSDF → 警告,用全常量兜底(灰色、平面法线、金属 0、粗糙 0.5) +- 贴图输入是程序纹理/复杂节点 → 警告,用该输入的默认常量 +- Blender 退出码非 0 → runner 透传 stderr 摘要 + +## 不做(YAGNI) + +合并图集/UV 重排、Cycles 烘焙、法线绿通道翻转选项(README 说明现状)、动画/骨骼处理(FBX 原样导出)。 + +## 验证 + +1. 实跑 `src/tong.fbx` 与 `src/woodcar.fbx` +2. 输出文件齐全,贴图尺寸 ≤2048 +3. 法线 round-trip 校验:按 shader 公式解码生成贴图的 RG,与源法线角度误差 < 2° +4. 拷入 Unity 工程验证材质/模型开箱可用(人工步骤)