Files
mtgodot-poc/docs/GODOT-POC-PLAN.md
T
Claude eeffa2a730 M2.5: ShaderMaterial + texture resolution + fog; multi-skeleton verified
- m2_material: Metin2-style ShaderMaterial. blend_mix shader (opaque /
  alpha-blend / alpha-test-cutout via `mode` uniform) + blend_add shader
  (additive glow). modulate(tex, COORD) * COLOR, diffuse_lambert.
- Metin2Model: material_mode prop ("metin2" ShaderMaterial | "standard");
  _resolve_texture does filename-convention + case-insensitive dir scan of
  *.dds (face/hair keyword slots, stem match, fallback); set_surface_texture
  override; guess_blend heuristic (hair->cutout, cape->alpha, effect->add).
  Texture cache. Fixed opaque path to NOT alpha-discard (was hiding bodies).
- harness: AABB-based camera framing, engine depth fog (MTGODOT_FOG=1),
  MTGODOT_ANIM_T fixed-pose capture (needs explicit anim reload first),
  MTGODOT_AUTOSHOT batch screenshot.

Verified: warrior 5/5 surfaces textured under the shader (t=9 dance pose,
lit, fogged). assassin 17/17 textured via dir scan. shaman_lord (gr2 v7)
renders. Screenshots test/golden/m25-*.png, m2c-t*.png.

Gap (M2.5 gate not closed): libgr2 POD API exposes no material/texture-name/
blend data, so multi-slot -> texture/blend mapping is heuristic only. Needs
a libgr2 material API (gr2_material.cpp walking the type tree) — same gap as
xrender-poc M1 T5. Documented in docs/GODOT-POC-PLAN.md §M2.5 T2.5.6.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaHYEY9rwLWt21PULiYjeJ
2026-08-29 09:35:01 +09:00

311 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Godot 渲染 Demo — 开发计划
> 内部研究方案,不对外公开。
> 与 [`PLAN.md`](./PLAN.md) 是**两条并行的跨平台基座候选**:本文件评估「Godot 4 + 自研资源 loader」,`PLAN.md` 评估「自研引擎 + bgfx RHI」。
> 复用 [`../libgr2`](../libgr2)、[`../formats`](../formats) 与 [`../oracle`](../oracle) 的真值数据。
| | |
|---|---|
| 状态 | PoC 规划 |
| 技术栈 | Godot **4.7.1** stable · GDExtension`godot-cpp` `4.7` 分支)· C++20 |
| 目标平台 | **Phase 1macOS**(当前)→ Phase 2iOS · Android |
| 判定周期 | Phase 1 ≈ 3.5–5 周(单人)→ **中期评审**(不下结论);Phase 2 后 → **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** | iOS · Android | M3(·M4 可选) | ④ 跨平台三端一致;⑤ 移动端性能 / 生命周期 | **go / no-go 结论** |
**关键澄清(写给评审)**Godot 在 macOS 上渲染一个蒙皮 gr2 **几乎不可能失败**。Phase 1 不是用来判断"该不该押 Godot"的——那个只有 Phase 2 能答。Phase 1 的价值是把**平台无关**的 GDExtension 工程(占本方案工程量的大头)做完,并确认视觉保真度可达,从而让 Phase 2 只剩"移动端 + 性能"这一个真未知。
**进入 Phase 2 的前置**:M2.5 门禁通过,且中期评审判定"桥接层工作量与观感均可接受"。否则在此转回 bgfx route,损失仅为 GDExtension 桥接层(≈ glTF importer 量级),`libgr2` 完整保留。
---
## 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 工程在 iOS 真机 + Android 真机(Vulkan + Compatibility)渲染与桌面一致。
5. **性能 / 生命周期达标** — 中端机单角色 / 多角色帧率达标,后台 / 恢复稳,Godot 节点开销可控。
### 明确排除(两个阶段都不做)
SpeedTree 植被、地形(Metin2 户外地图)、UI / Python 脚本层、eterpackPhase 1 用散文件)、GPU 蒙皮优化、LOD 策略、多角色 AI、网络、玩法。
> 地形与 `.mse` 特效在 Phase 1 也不做,但 **M2.5 会做通用 `ShaderMaterial` + 混合模式的地基**,为后续正式移植时的地形 / 特效渲染铺路。
### 判定口径
- **M2.5 门禁通过 + 中期评审"可接受"** = 进入 Phase 2。
- **门禁失败区分两类**(同 `PLAN.md` §01 口径):
- **工程性延期** — 材质差色、某曲线子类型没实现、某 API 用错。有明确修法,记下继续。
- **方案性死路** — 例如 Godot 节点 / 骨骼模型无法表达 Metin2 的多部件挂点体系、或多 stage 固定管线在 `ShaderMaterial` 里无法近似到可接受、或每帧写骨骼在合理规模下 Mac 上就已经卡。这才触发"转回 bgfx route"。
- **最终交付(Phase 2 末)** 一页纸对比结论:与 bgfx route 在 **工作量 / 性能 / 视觉保真度 / 长期维护成本** 四轴上的取舍。
### 与 bgfx route 的关系
| | 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 · 架构总览
```
mtgodot-poc/
extension/ GDExtensionC++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;跨文件 retargetmotion 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 描述 → ShaderMaterialM2.5
libgr2/ submodule → ../xrender-poc/libgr2
formats/ submodule → ../xrender-poc/formatsmsa/msm
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 macOSPhase 1
test/
golden/ godot-macos-*.png
compare.py 与 ../xrender-poc bgfx demo / 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` **`4.7` 分支**GDExtension(非模块编译) | 用官方导出模板,不自定义 Godot 构建 |
| 构建工具 | `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+macOS | Phase 1 不涉及 Compatibility |
| 阴影 / sky | Godot 内置 `DirectionalLight3D` shadow + `WorldEnvironment` procedural skyM2.5 接入 | 成本近零,Mac 观感必要 |
| 资产来源 | 从 `m2dev-client-main/assets`**warrior + 另 2 种骨架** 的 gr2/dds 到 `project/assets/`,散文件 | 不接 eterpackPhase 2 / M4 |
| 真值源 | `xrender-poc/oracle` golden + bgfx demo 同机位 / pose / t 截图 | 复用现成对拍基建 |
| 仓库 | 新建 `mtgodot-poc/``libgr2` / `formats` submodule 指向 `xrender-poc` | 隔离,不污染 bgfx route |
---
## 04 · 里程碑
### M0' · 脚手架(macOS12 天)
**目标**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 · 静态渲染(11.5 周)
**目标**warrior + 另 2 种骨架的 bind pose + 贴图在 Godot 里正确显示,多部件组装。
对标 [`steps/M1-static-render.md`](./steps/M1-static-render.md) T2T5。
| 任务 | 完成判据 |
|---|---|
| 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` |
| 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.52 周)
**目标**:从 GDExtension 每帧驱动 `Skeleton3D` 播动画,含跨文件 retarget 与 motion event。
对标 [`steps/M2-anim-skinning.md`](./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 · 材质与观感保真(11.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,本轮不做)iOS + Android + 性能
移动端真机、三端一致、性能、生命周期、Compatibility 渲染器、每帧写骨骼的 A/B 决策 —— **go / no-go 结论在此产生**。详见后续 `steps/M3-mobile.md`(届时再写)。
### M4 ·(Phase 2 可选拉伸)
接真实 eterpack、试水一个 `.mse` 特效、多 texture-stage 精确对拍。
---
## 05 · 工作量(Phase 1macOS
| 里程碑 | 单人 | 2 人并行 |
|---|---|---|
| M0' | 12 天 | 1 天 |
| M1 | 11.5 周 | 1 周(网格 / 骨架分工) |
| M2 | 1.52 周 | 11.5 周(动画 / 对拍分工) |
| M2.5 | 11.5 周 | 1 周(shader / 材质映射分工) |
| **Phase 1 合计** | **≈ 3.55 周** | **≈ 2.53.5 周** |
前置依赖:`xrender-poc``libgr2`(已完成)、`oracle` goldenM2 对拍要)、bgfx demoM1/M2/M2.5 截图对拍要,已可跑)、Godot 4.7.1 export templates(需在编辑器内下载)。
---
## 06 · 风险登记册
### Phase 1macOS
| 风险 | 触发信号 | 缓解 / 退路 |
|---|---|---|
| `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"死路",后者触发转回 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(移动端,本轮不激活,仅登记)
| 风险 | 缓解 / 退路 |
|---|---|
| 每帧 GDExtension 写骨骼的 CPU 成本(多角色) | M2 T2.7 已留档;转 A 方案(烘 `Animation` + `AnimationMixer` |
| Godot Compatibility(GLES3) 老安卓 shader 兼容 | M2.5 的 `ShaderMaterial` 写保守,避开 Compatibility 不支持特性 |
| Godot 节点开销(大场景规模) | `MultiMesh` / `RenderingServer` 直调兜底 |
| GDExtension ABI 绑 Godot minor | 锁 4.7.1,升级作独立任务 |
| iOS 交叉编译 `libgr2` 静态库 | 提前出 iOS `.a` 预编译产物 |
---
## 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` |
| 材质差距清单 | 中期评审报告内 |
| 50 角色压力采样(供 Phase 2 A/B 决策) | `test/godot-macos-stress.json` |
| **中期评审报告** | 桥接层工作量实测 + 观感对比截图 + 剩余风险 + "是否进入 Phase 2"建议 |
### Phase 2(后续)
| 产物 |
|---|
| iOS `.app` + Android APK/AAB + 真机性能 / 生命周期报告 |
| **go/no-go 对比结论(一页纸)**:与 bgfx route 在工作量 / 性能 / 视觉保真度 / 长期维护四轴对比,给出客户端跨平台基座选型建议 |
---
## 选型变更记录
- 2026-08-29 · 范围收敛为 **Phase 1 = macOS only**;新增 **M2.5 材质与观感保真** 作为 Phase 1 核心门禁;动画 A/B 决策推迟到 Phase 2Godot 版本钉死 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 + BGRA8level 0 + `generate_mipmaps()`
- 2026-08-29 · **动画方案**:确认走 **B 方案** —— `_process``gr2::sample_pose``Skeleton3D.set_bone_global_pose(i, gr2_to_godot(world[i]))`Godot GPU 蒙皮 `skin_matrix = global_pose(i) * bind_pose(i)` 恒等于 gr2 deformer 矩阵(`gr2_bridge.h` 有推导)。A 方案(烘 `Animation`)仍推迟到 Phase 2 按多角色 CPU 采样决定。
- 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 还原 / 待办项清单
| 项 | 现状 | 影响 | 出路 |
|---|---|---|---|
| **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` 未接线** | libgr2 已解析每个三角组的 material_index,但 `build_mesh` 目前一个 gr2 mesh = 一个 surface(未按 material_index 再拆分) | 单 mesh 内多材质的模型会用同一张贴图 | `build_mesh``tri_groups` 的 material_index 分 surface(有材质 API 后一起做) |
| **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 打磨) |
| **法线未做逆转置** | 走 Godot GPU 蒙皮,其法线处理与 Granny runtime 的「上 3×3 不逆转置」近似不完全一致 | 非均匀缩放骨骼下法线略有差异;warrior 类资产无明显问题 | Phase 2 逐帧对拍时量化 |