- 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
22 KiB
Godot 渲染 Demo — 开发计划
内部研究方案,不对外公开。 与
PLAN.md是两条并行的跨平台基座候选:本文件评估「Godot 4 + 自研资源 loader」,PLAN.md评估「自研引擎 + bgfx RHI」。 复用../libgr2、../formats与../oracle的真值数据。
| 状态 | PoC 规划 |
| 技术栈 | Godot 4.7.1 stable · GDExtension(godot-cpp 4.7 分支)· C++20 |
| 目标平台 | Phase 1:macOS(当前)→ Phase 2:iOS · 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)
- gr2 → Godot 场景对象 —
libgr2读出的骨架 / 蒙皮网格 / 权重能否正确映射到Skeleton3D+ArrayMesh+Skin,覆盖多种骨架(不止 warrior)。 - 动画正确 — 每帧从
libgr2::sample_pose驱动Skeleton3D,结果与 bgfx demo / Granny oracle 逐帧一致;跨文件 retarget、motion event 成立。 - 观感可还原 — 多 texture-stage 材质、alpha test、顶点色、additive 混合、fog、方向光阴影在 Godot 里做出来,与原客户端目视接近。
Phase 2 要证明的两件事(移动端,本轮不做)
- 跨平台一致 — 同一 GDExtension + Godot 工程在 iOS 真机 + Android 真机(Vulkan + Compatibility)渲染与桌面一致。
- 性能 / 生命周期达标 — 中端机单角色 / 多角色帧率达标,后台 / 恢复稳,Godot 节点开销可控。
明确排除(两个阶段都不做)
SpeedTree 植被、地形(Metin2 户外地图)、UI / Python 脚本层、eterpack(Phase 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/ 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/ submodule → ../xrender-poc/libgr2
formats/ submodule → ../xrender-poc/formats(msa/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 macOS(Phase 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 sky,M2.5 接入 |
成本近零,Mac 观感必要 |
| 资产来源 | 从 m2dev-client-main/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 里正确显示,多部件组装。
对标 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 |
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。
| 任务 | 完成判据 |
|---|---|
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,本轮不做)iOS + Android + 性能
移动端真机、三端一致、性能、生命周期、Compatibility 渲染器、每帧写骨骼的 A/B 决策 —— go / no-go 结论在此产生。详见后续 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"死路",后者触发转回 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-pocbgfx 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 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 方案 ——
_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 转置 → GodotTransform3D;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 逐帧对拍时量化 |