Networked client on the existing Godot 4.7 + libgr2 renderer:
- net: m2dev wire protocol (libsodium KX + XChaCha20), auth/select/game
phases, EntityStore world model, ~all GC/CG headers. char create/delete,
private shop / mall / cube, SHOP_GC_START_EX, guild, party (+ CG_PARTY_SET_STATE),
quests, dragon soul, refine, safebox, exchange.
- UI: in-game windows migrated 1:1 from the reference uiscript/root .py —
char status (/stat), inventory+equipment, select-item ([SELECT_ITEM] quest
token), system-option + game-option + ESC system menu, private-shop 39-grid,
party info board, shop tabs, atlas, minimap, quickbar, chat, …
- EterGrnLib polish: GR2 material blend/two-sided, LOD crossfade, motion-event
dispatch, contact shadow, ray-AABB picking, weapon grip pre-transform.
Portable asset IO (A1) — all extension/libgr2/formats/mtproto reads routed
through godot::FileAccess (res:// PCK works on iOS/Android); standalone-lib
*_path() kept for the non-Godot CTests. AssetResolver + PropertyRegistry
switched to a baked index (bake_asset_index.gd) instead of std::filesystem.
Mobile builds: build-{android,ios}.sh, export-android.sh, pack-assets.sh,
gen-debug-keystore.sh. Assets ship as a zip mounted at runtime by
project/asset_pack.gd (adb push now; HTTP download is a drop-in later).
ctest 10/10, 34 GDScript suites, macOS/iOS/Android all build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EJxkHiNKS4kybHS3XKyAJ
190 lines
11 KiB
Markdown
190 lines
11 KiB
Markdown
# 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 <file.gr2>
|
||
```
|
||
|
||
## 运行
|
||
|
||
```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 重写)。
|
||
本仓库内部研究用途,不对外公开。
|