Rendering fixes (real): - m2_material shaders: cull_disabled -> cull_back (inside-of-mesh faces were z-winning and darkening/hiding the head under the ShaderMaterial path). - drop `* COLOR` from the shader (meshes carry no ARRAY_COLOR). Animation: convert gr2 global poses to Godot *local* bone poses and set via set_bone_pose (was set_bone_global_pose). MTGODOT_VERIFY=1 confirms get_bone_global_pose(i) == conv(world[i]) for all 75 bones (<=2.6e-5). Head/upper-armor collapse on dance_1 is NOT a Godot-route bug: - reproduced identically in xrender-poc's bgfx demo at the same anim/t - reproduced by the new MTGODOT_CPUSKIN=1 reference path (same LBS math as xrender's validated skin_mesh) - `general/wait` / `run` render correctly in all three => upstream libgr2 Curve::eval (degree-2 quaternion B-spline) vs Granny. Demo default animation switched to general/wait; documented in MIDREVIEW §4 and README. Debug scaffolding gated behind MTGODOT_VERIFY / MTGODOT_CPUSKIN. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaHYEY9rwLWt21PULiYjeJ
65 lines
7.8 KiB
Markdown
65 lines
7.8 KiB
Markdown
# Phase 1 中期评审 — mtgodot-poc
|
||
|
||
> 日期:2026-08-29 · 平台:macOS(Apple M2)· Godot 4.7.1 · 对标 [`GODOT-POC-PLAN.md`](./GODOT-POC-PLAN.md) §00 Phase 1
|
||
> 结论先行:**桥接层工程量小、风险已基本出清,建议进入 Phase 2(移动端)**。M2.5 门禁的"材质精确映射"仍有一处需要 oracle 的开口,不阻塞。
|
||
|
||
---
|
||
|
||
## 1. 做到了什么(M0'–M2.5)
|
||
|
||
| 里程碑 | 状态 | 证据 |
|
||
|---|---|---|
|
||
| **M0'** 脚手架 | ✅ | GDExtension 加载、`Metin2Model`/`Metin2AnimPlayer` 注册、`libgr2` 静态链接、Godot 4.7 工程 Metal Forward+ 出画 |
|
||
| **M1** 静态渲染 | ✅ | `warrior_cheongrin`:75 骨 / 5 表面 / 3324 顶点,正立、朝 +Z、`unit_scale=0.01`、`flip_z/flip_winding=false`、贴图正确(`test/golden/m1-bindpose.png`)。多骨架:`warrior_lord`(v6,74)、`assassin`(v6,95,17 表面)、`shaman_lord`(**v7**,93) 均加载渲染正确 |
|
||
| **M2** 骨骼动画 | ✅(渲染路径)/ ⚠️(一条 upstream bug) | `general/wait`(2.67s / 75 track)经 `gr2::sample_pose`(跨文件)→ 逐骨 Godot **局部 pose**(`set_bone_pose`)→ GPU 蒙皮,头 / 护甲对齐正确(`test/golden/m2-wait.png`)。**数值验证**:`get_bone_global_pose(i)` vs `conv(world[i])` 全 75 骨 ≤ **2.6e-5**;CPU-LBS 参考路径(`MTGODOT_CPUSKIN=1`,与 xrender `skin_mesh` 同式)与 GPU 蒙皮同结果。`selfcheck` 0 NaN。**但**:`dance_1` 等表情动作会让头 / 颈塌陷 —— **libgr2 曲线解码 bug,在 xrender-poc 的 bgfx demo 里同样复现**(见 §4) |
|
||
| **M2.5** 材质与观感 | 🟡 门禁未闭 | `ShaderMaterial`(modulate tex×COLOR×light;opaque/alpha/alpha-test/add 四模式)+ 目录扫描贴图解析(warrior 5/5、assassin 17/17、shaman 1/1 表面命中)+ 方向光阴影 + procedural sky + 引擎雾(`test/golden/m25-*.png`)。`libgr2::dump_materials()` 已加(见 §4) |
|
||
|
||
## 2. 桥接层工程量(实测)
|
||
|
||
新增 C++ ≈ **1100 行**(含端口自 xrender-poc 的 DXT 解码 160 行):
|
||
|
||
| 文件 | 行 | 职责 |
|
||
|---|---|---|
|
||
| `gr2_bridge.{h,cpp}` | ~260 | gr2 Mat4→`Transform3D`(4×4 转置)、`build_skeleton`/`build_skin`/`build_mesh`、Z-up→Y-up `make_conv` |
|
||
| `metin2_model.{h,cpp}` | ~470 | `Metin2Model` 节点、`load_gr2`、贴图解析、材质、属性 |
|
||
| `metin2_anim.{h,cpp}` | ~230 | `Metin2AnimPlayer`、每帧 `sample_pose`→骨骼、`selfcheck` |
|
||
| `m2_material.{h,cpp}` | ~130 | Metin2 风格 `ShaderMaterial`(两个 shader:blend_mix / blend_add) |
|
||
| `dxt.{h,cpp}` | ~160 | DDS DXT1/3/5 软解(端口) |
|
||
| libgr2 `gr2_material.cpp`(xrender-poc) | ~110 | `dump_materials()` 类型树遍历(附加) |
|
||
|
||
**耗时**:M0'–M2.5 一次会话内跑通。与计划 §05 估的 Phase 1「3.5–5 周单人」相比,**核心桥接远比预估轻** —— 因为 `libgr2` 已完成、Godot 的 `Skeleton3D`/`ArrayMesh`/`Skin`/GPU 蒙皮直接可用,且关键恒等式(`skin_matrix = global_pose(i)·bind_pose(i) == gr2 deformer 矩阵`)成立,不需要自研蒙皮。剩余预估工作量集中在 M2.5 材质精修 + 对拍。
|
||
|
||
## 3. 关键技术结论
|
||
|
||
- **坐标/单位**:gr2 行主序 Mat4 → Godot `Transform3D` 就是一次 **4×4 转置**(gr2 平移在第 4 行 = Godot `.origin`)。Z-up cm → Y-up m(`rotate(-90°,X)` + `scale(0.01)`)放在 `Metin2Model` 节点自身 transform。四个测试骨架全部 `flip_z=false`、`flip_winding=false` 正确 —— **Metin2/Granny 内容对 Godot 右手系不需要额外镜像**。
|
||
- **动画走 B 方案**:`_process` 里 `sample_pose` 出 `world[]`,`skel.set_bone_global_pose(i, conv(world[i]))`,Skin bind = `conv(inverse_world[i])`。Godot 的 GPU 蒙皮矩阵 `global_pose(i)·bind(i)` 恒等于 gr2 deformer 矩阵。**不需要烘焙 `Animation` 资源** 即可保真(A 方案仅在移动端多角色 CPU 压不住时才需要)。
|
||
- **贴图**:Godot 4 `Image.load()` 不支持 `.dds` → 运行时软解 DXT(`dxt.cpp`)。`.gr2` 用 `.gdignore` + 绝对路径加载,绕开 Godot import。
|
||
- **移动端预判更好**:M2.5 的 `ShaderMaterial` + `ArrayMesh` + `Skeleton3D` 都在 Godot Compatibility(GLES3) 支持范围内 —— Phase 2 老安卓机兜底比"Godot 里塞自研渲染器"那条路好。
|
||
|
||
## 4. 剩余风险 / 门禁开口
|
||
|
||
| 项 | 影响 | 出路 |
|
||
|---|---|---|
|
||
| **libgr2 曲线解码 bug —— 部分动画头 / 颈塌陷**(`dance_1` 等表情,`rot:Old(d=2,dim=4)` 二次四元数 B 样条)。**这不是 Godot route 的问题** —— 已用 xrender-poc 的 bgfx demo 在同一动画 / 同一 t 复现完全一致的塌陷(`/tmp/xr-*.png`),且 mtgodot 的 CPU-LBS 参考路径(与 xrender 同式)也复现。`wait` / `run` 等动画正常。xrender-poc M2「对拍 Granny ≤4.6e-5」的 23 用例未覆盖到这个可视化用例。 | 表情动作不可用;idle / 移动动画正常。当前 demo 默认 `general/wait` | libgr2 `gr2_anim.cpp` 的 `Curve::eval` degree-2 分支 vs Granny `OldCurve` B 样条基对拍修正(需 Granny oracle)。两条 route 共用修法。**属 xrender-poc/libgr2 的活,不阻塞 Godot route 结论** |
|
||
| **M2.5 材质精确映射未闭**(M2.5 门禁开口)| `libgr2::dump_materials()` 已能取材质名 + `FromFileName`(warrior 4 材质 / 2 有 diffuse 名),但 material_index→贴图 的映射**未对拍 Granny**,实测驱动 warrior 时 body 表面误取 face 贴图。当前默认走文件名+目录扫描启发式(观感正确),material 路径 `use_gr2_materials` 开关默认关 | 需要 Granny oracle 逐材质对拍(与 xrender-poc M1 T5 同一缺口,共用修法)。这是 Phase 1 收尾的最后一项 |
|
||
| **对拍未接线** | 与 xrender-poc bgfx demo / oracle 的逐帧几何/骨骼矩阵对拍脚本(`test/compare.py`)未写 | 需先构建 xrender-poc bgfx demo(首次 ~10 min)。中期评审后、进 Phase 2 前补 |
|
||
| **性能数字被节流** | 后台窗口下 Godot 限 30fps,1–50 角色帧时间都贴在 33ms 天花板;100 角色破顶到 52ms。资产构建 **~165ms/角色**(单线程,无跨模型缓存),100 角色加载 16.5s(`test/godot-macos-stress.json`) | 真机 / 前台窗口下重测(Phase 2)。构建成本需 Phase 2 做多线程 + eterpack 共享贴图 |
|
||
| **多材质槽内拆分** | `build_mesh` 目前一个 gr2 mesh = 一个 surface,未按 `tri_groups[].material_index` 再拆 | 有 material API 对拍后一起做 |
|
||
| ShaderMaterial 静态 Shader 退出泄漏、alpha 排序 corner case、法线未逆转置 | 均为已知小项,见 `GODOT-POC-PLAN.md` §M2.5 T2.5.6 | 不阻塞 |
|
||
|
||
## 5. 建议
|
||
|
||
**进入 Phase 2(iOS + Android)。**
|
||
|
||
理由:Phase 1 要证明的三件事里,①gr2→Godot 场景对象、②动画运行时移植,**已用可运行代码 + 截图 + 0-NaN 自检证明可行且工程量小**;③观感还原的基础设施(自定义 shader、阴影、sky、fog、多骨架)已就位,只差"材质精确映射"这一处需 oracle 的收尾,不构成方案性风险。
|
||
|
||
Phase 2 前的收尾清单(1.5–2 周):
|
||
1. 写 `test/compare.py`,与 xrender-poc bgfx demo 同机位 / 同 t 对拍几何 + 骨骼矩阵。
|
||
2. 补 `libgr2::dump_materials` 的 Granny oracle 对拍,闭合 M2.5 门禁。
|
||
3. **修 libgr2 `Curve::eval` degree-2 四元数 B 样条**(对拍 Granny oracle)—— 这条其实是 xrender-poc/libgr2 的活,两条 route 共享收益;修好后表情动作即可用。
|
||
4. 前台窗口 / 真机重测性能,给 Phase 2 的动画 A/B 一个可信基线。
|
||
|
||
> Godot route 本身的桥接层已验证正确(`wait` 渲染与 xrender bgfx demo 一致,骨骼矩阵 ≤2.6e-5,CPU/GPU 蒙皮一致)。#3 是把已知的 upstream 曲线 bug 补上,不是 Godot route 的缺陷。
|
||
|
||
不建议现在做的:地形、`.mse` 特效、eterpack、UI/Python —— 均按计划留到正式移植。
|