Adapt Android mobile controls and native client UI

This commit is contained in:
shen
2026-09-28 19:39:13 -07:00
parent a1ae69aa7a
commit 3c52ced02e
180 changed files with 688 additions and 23782 deletions
+22 -179
View File
@@ -1,193 +1,36 @@
# mtgodot-poc
# Metin2 原生客户端
「**Godot 4 + 自研资源 loader**」跨平台方案的渲染 Demo。
用一个 C++ GDExtension 把 Metin2 的 `.gr2` 骨骼资源渲染进 Godot,用 Godot 内置渲染器。
40250 客户端的 macOS / Android 移植。SDL3 负责窗口与输入,Vulkan 负责 3D 和 UI 渲染;macOS 上通过 MoltenVK 使用 Metal。40250 的 C++ 逻辑、Python 2.7 脚本和 EPK 资源由 `extension/src/port` 与 `extension/src/platform` 提供。运行时不依赖 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)。
## macOS
自用项目(内部研究,不对外发布),分两阶段推进:
需要 CMake、SDL3、Vulkan loader、MoltenVK、glslc,以及仓库中的第三方子模块:
| 阶段 | 平台 | 里程碑 | 产出 |
|---|---|---|---|
| **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)。
当前进入游戏、模型/发型、贴地与技能栏问题的分阶段修复步骤,以及结合两个 Godot 项目的
对照验证和 macOS 包验收要求,见 [`docs/RENDERING-REPAIR-PLAN.md`](docs/RENDERING-REPAIR-PLAN.md)
(2026-09-08,实施计划;不表示其中工作已完成)。
---
## 现状
- [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
```sh
git submodule update --init --recursive
cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release \
-DMT_BUILD_NATIVE_RENDER=ON -DCMAKE_PREFIX_PATH=/opt/homebrew
cmake --build build-release --target mt_native_render -j8
./build-release/native_render/mt_native_render \
--live-client "/path/to/40250/Client" --login-screen --width 1024 --height 768
```
首次会编译 godot-cpp(几分钟)。扩展构建自动带上 vendored `libgr2` + `formats`。
详见 [native_render/README.md](native_render/README.md)。
只想构建 `libgr2` / `formats` / 校验工具(不碰 godot-cpp):
## Android
```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>
```sh
./build-android-native.sh Release --install
./push-android-client.sh /path/to/40250/Client
adb shell am start -n org.metin2port.client/.MainActivity
```
## 运行
APK 由 SDLActivity、`libSDL3.so` 和 Vulkan 原生 `libmain.so` 组成。40250 `Client` 资源单独复制到应用数据目录;`--live-server HOST:AUTH_PORT:GAME_PORT --login-screen` 可连接服务器。构建与启动细节见 [native_render/README.md](native_render/README.md)。
```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
```sh
./script/port_gate.sh macos
```
控制台出现 `[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 重写)。
本仓库内部研究用途,不对外公开。
`port_gate.sh` 构建并运行 40250 移植层的 C++ 测试。原生渲染验收入口是 `script/native_mac_acceptance.mjs`。历史迁移记录保留在 `docs/PORT-PLAN.md`;其中旧 Godot 路径已经退役。