按 docs/ 目录逐篇核对,只改与事实/路径不符的部分,不动尚未验证的计划条目: - `docs/steps/**` 在早前整理时已迁到 `docs/reference/steps/**`,全仓的旧路径 引用(PLAN.md / README.md / M0-gr2-reader.md 及 libgr2 的头注释)一并改正。 - CLIENT-GAP.md / CLIENT-GAP-FIX.md / BACKLOG.md / MIDREVIEW.md:把已经落地的 条目从「待办」改为已完成,删掉与代码现状矛盾的描述。 - ANDROID-TESTING.md / CLIENT-PORT.md / CLIENT-ROADMAP.md / GODOT-POC-PLAN.md / SHINSOO-WORLD-RENDERING.md / PARITY-GAP.md:同上,另补当前实际的构建/测试入口。 - THIRD-PARTY.md:补齐实际在用的第三方来源与许可说明。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJugvEJwz3FK4hw9ti3SRb
332 lines
27 KiB
Markdown
332 lines
27 KiB
Markdown
# Godot 渲染 Demo — 开发计划
|
||
|
||
> 内部研究方案,不对外公开。
|
||
>
|
||
> **2026-08-29 更新**:并行的 bgfx 自研引擎方案(`xrender-poc` / [`reference/PLAN.md`](./reference/PLAN.md))
|
||
> 经中期评审([`MIDREVIEW.md`](./MIDREVIEW.md))**停止开发**,本方案成为唯一在研路线。
|
||
> `libgr2` / `formats` / `oracle` / `tools` 已从 xrender-poc **vendored 进本仓库**
|
||
> (`../libgr2`、`../formats`、`../oracle`、`../tools`),不再是 submodule / sibling 引用。
|
||
> 下文中出现的 `xrender-poc/xxx` 路径按 [`reference/README.md`](./reference/README.md) 的映射表读。
|
||
> bgfx demo 截图作为交叉核对基线留在 `../test/bgfx-reference/`。
|
||
|
||
| | |
|
||
|---|---|
|
||
| 状态 | **已被工作范围超越(2026-09-08 复核)**。Phase 1(macOS)完成,见 [`MIDREVIEW.md`](./MIDREVIEW.md);Phase 2 的 iOS / Android 交叉编译与 APK 导出也已完成(`BACKLOG.md` F1/F2、[`PLATFORMS.md`](./PLATFORMS.md)),故文中「Phase 2 本轮不做」不再成立。§01「明确排除」清单已大面积失效,见该节的复核注。当前工作以 [`BACKLOG.md`](./BACKLOG.md)、[`CLIENT-GAP.md`](./CLIENT-GAP.md)、[`RENDERING-REPAIR-PLAN.md`](./RENDERING-REPAIR-PLAN.md) 为准 |
|
||
| 技术栈 | Godot **4.7.1** stable · GDExtension(`godot-cpp` 精确提交 `101ae38`,4.7 API)· C++20 |
|
||
| 目标平台 | **Phase 1:macOS**(当前)→ Phase 2:Android(一加 13 / Vulkan)· iOS(iPhone 16 / Metal)。自用,三台现代设备,不涉及 GLES3 / Compatibility |
|
||
| 判定周期 | Phase 1 ≈ 3.5–5 周(单人)→ **中期评审**;Phase 2 = 三设备各一次 bring-up + 性能留档(自用,无对外 go/no-go) |
|
||
| 复用 | `libgr2`(gr2 v6 读取器)· `formats/`(textscript · msa · msm)· `oracle/` golden · bgfx demo 截图 |
|
||
| 自研 | `Metin2Model` GDExtension 节点(gr2 → `Skeleton3D` + `ArrayMesh` + 材质) |
|
||
|
||
---
|
||
|
||
## 00 · 阶段划分
|
||
|
||
| 阶段 | 平台 | 里程碑 | 回答什么 | 产出 |
|
||
|---|---|---|---|---|
|
||
| **Phase 1** | macOS | M0' · M1 · M2 · **M2.5** | ① gr2 → Godot 场景对象能否正确映射;② 动画运行时(采样 / retarget / 事件)能否移植;③ **Metin2 观感能否在 Godot 里还原** | 中期评审报告:桥接层工作量实测 + 观感对比 + 剩余风险 |
|
||
| **Phase 2** | Android(一加 13)· iOS(iPhone 16) | M3(·M4 可选) | ④ 三端渲染一致;⑤ 两台移动设备性能 / 生命周期 | 三设备 bring-up 报告 + 性能留档 |
|
||
|
||
**关键澄清**:Godot 在 macOS 上渲染一个蒙皮 gr2 **几乎不可能失败**,Phase 1 的价值是把**平台无关**的 GDExtension 工程(占工程量大头)做完并确认视觉保真度可达。bgfx 自研引擎方案已于 2026-08-29 停止开发,Godot 是唯一在研路线;本项目自用、不对外发布,**没有"押哪条路线"的产品决策**。Phase 2 只剩"三台目标设备(Mac / 一加 13 / iPhone 16)上能不能跑 + 性能够不够自己用"。
|
||
|
||
**进入 Phase 2 的前置**:M2.5 门禁通过、中期评审确认桥接层与观感可接受即可推进。三台目标设备都是现代硬件,全程不涉及 GLES3 / Compatibility 渲染器,不做中低端 / 老机器。
|
||
|
||
---
|
||
|
||
## 01 · 目标与判定标准
|
||
|
||
### Phase 1 要证明的三件事(macOS)
|
||
|
||
1. **gr2 → Godot 场景对象** — `libgr2` 读出的骨架 / 蒙皮网格 / 权重能否正确映射到 `Skeleton3D` + `ArrayMesh` + `Skin`,覆盖多种骨架(不止 warrior)。
|
||
2. **动画正确** — 每帧从 `libgr2::sample_pose` 驱动 `Skeleton3D`,结果与 bgfx demo / Granny oracle 逐帧一致;跨文件 retarget、motion event 成立。
|
||
3. **观感可还原** — 多 texture-stage 材质、alpha test、顶点色、additive 混合、fog、方向光阴影在 Godot 里做出来,与原客户端目视接近。
|
||
|
||
### Phase 2 要证明的两件事(移动端,本轮不做)
|
||
|
||
4. **三端一致** — 同一 GDExtension + Godot 工程在 iPhone 16(Metal)+ 一加 13(Vulkan)渲染与 Mac 一致。
|
||
5. **性能 / 生命周期够自用** — 两台设备单角色 / 多角色帧率可接受(旗舰基线,非发布门槛),后台 / 恢复稳,Godot 节点开销可控。
|
||
|
||
### 明确排除(两个阶段都不做)
|
||
|
||
> **⚠️ 本清单已失效(2026-09-08 复核)。** 下列 9 项中 8 项后来都做了 —— POC 判定通过后项目
|
||
> 继续做成了完整客户端,范围扩张是有意的(`SHINSOO-WORLD-RENDERING.md` 的「定位」一节最早
|
||
> 记录了 UI 这一条)。逐项现状:
|
||
>
|
||
> | 原「不做」项 | 现状 | 落点 |
|
||
> |---|---|---|
|
||
> | SpeedTree 植被 | 🟡 已做 proxy | `formats/spt.cpp`,`BACKLOG` E10 |
|
||
> | 地形(Metin2 户外地图) | ✅ 已做 | `Metin2World` + `formats/terrain_mesh.cpp`,`BACKLOG` E8/E11/E12 |
|
||
> | UI / Python 脚本层 | ✅ 已做 | `project/ui/*`(uiscript 装载器 + 多个 1:1 窗口移植) |
|
||
> | eterpack | ✅ 已做 | `mtpack` + `packtool`,`BACKLOG` G1 |
|
||
> | GPU 蒙皮优化 | ✅ 已做 | `MTGODOT_GPUSKIN=1`,`m2_material` `SRC_SKIN` |
|
||
> | LOD 策略 | ✅ 已做 | `lod_distances` + crossfade,`BACKLOG` C9 |
|
||
> | 多角色 AI | ⬜ 仍不做 | —— |
|
||
> | 网络 | ✅ 已做 | `mtnet`(m2dev + 40250 classic 双后端) |
|
||
> | 玩法 | ✅ 大量已做 | `CLIENT-GAP.md` / `CLIENT-ROADMAP.md` P0–P10 |
|
||
|
||
SpeedTree 植被、地形(Metin2 户外地图)、UI / Python 脚本层、eterpack(Phase 1 用散文件)、GPU 蒙皮优化、LOD 策略、多角色 AI、网络、玩法。
|
||
|
||
> 地形与 `.mse` 特效在 Phase 1 也不做,但 **M2.5 会做通用 `ShaderMaterial` + 混合模式的地基**,为后续正式移植时的地形 / 特效渲染铺路。
|
||
|
||
### 判定口径
|
||
|
||
- **M2.5 门禁通过 + 中期评审"可接受"** = 推进 Phase 2。中期评审已判定桥接层工作量与观感均可接受([`MIDREVIEW.md`](./MIDREVIEW.md));bgfx route 已停,无「转回」分支。
|
||
- **Phase 2 若撞上方案性死路**(自写蒙皮着色器在 iPhone 16 / 一加 13 上跑不出、Godot 节点开销在自用规模下就卡)——记录清楚、就地想办法(回落 CPU LBS / `RenderingServer` 直调 / uniform 传骨骼),不再有另一条引擎路线兜底。
|
||
- **Phase 2 末交付**:三设备 bring-up + 性能留档报告(自用参考,不做对外选型结论)。
|
||
|
||
### 与 bgfx route 的关系(历史对照,bgfx route 已于 2026-08-29 停止开发)
|
||
|
||
| | bgfx route(`PLAN.md`) | Godot route(本文件) |
|
||
|---|---|---|
|
||
| 渲染器 | 自研(bgfx 薄封装 + `.sc` 着色器) | Godot 内置渲染器 |
|
||
| 平台壳 / 输入 / 音频 / 打包 | 自己拼(SDL3 / 薄壳 + 各库) | Godot 全包 + 编辑器 |
|
||
| gr2 解析 | `libgr2` | `libgr2`(同一份) |
|
||
| 动画运行时 | 自研(复用 EterGrnLib 逻辑) | 映射到 `Skeleton3D` / `AnimationMixer` |
|
||
| 阴影 / 环境 / sky | 自研 | Godot 内置(M2.5 直接用) |
|
||
| 长期 | 自己养一个引擎 | 跟随 Godot 版本(GDExtension ABI 绑 minor) |
|
||
|
||
**`libgr2` 在两条路里都是核心,完整复用。**
|
||
|
||
---
|
||
|
||
## 02 · 架构总览
|
||
|
||
```
|
||
metin2-client/ # 建仓时叫 mtgodot-poc,后更名
|
||
extension/ GDExtension(C++,godot-cpp 4.7)
|
||
src/
|
||
register_types.cpp 注册 Metin2Model / Metin2AnimPlayer
|
||
metin2_model.{h,cpp} load_gr2 → Skeleton3D + ArrayMesh + Skin + material
|
||
metin2_anim.{h,cpp} 每帧 libgr2::sample_pose → Skeleton3D bone pose;跨文件 retarget;motion event
|
||
gr2_bridge.{h,cpp} libgr2 POD 视图 → Godot 数组打包;basis + 单位换算(集中一处)
|
||
dds_loader.{h,cpp} DDS → Godot Image(散文件路径用;res:// 里的 .dds 交 Godot 原生导入)
|
||
m2_material.{h,cpp} texture-stage 描述 → ShaderMaterial(M2.5)
|
||
(libgr2 / formats 在仓库根,顶层 CMake add_subdirectory 进来)
|
||
SConstruct / CMakeLists godot-cpp 构建(Phase 1 只出 macOS arm64)
|
||
project/ Godot 4.7.1 demo 工程
|
||
main.tscn orbit 相机 + DirectionalLight3D(+shadow) + WorldEnvironment(sky) + Metin2Model
|
||
main.gd 加载资产、切模型 / 动画、截图、性能 HUD
|
||
assets/ warrior + 另 2 种骨架的 gr2/dds
|
||
.gdignore ← 让 Godot 不去 import .gr2(见下)
|
||
export_presets.cfg macOS(Phase 1)
|
||
test/
|
||
golden/ godot-macos-*.png
|
||
compare.py 与 test/bgfx-reference/ 参考截图 + oracle 对拍
|
||
docs/
|
||
GODOT-POC-PLAN.md 本文件
|
||
steps/ 按需拆
|
||
```
|
||
|
||
### 分层(Phase 1)
|
||
|
||
| 层 | 内容 |
|
||
|---|---|
|
||
| 平台 / 窗口 / 输入 / 打包 | **Godot**(macOS) |
|
||
| 渲染器 | **Godot 内置** Forward+(macOS / Metal) |
|
||
| 资源运行时 | **`Metin2Model` GDExtension**:gr2 → `Skeleton3D` / `ArrayMesh` / `Skin` / `ShaderMaterial` |
|
||
| 动画 | 每帧 `libgr2::sample_pose` 写 `Skeleton3D`(**B 方案**);A/B 决策**推迟到 Phase 2**(Mac 上信号不足),M2 只做一次 50 角色压力采样留档 |
|
||
| 解析 | **`libgr2`(复用)** · `formats/`(msa/msm,复用) · DDS |
|
||
| 数学 / 坐标 | Godot `Transform3D` / `Basis`;gr2 art-tool basis → Godot 右手 Y-up + 单位缩放,在 `gr2_bridge` 一次性处理,约定见 §03 |
|
||
|
||
### 两个实操约定(Phase 1 T1 前定死,写进 `gr2_bridge` 注释)
|
||
|
||
- **`.gr2` 不进 import 流程**:`project/assets/` 放 `.gdignore`,`.gr2` 由 `Metin2Model` 在运行时用绝对路径 / `user://` 直接读;`.dds` 可留在 `res://` 交 Godot 原生导入,或也走运行时 `dds_loader`(二选一,T1.3 决定并记录)。
|
||
- **单位缩放**:定 `M2_TO_GODOT = 0.01`(Metin2 约 100 unit ≈ 1 Godot m),相机 near/far、`DirectionalLight3D` 阴影范围按此设。若与资产实测不符,在 T1.1 调整并记录。
|
||
|
||
---
|
||
|
||
## 03 · 技术选型
|
||
|
||
| 项 | 选择 | 理由 |
|
||
|---|---|---|
|
||
| 引擎版本 | **Godot 4.7.1 stable**(本机已装),锁死不随手升 | GDExtension ABI 绑 minor;升级作独立任务 |
|
||
| 扩展机制 | `godot-cpp` **`101ae38034304346a46ea9ea84ae156d3e860496`**,GDExtension(非模块编译) | gitlink 精确锁定,目标 API 4.7;升级作独立任务 |
|
||
| 构建工具 | `godot-cpp` 的 **CMake 路径**(本机已有 cmake;不装 SCons)或 `brew install scons` 二选一 | Phase 1 只出 macOS arm64,摩擦最小 |
|
||
| gr2 解析 | `xrender-poc/libgr2` 静态库嵌入 extension | 与 bgfx route 同一份,已过 9166 fuzz + oracle |
|
||
| 动画方案 | **B(每帧写骨骼)**;A(烘 `Animation`)**决策推迟到 Phase 2** | B 直接复用 `libgr2` 采样、最保真;A 的动机(多角色 CPU)在 Mac 上压不出来 |
|
||
| 骨骼权重 | `ArrayMesh` 4 权重优先;>4 影响则 `ARRAY_FLAG_USE_8_BONE_WEIGHTS` | M1 T1.2 早验,三种骨架都查 |
|
||
| 贴图 | 见 §02 实操约定(`.dds` 原生导入 vs 运行时 `dds_loader`,T1.3 定) | 两条都留 |
|
||
| 材质 | **自定义 `ShaderMaterial`**(`light()` 自定义着色贴 Metin2 观感);M1 先 `StandardMaterial3D` 上屏,M2.5 换 | 多 stage 只做近似,列差距清单 |
|
||
| 渲染器 | Forward+ / Mobile renderer | 三台目标设备(Mac / 一加 13 / iPhone 16)都是现代硬件,全程不涉及 Compatibility(GLES3) |
|
||
| 阴影 / sky | Godot 内置 `DirectionalLight3D` shadow + `WorldEnvironment` procedural sky,M2.5 接入 | 成本近零,Mac 观感必要 |
|
||
| 资产来源 | 从 `assets/` 取 **warrior + 另 2 种骨架** 的 gr2/dds 到 `project/assets/`,散文件 | 不接 eterpack(Phase 2 / M4) |
|
||
| 真值源 | `xrender-poc/oracle` golden + bgfx demo 同机位 / pose / t 截图 | 复用现成对拍基建 |
|
||
| 仓库 | 新建 `mtgodot-poc/`,`libgr2` / `formats` submodule 指向 `xrender-poc` | 隔离,不污染 bgfx route |
|
||
|
||
---
|
||
|
||
## 04 · 里程碑
|
||
|
||
### M0' · 脚手架(macOS,1–2 天)
|
||
|
||
**目标**:GDExtension 骨架 + `libgr2` 集成 + Godot 工程能跑,仅 macOS。
|
||
|
||
| 任务 | 完成判据 |
|
||
|---|---|
|
||
| T0.1 `godot-cpp` 4.7 GDExtension 骨架,注册空节点 `Metin2Model`(继承 `Node3D`) | Godot 4.7.1 编辑器里能添加该节点、无报错 |
|
||
| T0.2 构建集成 `libgr2` 静态库(CMake 或 SCons),仅 macOS arm64 | `libgr2` 符号进 extension,`.dylib` 加载成功 |
|
||
| T0.3 Godot 工程骨架:`main.tscn`(orbit 相机 + `DirectionalLight3D` + `WorldEnvironment` + 空 `Metin2Model`),`main.gd`(orbit 控制 + 截图键) | 编辑器运行出带 sky 的场景 + 一个 cube,鼠标可轨道 |
|
||
| T0.4 `project/assets/.gdignore`;`export_presets.cfg` 加 macOS preset | 导出一次 macOS `.app`,空场景能跑 |
|
||
|
||
**门禁 M0'**:macOS 上跑起「Godot 场景 + sky + cube + orbit 相机」,导出的 `.app` 也能跑。
|
||
|
||
---
|
||
|
||
### M1 · 静态渲染(1–1.5 周)
|
||
|
||
**目标**:warrior + 另 2 种骨架的 bind pose + 贴图在 Godot 里正确显示,多部件组装。
|
||
|
||
对标 [`reference/steps/M1-static-render.md`](./reference/steps/M1-static-render.md) T2–T5。
|
||
|
||
| 任务 | 完成判据 |
|
||
|---|---|
|
||
| T1.1 `gr2_bridge`:`libgr2::Skeleton` → `Skeleton3D`(bone 名 / parent / rest `Transform3D`);basis + 单位缩放在此处理,约定写进注释 | 骨架层级树与 `gr2dump --members` 一致;三种骨架的骨骼数都正确 |
|
||
| T1.2 `libgr2::Mesh` → `ArrayMesh`(`ARRAY_VERTEX/NORMAL/TANGENT/TEX_UV/BONES/WEIGHTS`)+ `Skin`(bind 矩阵);处理 >4 权重 | 白模轮廓 = Blender 里同模型;无破面;三种骨架都过 |
|
||
| T1.3 贴图路径决策(`.dds` 原生导入 vs 运行时 `dds_loader`),实现选定方案;warrior 贴图(实测 512×512 DXT3 5mip) | UV 无错位 / 无镜像;决策写进 `extension/README.md`(**该文件未产出**,2026-09-08 复核;决策实际记录在 `MIDREVIEW.md` §3「贴图」与 `extension/src/dxt.cpp` 注释里) |
|
||
| T1.4 材质:`StandardMaterial3D`(albedo 贴图 + unshaded 先看轮廓),可切白模 | 贴图目视正确 |
|
||
| T1.5 多部件(body / weapon / hair)组装,武器挂 `BoneAttachment3D` | 部件数正确、武器跟手骨 |
|
||
| T1.6 `test/compare.py`:与 bgfx demo 同机位截图 | 几何一致(允许色彩空间 / 光照差异) |
|
||
|
||
**门禁 M1**:warrior + 2 种骨架 bind pose 下与 bgfx demo 同机位截图**几何像素级接近**;顶点数、部件数、骨骼数与 oracle dump 一致。
|
||
|
||
---
|
||
|
||
### M2 · 骨骼动画(1.5–2 周)
|
||
|
||
**目标**:从 GDExtension 每帧驱动 `Skeleton3D` 播动画,含跨文件 retarget 与 motion event。
|
||
|
||
对标 [`reference/steps/M2-anim-skinning.md`](./reference/steps/M2-anim-skinning.md)。
|
||
|
||
| 任务 | 完成判据 |
|
||
|---|---|
|
||
| T2.1 `Metin2AnimPlayer`:`_process` 里 `libgr2::sample_pose(t)` → 写 `Skeleton3D` 每骨 local `Transform3D` | 单个动画连续播放、不抖 |
|
||
| T2.2 跨文件 retarget:骨架 gr2 + 独立动画 gr2 / `.msa` 按骨名对齐(必要时 `RetargetModifier3D` / 骨名映射表) | warrior 骨架播 `dance_1`(独立文件)姿势正确 |
|
||
| T2.3 motion event:`.msa`/`.msm` 里的帧事件(放音 / 特效锚点 / 震屏)解析并派发(Phase 1 只需回调打点,不接实际特效) | 事件在正确帧触发,日志可见 |
|
||
| T2.4 `main.gd`:idle + 一个动作,按键 / HUD 按钮切换 | 切换无爆姿势 |
|
||
| T2.5 逐帧对拍:同 `t` 的 `Skeleton3D` 世界骨骼矩阵 vs bgfx demo / oracle | 差值在坐标系 / 单位换算误差内(沿用 M2 的 ~6.5e-5 量级口径);全程无 NaN |
|
||
| T2.6 全量采样自检:181 个 warrior 动画逐个 load + sample | 0 崩溃、0 NaN |
|
||
| T2.7 留档:50 角色同播动画的帧时间 / `_process` 耗时采样(**不作门禁**,给 Phase 2 A/B 决策用) | 数据写进 `test/godot-macos-stress.json` |
|
||
|
||
**门禁 M2**:181 动画全部加载采样无 NaN;选定动作与 bgfx demo 同 `t` 视觉一致、数值在换算误差内;motion event 触发帧正确。
|
||
|
||
---
|
||
|
||
### M2.5 · 材质与观感保真(1–1.5 周)★ Phase 1 的核心门禁
|
||
|
||
**目标**:把 Metin2 的固定管线材质在 `ShaderMaterial` 里近似出来,让 Mac 版渲染"看起来做好了"。
|
||
|
||
| 任务 | 完成判据 |
|
||
|---|---|
|
||
| T2.5.1 `m2_material`:Metin2 材质描述(texture-stage op / arg / alpha op)→ 自定义 `ShaderMaterial`(Godot shading language,`light()` 自定义) | 单 stage `MODULATE(TEXTURE, DIFFUSE)` = `tex * vertex_color` 与 bgfx demo `texture_stage.sh` 输出一致 |
|
||
| T2.5.2 多 stage 近似:`MODULATE2X` / `ADD` / `BLENDDIFFUSEALPHA` 等常见组合 | warrior 全套贴图正确显示,与原客户端目视接近 |
|
||
| T2.5.3 alpha test(`D3DCMP_*`)+ alpha blend + additive(装备发光) | 半透明 / 发光部件正确,排序无明显错误 |
|
||
| T2.5.4 顶点色 + fog(线性 / exp2) | 与 bgfx demo 对拍一致 |
|
||
| T2.5.5 接 Godot `DirectionalLight3D` 阴影 + `WorldEnvironment`(procedural sky + ambient) | 角色有投影、受环境光;整体画面完整 |
|
||
| T2.5.6 差距清单:列出无法 1:1 还原的项(排序 corner case、特定 blend op、mip / 各向异性差异) | 写进中期评审报告 |
|
||
|
||
**门禁 M2.5**:warrior 完整着装 + 动画,在 Godot 里与原客户端 / bgfx demo **目视接近**(结构化对比 + 直方图,不做像素比);差距清单里没有"方案性死路"项。
|
||
|
||
---
|
||
|
||
### M3 ·(Phase 2,本轮不做)Android + iOS bring-up + 性能
|
||
|
||
一加 13(Vulkan)+ iPhone 16(Metal)真机跑起来、三端一致、性能留档、生命周期。iOS 需把 GDExtension 按 arm64 **静态库**编出来(链进 app 二进制)。不涉及 Compatibility 渲染器。详见后续 `steps/M3-mobile.md`(届时再写)。
|
||
|
||
### M4 ·(Phase 2 可选拉伸)
|
||
|
||
接真实 eterpack、试水一个 `.mse` 特效、多 texture-stage 精确对拍。
|
||
|
||
---
|
||
|
||
## 05 · 工作量(Phase 1,macOS)
|
||
|
||
| 里程碑 | 单人 | 2 人并行 |
|
||
|---|---|---|
|
||
| M0' | 1–2 天 | 1 天 |
|
||
| M1 | 1–1.5 周 | 1 周(网格 / 骨架分工) |
|
||
| M2 | 1.5–2 周 | 1–1.5 周(动画 / 对拍分工) |
|
||
| M2.5 | 1–1.5 周 | 1 周(shader / 材质映射分工) |
|
||
| **Phase 1 合计** | **≈ 3.5–5 周** | **≈ 2.5–3.5 周** |
|
||
|
||
前置依赖:`xrender-poc` 的 `libgr2`(已完成)、`oracle` golden(M2 对拍要)、bgfx demo(M1/M2/M2.5 截图对拍要,已可跑)、Godot 4.7.1 export templates(需在编辑器内下载)。
|
||
|
||
---
|
||
|
||
## 06 · 风险登记册
|
||
|
||
### Phase 1(macOS)
|
||
|
||
| 风险 | 触发信号 | 缓解 / 退路 |
|
||
|---|---|---|
|
||
| `godot-cpp` + `libgr2` 构建集成摩擦 | M0' 链接 / 加载失败 | 只出 macOS arm64,摩擦面最小;CMake 路径优先,SCons 备选 |
|
||
| `.gr2` 被 Godot import 流程扫到报错 | 打开工程时一堆 import 错误 | `project/assets/.gdignore`;`.gr2` 走运行时绝对路径 / `user://`,T1 前定死 |
|
||
| gr2 basis / 单位换算到 Godot(左手 → 右手 Y-up + 缩放) | M1 模型躺 / 镜像 / 骨骼反向 / 大小离谱 | `gr2_bridge` 一次性处理,定 `M2_TO_GODOT`,用朝向明确资产手校,写注释 |
|
||
| `Skeleton3D` 骨骼数上限 / 8 权重路径行为 | M1 T1.2 蒙皮爆开 / 部位错位 | 三种骨架都查 `gr2dump`;>4 权重走 8-bone flag |
|
||
| **多 stage 固定管线材质无法在 `ShaderMaterial` 近似到可接受** | M2.5 T2.5.2/3 关键装备观感明显错 | **这是 Phase 1 主要方案性风险**;差距清单区分"可延期"vs"死路"。中期评审已判定为可延期(观感打磨,见 `MIDREVIEW.md`);bgfx route 已停,无「转回」分支 |
|
||
| 半透明 / additive 排序 corner case | M2.5 T2.5.3 发光部件穿插 | 记入差距清单;Godot 的 `render_priority` / `depth_draw` 调,不追求 100% |
|
||
| motion event 语义(`.msa`/`.msm` 帧事件类型) | M2 T2.3 事件类型没覆盖全 | Phase 1 只需打点回调,实际特效 Phase 2;未覆盖类型列清单 |
|
||
|
||
### Phase 2(移动端,本轮不激活,仅登记)
|
||
|
||
| 风险 | 缓解 / 退路 |
|
||
|---|---|
|
||
| 每帧 CPU LBS 全量重建 mesh 的成本(多角色) | `MTGODOT_GPUSKIN=1` 自写蒙皮着色器(已实现);多线程构建 + eterpack 共享资产 |
|
||
| 自写蒙皮着色器(`texelFetch` + RGBAF + `BONE_INDICES`)在 iPhone 16 Metal / 一加 13 Vulkan 上的行为 | Phase 2 各跑一次;出问题回落 CPU LBS 或改 uniform 数组传骨骼。三台都是现代设备,无 GLES3 约束 |
|
||
| Godot 节点开销(大场景规模) | `MultiMesh` / `RenderingServer` 直调兜底 |
|
||
| GDExtension ABI 绑 Godot minor | 锁 4.7.1,升级作独立任务 |
|
||
| iOS:GDExtension 须静态链接进 app 二进制(`libgr2` 出 iOS arm64 `.a`),签名 / 模板脾气大 | 自用免费 provisioning 够用;预留 2–3 天 bring-up |
|
||
|
||
---
|
||
|
||
## 07 · 验证方法论
|
||
|
||
- **真值源**:`xrender-poc/oracle` 的 Granny golden 数据 + `xrender-poc` bgfx demo(同机位 / pose / `t` 截图与数值 dump,Metal 后端在同一台 Mac 上跑)。
|
||
- **每个门禁**存 `test/golden/godot-macos-<case>.png` + 骨骼矩阵 / 顶点 dump,跑 `test/compare.py` 与 bgfx route 对拍。
|
||
- **资产集**:`xrender-poc/test/assets.list` 里的 warrior 相关文件 + 另选 2 种骨架(记入本仓库 `test/assets.list`)。
|
||
- **对拍容差**:几何 / 骨骼矩阵按坐标系 + 单位换算误差内严格比;颜色 / 光照因渲染器不同**不做像素比**,只做结构化目视 + 直方图。
|
||
|
||
---
|
||
|
||
## 08 · 交付物
|
||
|
||
### Phase 1(本轮)
|
||
|
||
| 产物 | 位置 |
|
||
|---|---|
|
||
| `mtgodot-poc/` 仓库:GDExtension 源 + Godot 4.7.1 demo 工程 | 新仓库 |
|
||
| macOS 导出配置 | `project/export_presets.cfg` |
|
||
| 与 bgfx demo 的几何 / 动画对拍报告 | ~~`test/godot-snapshot.json`~~ **未产出**(2026-09-08 复核)。实际落地的是 `test/compare.py` + `test/compare-report.json`(进程 / 截图 / 结构硬门禁),与 bgfx 的比对停留在 `test/bgfx-reference/` 的人工交叉核对 |
|
||
| 材质差距清单 | 中期评审报告内 |
|
||
| 50 角色压力采样(供 Phase 2 A/B 决策) | `test/godot-macos-stress.json` |
|
||
| **中期评审报告** | 桥接层工作量实测 + 观感对比截图 + 剩余风险 + "是否进入 Phase 2"建议 |
|
||
|
||
### Phase 2(后续)
|
||
|
||
| 产物 |
|
||
|---|
|
||
| iOS `.ipa`(iPhone 16)+ Android APK(一加 13)+ 两台真机性能 / 生命周期报告 |
|
||
| 三设备 bring-up 报告:三端渲染一致性截图 + 各设备帧率留档(自用参考,不做对外选型结论) |
|
||
|
||
---
|
||
|
||
## 选型变更记录
|
||
|
||
- 2026-08-29 · **设备范围定死(自用)**:目标 = Mac(macOS / Metal)+ 一加 13(Android arm64 / Vulkan)+ iPhone 16(iOS arm64 / Metal),各一台。取消对外 go/no-go 门禁(bgfx route 已停、项目自用);Phase 2 收敛为「三设备各一次 bring-up + 性能留档」。**全程不涉及 GLES3 / Compatibility 渲染器**,不做中低端 / 老机器。SHINSOO 的 W0→W1+ 因此无外部门禁。
|
||
- 2026-08-29 · 范围收敛为 **Phase 1 = macOS only**;新增 **M2.5 材质与观感保真** 作为 Phase 1 核心门禁;动画 A/B 决策推迟到 Phase 2;Godot 版本钉死 4.7.1 / godot-cpp 4.7。
|
||
- 2026-08-29 · **M1 T1.3**:贴图走 **运行时 `dds_loader`**(`src/dxt.cpp`,端口自 xrender-poc)。理由:Godot 4 的 `Image.load()` 不支持 `.dds`,且资产从 `res://` 外的绝对路径加载(`.gr2` 走 `.gdignore` 同理)。DXT1/3/5 + BGRA8,level 0 + `generate_mipmaps()`。
|
||
- 2026-08-29 · **动画方案**:B 方案(每帧 `gr2::sample_pose` 驱动)。**两条蒙皮路径**,都用 libgr2 的 `skin` 矩阵完整仿射(shear 保留):默认 **CPU LBS**(`metin2_anim.cpp` → `Metin2Model::cpu_skin`,每帧重建 `ArrayMesh`);`MTGODOT_GPUSKIN=1` → **自写蒙皮顶点着色器**(`m2_material` `SRC_SKIN`,逐骨 4×3 矩阵存 RGBAF 纹理,`Metin2Model::gpu_skin` 每帧更新,绕过 `Skeleton3D`)。原因:Godot 内置 GPU 蒙皮 / `set_bone_pose` 会正交化骨骼矩阵、**丢掉 Granny 烘在 rig 骨(`bone_front_*`/`Bone_shoulder_*`;warrior 7 / sura 16)上的 shear**(`tools/anim_probe` 定位,非 libgr2 —— `world[]` 对拍 Granny ≤4.1e-5)。旧 `Skeleton3D::set_bone_pose` 路径已删。A 方案(烘 `Animation`)走 `Skeleton3D` 骨骼、同样丢 shear,不用。
|
||
- 2026-08-29 · **坐标/单位**:`gr2_to_godot()` = gr2 行主序 Mat4 的 4×4 转置 → Godot `Transform3D`;Z-up cm → Y-up m(`rotate(-90°,X)` + `scale(0.01)`)放在 `Metin2Model` 节点自身 transform(`make_conv`)。warrior/warrior_lord/assassin/shaman_lord 实测 `flip_z=false`、`flip_winding=false`、`unit_scale=0.01` 正立、朝 +Z、无镜像。
|
||
|
||
### M2.5 T2.5.6 — 材质无法 1:1 还原 / 待办项清单
|
||
|
||
> **不再单独维护** —— 全部并入 [`BACKLOG.md`](./BACKLOG.md) B 段(`B1`–`B11`)。下表为历史快照。
|
||
|
||
| 项 | 现状 | 影响 | 出路 |
|
||
|---|---|---|---|
|
||
| **libgr2 POD API 不暴露材质数据** —— 只有 `material_count`/`texture_count`,无材质名、无贴图文件名、无 texture-stage op、无 `D3DBLEND_*`/alpha-test 标志 | 贴图靠**文件名约定 + 目录大小写不敏感扫描**(`_resolve_texture`);blend 靠**表面名关键字启发式**(`guess_blend`:hair→alpha-test、cape/skirt→alpha、effect/glow→add,其余 opaque) | warrior 5/5、assassin 17/17、shaman 1/1 表面命中贴图,但**多材质槽 → 具体贴图/混合的精确映射做不到**;换皮/多贴图变体、精确 alpha-test 阈值、MODULATE2X/ADDSIGNED 等 stage op 无法复现 | **需要给 libgr2 加材质 API**(新文件 `gr2_material.cpp`,走类型树取 `Materials[].Name` + `Maps[].Texture.FromFileName` + shader/blend hint)。这也正是 xrender-poc M1 T5「多 stage 材质对拍待 oracle」的同一缺口,两条 route 共用修法。 |
|
||
| **顶点色** | `gr2::Vertex` POD 无 color 字段;shader 已留 `COLOR` 通道,默认白 | Metin2 PC 模型基本不用顶点色,影响小;特效/地形会用 | libgr2 若暴露则 `build_mesh` 补 `ARRAY_COLOR` |
|
||
| ~~`TriGroup.material_index` 未接线~~ **已做(2026-08-29)** | `gr2_bridge::build_parts()` 把一个 gr2 mesh 按 `tri_groups` 拆成 N 个 `RenderPart` → N 个 Godot surface,贴图取 `Mesh::material_textures[matidx]`(mesh-local,非全局 `Materials[]`)。GPU 和 CPU 蒙皮路径都走 `RenderPart`。 | — |
|
||
| **ShaderMaterial 静态 `Shader` 退出泄漏** | `shader_for()` 用 function-static `Ref<Shader>`,Godot 退出时报 "1 shader never freed" | 仅退出时一条 warning,无功能影响 | 改成每材质自持 Shader,或注册 cleanup |
|
||
| **alpha 排序 corner case** | 半透明/additive 用 `render_priority` 粗排,未做逐三角/OIT | 多层半透明部件可能穿插 | 记录,不追求 100%(Phase 2 M2.5 打磨) |
|
||
| **法线变换** | CPU 与 GPU(`SRC_SKIN` 着色器)两条路径都用混合矩阵的**上 3×3、不逆转置** —— 与 Granny runtime 一致(见 `docs/reference/steps/M2-anim-skinning.md`)。非均匀缩放骨骼下这是 Granny 自己的近似,我们与之一致。 | 已一致 | — |
|