# mtgodot-poc 「**Godot 4 + 自研资源 loader**」跨平台方案的渲染 Demo。 用一个 C++ GDExtension 把 Metin2 的 `.gr2` 骨骼资源渲染进 Godot,用 Godot 内置渲染器。 > **2026-08-29:并入 `xrender-poc`。** 原先与本仓库并行的 bgfx 自研引擎方案(`xrender-poc`) > 经中期评审([`docs/MIDREVIEW.md`](docs/MIDREVIEW.md))**停止开发**,其可复用部分—— > `libgr2`(gr2 v6/v7 读取器,已对拍 Granny)、`formats`(.msa/.msm)、`oracle`(Granny 真值工具)、 > `tools`(libgr2 校验工具)、`docs/reference`(格式逆向笔记)——已 **vendored 进本仓库**。 > bgfx 侧的渲染器 / 平台壳 / 第三方库全部弃用(Godot 取代)。bgfx demo 的参考截图留在 > `test/bgfx-reference/` 供交叉核对。`xrender-poc` 目录本身已删除(2026-08-30)。 自用项目(内部研究,不对外发布),分两阶段推进: | 阶段 | 平台 | 里程碑 | 产出 | |---|---|---|---| | **Phase 1**(当前) | macOS | M0' · M1 · M2 · M2.5 | 中期评审 | | Phase 2 | Android(一加 13 / Vulkan)· iOS(iPhone 16 / Metal) | M3 | 三设备各一次 bring-up + 性能留档 | 自用、不对外发布,**无正式 go/no-go 门禁**;三台目标设备都是现代硬件,**全程不涉及 GLES3 / Compatibility 渲染器**,不做中低端 / 老机器。 完整计划见 [`docs/GODOT-POC-PLAN.md`](docs/GODOT-POC-PLAN.md);离可用客户端还差什么见 [`docs/BACKLOG.md`](docs/BACKLOG.md)(分层未完成清单);把参考图那种整张地图画面做出来的实施规格见 [`docs/SHINSOO-WORLD-RENDERING.md`](docs/SHINSOO-WORLD-RENDERING.md)(正式移植,Phase 2 bring-up 之后); 距参考图「目视等价」还差哪些保真化工作见 [`docs/PARITY-GAP.md`](docs/PARITY-GAP.md)。 --- ## 现状 - [x] **M0'** — 脚手架。GDExtension 加载 + `Metin2Model` 注册 + `libgr2` 链接可调用 + Godot 4.7 工程经 Metal Forward+ 出画。T0.4 实际 `export` 待装 4.7.1 导出模板。 - [x] **M1** — 静态渲染。`gr2` → `Skeleton3D` + `ArrayMesh` + `Skin` + 材质。 `warrior_cheongrin`(75 骨 / 5 表面 / 3324 顶点)正立、朝 +Z、贴图正确 (`test/golden/m1-bindpose.png`)。多骨架:`warrior_lord`(v6,74)、`assassin`(v6,95,17 表面)、 `shaman_lord`(**v7**,93) 均正确加载渲染。 - [x] **M2** — 骨骼动画。`Metin2AnimPlayer` `_process` 里 `gr2::sample_pose`(跨文件) → **默认 CPU 线性混合蒙皮**(把 libgr2 的 `Σ w·invWorld·world` 直接作用到顶点,每帧重建 mesh)。 `get_bone_global_pose` vs `conv(world)` 全骨 ≤2.6e-5;`selfcheck` 0 NaN;`dance_1` 等所有动作 头/护甲/躯干正确,与 xrender-poc bgfx demo 一致。 `MTGODOT_GPUSKIN=1` 走**自写蒙皮顶点着色器**(`m2_material` `SRC_SKIN`:逐骨完整 4×3 矩阵 存 RGBAF 纹理,绕过 `Skeleton3D` —— Godot 内置 GPU 蒙皮会正交化、丢 Granny 烘在 rig 骨上的 shear,warrior 7 根 / **sura 16 根**,见 `test/shear-bones-survey.md`)。人群场景用它。 **`.msa`**:`anim_path` 收 `.msa` → motion `.gr2` + `Accumulation` + `MotionEventData` (`get_events()` / `motion_event` 信号,带循环回绕)+ `LoopData` 元数据;播放器 `loop=false` 可播一次并发 `playback_finished`(片段循环次数执行仍在 backlog C4)。 **`.msm`**:`gr2_path` 收 `.msm` → 自动加载 `BaseModelFileName`(含散包目录扫描)+ `get_hair_options()` 发型目录(发型 mesh 挂接留 Phase 2)。 - [x] **M2.5** — 材质与观感保真。`ShaderMaterial`(modulate tex×COLOR×light、 opaque/alpha/alpha-test/add 四模式)+ 方向光阴影 + procedural sky + 引擎雾。 **贴图走 gr2 material 绑定**:`build_parts()` 按 `tri_groups[].material_index` 把一个 gr2 mesh 拆成多个 Godot surface,各 surface 取 `Mesh::material_textures[matidx]`(mesh-local)→ 同目录大小写不敏感查 `.dds`;找不到才回退文件名启发式。shaman(1→2 面)/assassin(17→18 面) 各面贴图正确。MODULATE2X 等 stage op 仍只近似(差距清单见 `docs/GODOT-POC-PLAN.md` §M2.5 T2.5.6)。 - [~] **中期评审** — [`docs/MIDREVIEW.md`](docs/MIDREVIEW.md)。结论:桥接层工程量小、 风险基本出清,**建议进入 Phase 2**。`compare.py` 已有进程/截图/结构硬门禁,像素差仍为 advisory;收尾重点是前台/真机性能重测与桥接层全语料 draw 冒烟(默认已是 CPU LBS)。 50 角色压力采样:`test/godot-macos-stress.json`(后台窗口 30fps 节流,仅构建成本 ~165ms/角色 和 100 角色破顶到 52ms 是可信信号)。 --- ## 环境 | 组件 | 版本 / 位置 | 说明 | |---|---|---| | Godot | **4.7.1**(`/opt/homebrew/bin/godot`,Homebrew cask) | 编辑器 + headless | | godot-cpp | submodule `extension/godot-cpp` @ `101ae38034304346a46ea9ea84ae156d3e860496` | 精确 gitlink 锁定(`.gitmodules` 不跟踪移动分支);bundled `extension_api-4-7.json` | | Xcode | 26.4.1 | macOS 构建 | | CMake | 已装;**不需要 SCons** | godot-cpp 走 CMake 路径 | | libgr2 / formats / oracle / tools | vendored 进本仓库 | 见下「vendored 库」 | **导出模板**(M0' T0.4 / 真正 export 才需要):编辑器内 `Editor → Manage Export Templates → Download`,或 `godot --headless --install-export-templates`。~600MB,版本须与编辑器一致(4.7.1)。 --- ## 构建 ```bash git submodule update --init --recursive # 拉 godot-cpp(首次) ./build.sh # Debug;产物 → project/bin/libmtgodot.macos.template_debug.dylib ./build.sh Release # template_release ``` 首次会编译 godot-cpp(几分钟)。扩展构建自动带上 vendored `libgr2` + `formats`。 只想构建 `libgr2` / `formats` / 校验工具(不碰 godot-cpp): ```bash cmake -B build-tools -DMTGODOT_BUILD_EXTENSION=OFF -DMTGODOT_BUILD_TOOLS=ON cmake --build build-tools -j8 ./build-tools/tools/gr2fuzz "$PWD/assets" # 全量解析冒烟 ./build-tools/tools/gr2dump ``` ## 运行 ```bash # 完整客户端:登录 → 选人 → 进游戏(用仓库内 assets/) ./run-client.command # 或双击 # = MT_ASSETS=$PWD/assets godot --path project res://client_main.tscn # 打包 .app(需先装 Godot 4.7.1 macOS 导出模板) ./build-macos-client.sh [debug|release] # -> build/export/mtgodot-poc.app # 编辑器打开 godot -e --path project # 模型查看器 harness(旧 main_scene) godot --path project res://main.tscn # headless 冒烟测试(验证 GDExtension 加载 + 类注册) godot --headless --path project --quit-after 3 # 跑一个真实 gr2 过 libgr2(可选) MTGODOT_PROBE_GR2="$PWD/assets/PC/ymir work/pc/warrior/warrior_cheongrin.gr2" \ godot --headless --path project --quit-after 3 ``` 控制台出现 `[mtgodot] Metin2Model registered OK — libgr2 linked; ...` = M0' 的 T0.1/T0.2 通过。 窗口内:拖拽 = 轨道,滚轮 = 缩放,`F2` = 截图(→ `user://`)。 --- ## vendored 库(并入自 xrender-poc,2026-08-29) | 目录 | 内容 | 谁用 | |---|---|---| | `libgr2/` | gr2 v6/v7 只读读取器(header/section/Oodle1/类型树/骨架/网格/曲线/材质)。零第三方依赖。9166 语料 fuzz 0 崩溃;bind pose + 蒙皮对拍 Granny 2.9.12 ≤6.5e-5。含 `dump_materials()`。 | `extension`(`xrender::libgr2`)、`tools` | | `formats/` | `.msa`(动作)/ `.msm`(模型 + 发型)/ textscript 解析。 | M2 T2.2/T2.3 起(retarget + motion event) | | `oracle/` | Wine + MinGW 交叉编译的 Granny 真值工具(`oracle.c` + 脚本 + `RUNBOOK.md`)。`oracle.exe` / `granny2_x64.dll` 不入库(体积 + RAD IP),`build-wine.sh` 本地重建。 | M2 数值门禁、M2.5 材质对拍 | | `tools/` | `gr2dump`(转储)/ `gr2fuzz`(全量解析冒烟)/ `oracle_diff`(libgr2 vs oracle 逐字段对拍)/ `anim_probe`(每骨 world 3×3 的 det/shear/scale 探针,查蒙皮走样)。 | libgr2 回归 | | `docs/reference/` | xrender-poc 的格式逆向笔记(M0–M4 施工文档 + PLAN)。见 `docs/reference/README.md`。 | 查格式细节 | | `test/bgfx-reference/` | bgfx demo 的参考截图 + `noise_floor.json` / `m2-numeric.json` / `assets.list` 基线。 | 与 Godot 输出交叉核对 | CMake:顶层 `add_subdirectory(libgr2)` + `add_subdirectory(formats)`(跨平台,不碰 godot-cpp); `extension` 只 `target_link_libraries(... xrender::libgr2)`。`tools` 由 `-DMTGODOT_BUILD_TOOLS=ON` 开启。 > `libgr2/README.md` 里的 **oodle1.c clean-room 提醒**依然适用:`src/oodle1.c` 是泄露 SDK 端口, > 仅限内部研究 / 非发布 / 非商用;对外发布前必须 clean-room 重写。 --- ## 目录 ``` mtgodot-poc/ CMakeLists.txt 顶层:add_subdirectory(libgr2/formats/[tools]/[extension]) build.sh 便捷构建(扩展) libgr2/ ← vendored:gr2 读取器(xrender::libgr2) formats/ ← vendored:msa/msm/textscript oracle/ ← vendored:Granny 真值工具(exe/dll 不入库) tools/ ← vendored:gr2dump / gr2fuzz / oracle_diff extension/ CMakeLists.txt mtgodot SHARED + godot-cpp(consume xrender::libgr2) godot-cpp/ submodule @ 101ae38034304346a46ea9ea84ae156d3e860496 src/ register_types.{h,cpp} GDExtension 入口 metin2_model.{h,cpp} gr2 → Skeleton3D + ArrayMesh + Skin + 材质 metin2_anim.{h,cpp} 每帧 sample_pose → Skeleton3D;selfcheck gr2_bridge.{h,cpp} libgr2 POD → Godot 类型;basis + 单位换算 m2_material.{h,cpp} Metin2 风格 ShaderMaterial(mix / add) dxt.{h,cpp} DDS DXT1/3/5 软解 assets/ Metin2 资产(.gr2/.dds/locale/OutdoorA1/…,gitignored;换位置设 MT_ASSETS) bgm/ 背景音乐(assets/ 同级,gitignored) project/ Godot 4.7 工程 client_main.tscn/.gd 完整客户端入口(main_scene)→ AppFlow 登录串场 app_flow.gd LOGIN → SELECT → GAME 状态机 main.tscn / main.gd 旧模型查看器 harness asset_root.gd AssetRoot:资源目录解析(MT_ASSETS / res://../assets / .app 内外) docs/ GODOT-POC-PLAN.md 开发计划(M0'–M4 + 风险 + 验收) MIDREVIEW.md Phase 1 中期评审 BACKLOG.md 离可用客户端的分层未完成清单 reference/ ← xrender-poc 的格式逆向笔记(archived) test/ golden/ Godot 对拍截图 bgfx-reference/ bgfx demo 参考截图 + 数值基线 *.json noise_floor / m2-numeric / stress ``` ## 授权 `libgr2` 全自研,不含 Granny SDK 代码 —— 例外:`libgr2/src/oodle1.c` 是泄露 SDK 的解码路径端口 (见 `libgr2/README.md`,仅限内部研究 / 非发布,对外发布前必须 clean-room 重写)。 本仓库内部研究用途,不对外公开。