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
346 lines
21 KiB
Markdown
346 lines
21 KiB
Markdown
# Demo 开发方案 —— bgfx 渲染 assets 下的 gr2 资源
|
||
|
||
> 本文件把 [`PLAN.md`](./PLAN.md) 的 M0 + M1(+ M2 拉伸目标)落成一个**可演示的 demo** 的文件级施工图。
|
||
> demo 只做 macOS(Metal),跑通后 iOS/Android 是同一套代码换 toolchain。
|
||
> 前置事实、坐标/格式细节、风险都以 `PLAN.md` 为准,这里不重复论证。
|
||
|
||
---
|
||
|
||
## 1 · Demo 目标与演示脚本
|
||
|
||
**一句话**:一个原生 macOS 窗口,直接从 `m2dev-client-main/assets/` 读一个 `.gr2` + 它的 `.dds`,用 bgfx/Metal 把这个带蒙皮的角色渲染出来,轨道相机可转,能切线框 / 法线可视化;拉伸目标是让它播一个 `.msa` 动作。
|
||
|
||
**演示脚本(给人看的顺序)**:
|
||
|
||
| 步骤 | 屏幕上 | 证明了 |
|
||
|---|---|---|
|
||
| 1 | 终端跑 `gr2dump warrior_cheongrin.gr2`,打印骨架树(骨骼名 + 父索引 + 层级缩进)、网格摘要(顶点数 / 索引数 / 三角组)、动画列表 | libgr2 能脱离 Granny 读结构 |
|
||
| 2 | 窗口出现,warrior 网格以**绑定姿势**显示,白模,轨道相机可拖转、滚轮缩放 | gr2 顶点 / 索引 / 骨架 → bgfx,交接成立 |
|
||
| 3 | 按 `T` 贴上 `warrior_cheongrin.dds`(DXT3 软解),材质正确 | DXT 解码 + texture-stage + UV 正确 |
|
||
| 4 | 按 `N` 切法线可视化、按 `W` 切线框 | 自检工具,抓法线翻转 / 拓扑错 |
|
||
| 5(拉伸)| 按 `Space` 播 `action/dance_1.gr2` + `dance_1.msa`,角色跳舞,CPU 蒙皮 | 曲线采样 + LBS 成立,M2 的核心 |
|
||
| 6 | 角标 HUD 显示 FPS / draw call / 三角数(bgfx 自带 `showStats`) | 有性能可读数 |
|
||
|
||
**不做**:地形、特效、UI、多部件装配、eterpack(demo 用散装文件)、iOS/Android(同代码后续换 toolchain)。
|
||
|
||
---
|
||
|
||
## 2 · 演示用资产(具体文件,散装,无需解包)
|
||
|
||
| 用途 | 路径(相对 `m2dev-client-main/`) | 事实 |
|
||
|---|---|---|
|
||
| 主模型 | `assets/PC/ymir work/pc/warrior/warrior_cheongrin.gr2` | 92 770 B,gr2 格式 v6,8 section,**section 全 Oodle1 压缩**(实测)|
|
||
| 主贴图 | `assets/PC/ymir work/pc/warrior/warrior_cheongrin.dds` | **512×512 DXT3,5 mip** |
|
||
| LOD 对照 | `warrior_cheongrin_lod_01/02/03.gr2` | 同骨架、减面,用于 M2 的 LOD 一致性检查 |
|
||
| 动作(拉伸)| `assets/PC/ymir work/pc/warrior/action/dance_1.gr2` + `dance_1.msa` | 动画单独一个 gr2 + 文本 msa |
|
||
| 刚体静态件(对照)| `assets/Zone/ymir work/zone/oxevent/ox_01.gr2` | 无骨骼,验证 rigid 分支 |
|
||
|
||
> demo 起步**不碰 `.msm`**(`warrior_m.msm` 引用 `warrior_novice.GR2` + 61 组头发 + 一堆动作,太复杂)。直接 `warrior_cheongrin.gr2` + 同名 `.dds` 是自足的 mesh+texture 对。`.msm` 解析留到 M2 多部件装配。
|
||
|
||
**资产接入方式**:CMake 里配一个 `XRENDER_ASSET_ROOT` 指向 `../m2dev-client-main/assets`,demo 直接 `fopen` / mmap 读,不复制。
|
||
|
||
---
|
||
|
||
## 3 · 依赖与仓库骨架
|
||
|
||
```
|
||
xrender-poc/
|
||
third_party/
|
||
bgfx.cmake/ submodule → github.com/bkaradzic/bgfx.cmake(拉 bx/bimg/bgfx)
|
||
⚠ pin 到与 shaders.rar 的 .sc 草稿相近的 bgfx 版本
|
||
sokol/ 只放 sokol_app.h(单文件,手动 vendor)
|
||
glm/ submodule 或单目录 vendor
|
||
cgltf/ 单头文件(gr2dump 导出 glTF 用)
|
||
reuse/
|
||
EterImageLib/ 从 ../MobileSource 冻结拷入:DXTCImage.{h,cpp} + StdAfx + 依赖的最小集
|
||
EterBase/ (拉伸)CFileBase / CMappedFile —— demo 初期可先用裸 fopen
|
||
libgr2/
|
||
include/gr2.h
|
||
src/gr2_file.cpp header + section table + fixup 重定位
|
||
src/gr2_typetree.cpp 自描述 data_type_definition 遍历器
|
||
src/gr2_fileinfo.cpp FileInfo 根对象 → skeleton/mesh/material/animation 视图
|
||
src/gr2_skeleton.cpp 骨骼数组 + bind pose 自洽检查
|
||
src/gr2_mesh.cpp 顶点/索引/三角组/BoneBindings 提取
|
||
src/gr2_anim.cpp TrackGroup → 每骨曲线;曲线子类型解码(demo 子集)
|
||
src/gr2_decompress.cpp section 解压分派
|
||
src/oodle1.c Granny Oodle1 解码(已实现,端口自泄露 SDK;9166/9166 验证通过)
|
||
engine/
|
||
rhi.h/.cpp ≈150 行 bgfx 薄封装
|
||
camera.h/.cpp 轨道相机
|
||
skinning.h/.cpp CPU LBS(拉伸)
|
||
animation.h/.cpp 曲线采样 + 世界姿势累积(拉伸)
|
||
scene.h/.cpp 把 libgr2 视图 → GPU buffer + draw item
|
||
formats/
|
||
msa.cpp (拉伸)文本 msa 解析
|
||
app/
|
||
main.cpp sokol_app 回调 + bgfx init + 输入 + demo 状态机
|
||
shaders/
|
||
varying.def.sc 从 shaders.rar 拷 + 加 a_indices/a_weight
|
||
vs_pnt.sc fs_pnt.sc 从 shaders.rar 拷改
|
||
vs_pnt_skinned.sc 新写(骨骼矩阵 uniform 数组)
|
||
texture_stage.sh 从 shaders.rar 原样拷
|
||
compile.cmake shaderc → metal + bin2c
|
||
tools/
|
||
gr2dump/main.cpp CLI:结构化 dump + glTF 导出
|
||
oracle/ Windows-only,见 PLAN §07(demo 阶段可先跳,用 Blender io_scene_gr2 当参照)
|
||
cmake/
|
||
macos.cmake
|
||
CMakeLists.txt
|
||
```
|
||
|
||
---
|
||
|
||
## 4 · 构建(macOS 优先)
|
||
|
||
```bash
|
||
# 1. 拉依赖
|
||
git submodule add https://github.com/bkaradzic/bgfx.cmake third_party/bgfx.cmake
|
||
cd third_party/bgfx.cmake && git submodule update --init && cd -
|
||
# 固定 bgfx 版本(示例):cd third_party/bgfx.cmake/bgfx && git checkout <pinned-tag>
|
||
|
||
# 2. 配置 + 编
|
||
cmake -B build -DXRENDER_ASSET_ROOT=../m2dev-client-main/assets -DCMAKE_BUILD_TYPE=Debug
|
||
cmake --build build -j
|
||
|
||
# 3. 跑
|
||
./build/tools/gr2dump/gr2dump "$XRENDER_ASSET_ROOT/PC/ymir work/pc/warrior/warrior_cheongrin.gr2"
|
||
./build/app/xrender-demo
|
||
```
|
||
|
||
**`CMakeLists.txt` 要点**:
|
||
|
||
- `add_subdirectory(third_party/bgfx.cmake)` → 得到 `bgfx` / `bx` / `bimg` / `shaderc` 目标。
|
||
- 着色器编译:`app/shaders/compile.cmake` 里对每个 `.sc` 跑 `shaderc -p metal --platform osx`,产物过 `bin2c` 生成 `*.sc.bin.h`,作为 `xrender-demo` 的生成依赖。
|
||
- `reuse/EterImageLib` 编成静态库 `xr_eterimage`;`-Wno-*` 压掉老代码告警;可能要 `-DXR_STANDALONE` 剥掉 `StdAfx.h` 里的 Windows include。
|
||
- macOS:`-framework Cocoa -framework Metal -framework QuartzCore`,`main.cpp` 编成 `.mm`(sokol_app 的 macOS 后端要 ObjC)。
|
||
|
||
---
|
||
|
||
## 5 · 模块与文件清单(职责 + 估行)
|
||
|
||
### libgr2(demo 子集,只读)
|
||
|
||
| 文件 | 职责 | 估行 | demo 边界 |
|
||
|---|---|---|---|
|
||
| `gr2_file.cpp` | magic(`GRNFileMV_Old`)+ `grn_file_header` + section 表(**已实现**,9166/9166 跑通)+ fixup 重定位 | 250 | v6 / 32-bit LE |
|
||
| `gr2_decompress.cpp` + `oodle1.c` | section 解压分派 + Granny Oodle1 解码器 | 40 + 450 | **已实现**:从泄露 SDK 端口解码路径,9166/9166 展开到精确 `ExpandedDataSize`,内嵌串验证 |
|
||
| `gr2_typetree.cpp` | 按 `granny_data_type_definition`(文件内嵌,自描述)递归走类型树,把裸内存映射成可访问的字段 | 300 | 通用遍历器,不硬编码结构;只需支持 gr2 实际用到的成员类型(Real32 / Int32 / Ref / ReferenceToArray / Inline / String) |
|
||
| `gr2_fileinfo.cpp` | 定位 `FileInfo` 根对象,暴露 `Skeletons[] / VertexDatas[] / TriTopologies[] / Meshes[] / Materials[] / Textures[] / Models[] / Animations[]` 的 span 视图 | 150 | — |
|
||
| `gr2_skeleton.cpp` | 骨骼数组(`Name / ParentIndex / LocalTransform(SRT) / InverseWorld4x4`);**bind pose 自洽检查**:重建 `world_bind`,验 `world_bind[i]·InverseWorld4x4[i]≈I` | 180 | — |
|
||
| `gr2_mesh.cpp` | 顶点(识别 `PNT332` / `PNT3322` / 带 `BoneWeights+BoneIndices` 的蒙皮变体)、索引、`TriGroups`(按材质分段)、`BoneBindings`(mesh→skeleton 骨骼名映射) | 220 | 只支持上述三种顶点布局 |
|
||
| `gr2_anim.cpp` | `Animation → TrackGroups → TransformTracks`;每骨 position/orientation/scaleshear 曲线;曲线解码 + `Animation::sample_local(t)` | 320 | **已实现(全覆盖)**:M0 实测 Metin2 曲线**全部**是 `OldCurveType`(Granny 2.4:`{Degree; Knots[]; Controls[]}`,无压缩变体)。degree 0(常量)/ 1(线性)/ 2(二次 B 样条)+ 四元数归一。**degree 3 全样本 0 个**,无需烘焙退路。精度待 oracle 层① 对拍 |
|
||
|
||
`include/gr2.h` 对外暴露纯 POD 视图(`gr2::Skeleton` / `gr2::Mesh` / `gr2::Animation`),不泄露内部指针 —— 上层只依赖这个头。
|
||
|
||
### reuse/EterImageLib(冻结拷入)
|
||
|
||
- `DXTCImage.{h,cpp}`:`LoadHeaderFromMemory` + `LoadFromMemory` + `Decompress(level, DWORD* out)` → RGBA8。demo 用 DXT3 分支。
|
||
- 依赖裁剪:把 `StdAfx.h` 换成一个最小 shim(`typedef uint8_t BYTE` 等),去掉 `windows.h`。
|
||
- 备选:DXT3 解码就 ~150 行,嫌 EterImageLib 依赖脏可以自己写一个 `dxt.cpp`。
|
||
|
||
### engine/rhi.{h,cpp}(≈150 行)
|
||
|
||
对齐 `StateManager` 语义的 bgfx 薄封装:
|
||
|
||
```cpp
|
||
namespace rhi {
|
||
void init(void* nativeWindowHandle, int w, int h); // bgfx::PlatformData + bgfx::init
|
||
void resize(int w, int h);
|
||
void beginFrame(const glm::mat4& view, const glm::mat4& proj);
|
||
Handle createVB(const void* data, uint32_t size, const bgfx::VertexLayout&);
|
||
Handle createIB(const void* data, uint32_t size, bool i32);
|
||
Handle createTex2D(const void* rgba, uint16_t w, uint16_t h, uint16_t mips);
|
||
Handle createProgram(const uint8_t* vs, uint32_t vsLen, const uint8_t* fs, uint32_t fsLen);
|
||
void setModel(const glm::mat4&);
|
||
void setBones(const glm::mat4* mtx, uint16_t n); // uniform 数组,vs_pnt_skinned 用
|
||
void setTexture(Handle);
|
||
void setStageUniforms(const StageDesc&); // texture_stage.sh 的 uniform
|
||
void submit(Handle vb, Handle ib, Handle prog, uint64_t state);
|
||
void endFrame();
|
||
}
|
||
```
|
||
|
||
### app/main.cpp
|
||
|
||
- `sokol_app` 描述:`.window_title`、`.high_dpi=true`、macOS 后端;**不让 sokol_app 建 GL/Metal 设备** —— 用 `sapp_metal_get_layer()` / `sapp_macos_get_window()` 取原生 handle 传给 `rhi::init`(这就是 PLAN §03 的交接点,D2 的验收)。
|
||
- `frame_cb`:更新相机 → `rhi::beginFrame` → 遍历 scene draw items → `rhi::submit` → `bgfx::frame()`。
|
||
- `event_cb`:鼠标拖 = 轨道、滚轮 = 缩放、键 `T/N/W/Space`。
|
||
- demo 状态机:`enum { BindPose, Textured, NormalViz, Wireframe, Animating }`。
|
||
|
||
### app/shaders
|
||
|
||
| 文件 | 来源 | 改动 |
|
||
|---|---|---|
|
||
| `varying.def.sc` | shaders.rar 拷 | 加 `int4 a_indices : BLENDINDICES;` `vec4 a_weight : BLENDWEIGHT;` |
|
||
| `vs_pnt.sc` / `fs_pnt.sc` | shaders.rar 拷 | `vs_pnt` 补 `a_color0`;`fs_pnt` 加 fog(demo 可先关);确认 `#include <bgfx_shader.sh>` 对上 pin 的 bgfx 版本 |
|
||
| `vs_pnt_skinned.sc` | 新写 | `mat4 skin = u_bones[a_indices.x]*a_weight.x + …`(4 权重);其余同 `vs_pnt` |
|
||
| `texture_stage.sh` | shaders.rar 原样 | 不改;demo 里给它喂"单 stage MODULATE(TEXTURE, DIFFUSE)"的 uniform,等价于 `tex * vertexColor` |
|
||
| `fs_normalviz.sc` | 新写(10 行) | `gl_FragColor = vec4(v_normal*0.5+0.5, 1)` |
|
||
|
||
### tools/gr2dump/main.cpp
|
||
|
||
- 参数:`gr2dump <file.gr2> [--gltf out.glb]`。
|
||
- stdout:骨架树(缩进)、每 mesh 的顶点/索引/三角组/顶点布局、每 animation 的时长 + track 数 + 曲线子类型直方图、bind pose 自洽检查结果(PASS/FAIL + max 偏差)。
|
||
- `--gltf`:用 cgltf 写骨架 + 第一个 mesh 的 bind pose + (若实现了)第一个 animation。**仅供 Blender 目视**,动画正确性不认它(PLAN §06 M0)。
|
||
|
||
---
|
||
|
||
## 6 · 实现顺序(每步一个可演示切片)
|
||
|
||
| 步 | 交付 | 演示点 | 验收 |
|
||
|---|---|---|---|
|
||
| **D0** | CMake 骨架编过:空 `xrender-demo` 开一个 bgfx 清屏窗口(纯色)+ `bgfx::showStats` | 窗口出现、FPS 角标在跳 | bgfx 经 sokol_app handle 起来了(**PLAN §03 交接子门禁的最小版**) |
|
||
| **D1** | `gr2_file` + `gr2_typetree` + `gr2_fileinfo` + `gr2_skeleton`;`gr2dump` 能打印 `warrior_cheongrin.gr2` 骨架树 + mesh 摘要 | 终端 dump 输出 | 骨骼数 / 名称 / 父索引与 Blender `io_scene_gr2` 导入一致;bind pose 自洽 PASS |
|
||
| **D2** | `gr2_mesh` + `engine/scene` + `rhi` + `vs_pnt/fs_pnt`:warrior **白模绑定姿势**上屏,轨道相机 | 能转的白模 | 轮廓 = Blender 里同模型;无背面/法线翻转(配合 D4 的法线可视化确认) |
|
||
| **D3** | `reuse/EterImageLib` DXT3 解码 → `rhi::createTex2D` → `texture_stage.sh`;按 `T` 贴图 | 有材质的 warrior | UV 无错位、无镜像;和客户端截图目视一致 |
|
||
| **D4** | `fs_normalviz` + 线框 state(`BGFX_STATE_PT_LINES` 或 `BGFX_DEBUG_WIREFRAME`);`N` / `W` 切换 | 法线彩图 / 线框 | 法线朝外;三角组分段正确 |
|
||
| **D5(拉伸)** | `gr2_anim`(子集)+ `formats/msa` + `animation` + `skinning`(CPU LBS)+ `vs_pnt_skinned`;`Space` 播 `dance_1` | 角色跳舞 | 与 Blender 导入的同一动画逐帧目视一致;无肢体飞出/顶点塌陷。数值对拍要 oracle(见 §8) |
|
||
|
||
**D0–D4 = 可交付的 demo**(静态带贴图 + 自检工具)。D5 是加分项,卡住不影响 demo 成立。
|
||
|
||
---
|
||
|
||
## 7 · 关键实现细节
|
||
|
||
### 7.1 gr2 v6 文件结构(`gr2_file.cpp`)
|
||
|
||
```
|
||
0x00 BYTE magic[16] // B8 67 B0 CA F8 6D B1 0F 84 72 8C 7E 5E 19 00 1E ← 认版本/字节序
|
||
0x10 u32 headerSize // 0x1B8
|
||
0x14 u32 headerFormat // 0(这不是 section 压缩!section 压缩看每段 grn_section.Format)
|
||
0x18 u32 reserved[2]
|
||
--- GrannyFileHeader ---
|
||
0x20 u32 version // 6
|
||
0x24 u32 totalSize // == 文件大小,用来自检解析对齐
|
||
0x28 u32 crc32
|
||
0x2C u32 sectionArrayOffset // 0x38
|
||
0x30 u32 sectionArrayCount
|
||
0x34 u32 rootObjectTypeSection / rootObjectTypeOffset / rootObjectSection / rootObjectOffset
|
||
...
|
||
--- Section[sectionArrayCount] --- 每项 ~44 B:
|
||
u32 Format // 0=none 1=Oodle0 2=Oodle1(Metin2 实测全 2)
|
||
u32 dataOffset, dataSize
|
||
u32 expandedDataSize
|
||
u32 alignment
|
||
u32 first16Bit / first8Bit // marshalling 边界
|
||
u32 pointerFixupArrayOffset, pointerFixupArrayCount
|
||
u32 mixedMarshallingFixupArrayOffset, mixedMarshallingFixupArrayCount
|
||
```
|
||
|
||
**流程**:读 header → 逐 section:`gr2_decompress` 展开到 `expandedDataSize` 的缓冲 → 应用 pointer fixup(把文件内偏移改写成进程内指针)→ (小端机上 mixed-marshalling fixup 可跳过,big-endian 才需要)→ 得到一组可随机访问的 section 内存块。root object 在 `(rootObjectSection, rootObjectOffset)`,其类型定义在 `(rootObjectTypeSection, rootObjectTypeOffset)`。
|
||
|
||
### 7.2 自描述类型树(`gr2_typetree.cpp`)
|
||
|
||
`granny_data_type_definition` 是数组,每项:`{ MemberType(u32), Name(char*), ReferenceType(def*), ArrayWidth(i32), Extra[3], Ignored }`,以 `MemberType==0`(End)结尾。`MemberType` 枚举含 `Inline / Reference / ReferenceToArray / ArrayOfReferences / Real32 / Int32 / UInt32 / String / Transform / …`。
|
||
|
||
写一个 `walk(void* obj, const TypeDef* type, Visitor&)`:按成员类型算 stride、递归 `Reference`/`ReferenceToArray`。**不要硬编码 struct 布局** —— 不同 Granny 小版本字段顺序会变。上层 `gr2_fileinfo` 按**成员名**(`"Skeletons"`, `"Meshes"` …)取字段,用 `GrannyFindMatchingMember` 式的按名查找。
|
||
|
||
### 7.3 顶点布局 → `bgfx::VertexLayout`(`gr2_mesh.cpp` + `scene.cpp`)
|
||
|
||
gr2 mesh 的顶点类型在文件里(`GrannyGetMeshVertexType`)。demo 支持:
|
||
|
||
| gr2 顶点类型 | 成员 | bgfx layout |
|
||
|---|---|---|
|
||
| `PNT332` | Pos3f, Norm3f, UV2f | Position/Normal/TexCoord0 |
|
||
| `PNT3322` | Pos3f, Norm3f, UV2f, UV2f | + TexCoord1 |
|
||
| 蒙皮变体 | + BoneWeights(4×u8 归一) + BoneIndices(4×u8) | + Weight/Indices(`bgfx::Attrib::Weight` + `Indices`,`AttribType::Uint8`, normalized=weight true / indices false)|
|
||
|
||
`GrannyCopyMeshVertices(mesh, dstType, dstBuf)` 的效果自己实现:按源类型逐顶点拷到一个 demo 统一的打包结构,再 `bgfx::createVertexBuffer`。索引:gr2 是 u16 或 u32,`GrannyCopyMeshIndices` 同理。三角组 `TriGroups` 给 `(materialIndex, triFirst, triCount)`,每组一次 `rhi::submit`。
|
||
|
||
### 7.4 DXT3 → bgfx 纹理(`scene.cpp`)
|
||
|
||
demo 走**运行时软解**(PLAN §08:移动端无 S3TC,且要和 oracle 对齐时贴图要能预解码):
|
||
|
||
```
|
||
CDXTCImage img;
|
||
img.LoadHeaderFromMemory(ddsBytes); // 认 512x512 DXT3 5mip
|
||
img.LoadFromMemory(ddsBytes);
|
||
std::vector<uint32_t> rgba(w*h);
|
||
img.Decompress(0, rgba.data()); // level 0;mip 链 demo 可先不传,让 bgfx 不采样 mip
|
||
rhi::createTex2D(rgba.data(), 512, 512, 1);
|
||
```
|
||
|
||
(`bgfx::createTexture2D` + `BGFX_SAMPLER_MIN_POINT` 之类先关 mip,D3 通过后再补全 mip 链。)
|
||
|
||
### 7.5 坐标系(`scene.cpp` / `camera.cpp`)
|
||
|
||
PLAN §07:**约定写死**。demo 取"左手 Y-up、单位 = gr2 原始单位、根变换已 apply"。gr2 里骨架/网格是 Granny 约定(通常右手 Z-up 或文件指定的 art tool basis)。demo 先加一个固定 `basisFix`(可能是绕 X -90° + Z 翻转,M0 用朝向明确的资产标定),乘进 model 矩阵。**左右手系**:bgfx 用 `bx::mtxLookAt` / `bx::mtxProj` 的 `bx::Handedness` 参数统一,和 `basisFix` 一起调到"warrior 正着站、面朝 +Z"。
|
||
|
||
### 7.6 bgfx 提交循环(`main.cpp` / `rhi.cpp`)
|
||
|
||
```cpp
|
||
bgfx::setViewRect(0, 0,0, w,h);
|
||
bgfx::setViewClear(0, BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f);
|
||
bgfx::setViewTransform(0, &view, &proj);
|
||
// per draw item:
|
||
bgfx::setTransform(&model);
|
||
bgfx::setVertexBuffer(0, vb);
|
||
bgfx::setIndexBuffer(ib);
|
||
bgfx::setTexture(0, s_texColor, tex);
|
||
bgfx::setUniform(u_stageColor, &stage, 1);
|
||
if (skinned) bgfx::setUniform(u_bones, bones, boneCount);
|
||
bgfx::setState(BGFX_STATE_WRITE_RGB|BGFX_STATE_WRITE_A|BGFX_STATE_WRITE_Z
|
||
|BGFX_STATE_DEPTH_TEST_LESS|BGFX_STATE_CULL_CW
|
||
|BGFX_STATE_MSAA);
|
||
bgfx::submit(0, prog);
|
||
// end:
|
||
bgfx::frame();
|
||
```
|
||
|
||
深度范围 / Y 翻转:用 `bgfx::getCaps()->homogeneousDepth` 和 `originBottomLeft` 决定 `bx::mtxProj` 参数,别手写。
|
||
|
||
### 7.7 sokol_app ↔ bgfx 交接(`main.cpp`,D0 的验收)
|
||
|
||
```cpp
|
||
// sokol_app 里关掉它自己的渲染循环意图,只要窗口 + 事件
|
||
bgfx::PlatformData pd{};
|
||
pd.nwh = sapp_macos_get_window(); // NSWindow*
|
||
pd.ndt = nullptr;
|
||
// Metal: 也可以 pd.nwh = (__bridge void*)sapp_metal_get_layer(); // CAMetalLayer*
|
||
bgfx::Init init;
|
||
init.type = bgfx::RendererType::Metal;
|
||
init.platformData = pd;
|
||
init.resolution.width = sapp_width();
|
||
init.resolution.height = sapp_height();
|
||
bgfx::init(init);
|
||
```
|
||
|
||
若这条在 macOS 上出不了画面(sokol_app 和 bgfx 抢 layer)——按 PLAN §03 退路:`app/` 换成 SDL2 窗口,`SDL_GetWindowWMInfo` 取 `NSWindow*`,其余不变。**D0 就是来验证这个的**,别拖。
|
||
|
||
---
|
||
|
||
## 8 · demo 阶段相关的坑(从 PLAN §08 摘)
|
||
|
||
| 坑 | 在 demo 里的表现 | demo 阶段怎么办 |
|
||
|---|---|---|
|
||
| sokol_app + bgfx 抢 context | D0 黑屏 / 崩 | D0 卡死就切 SDL2,别硬啃 |
|
||
| `.sc` 草稿针对某 bgfx 版本 | shaderc 编不过 / uniform 名对不上 | submodule pin 到相近版本;编不过就照 `bgfx_shader.sh` 手改宏 |
|
||
| DXT 软解 vs 客户端 GPU S3TC | D3 贴图和客户端截图有细微色差 | demo 目视够了;要数值对拍时两侧都用软解 RGBA |
|
||
| 坐标系 basis | D2 warrior 躺着 / 镜像 / 巨大 | 用朝向明确的资产手调 `basisFix`,记进 `scene.cpp` 注释 |
|
||
| ~~曲线子类型超出实现~~ | — | **已消除**:M0 实测全 `OldCurveType`,degree ≤ 2,`gr2_anim.cpp` 全覆盖 |
|
||
| `InverseWorld4x4` 读错但一致 | D2 白模看着对,D5 蒙皮炸 | **已兜住**:M0 self-check 全量 9166 跑过,`warrior_cheongrin` 6.1e-5 PASS |
|
||
| root 偏移放法两种(`InitialPlacement` vs 烘进 root local) | 世界姿势整体偏移 ~100 单位 | **已处理**:`world[root] = Composite(local[root]) · model.InitialPlacement`(M0 gr2_fileinfo 已关联 model→skeleton)|
|
||
| mediump 精度(移动端才有) | macOS demo 无此问题 | 到 iOS demo 再管,骨骼矩阵用 highp |
|
||
|
||
---
|
||
|
||
## 9 · 怎么跑 / 演示检查点
|
||
|
||
```bash
|
||
export XRENDER_ASSET_ROOT="$PWD/../m2dev-client-main/assets"
|
||
cmake -B build -DXRENDER_ASSET_ROOT="$XRENDER_ASSET_ROOT" && cmake --build build -j
|
||
|
||
# 结构(D1)
|
||
./build/tools/gr2dump/gr2dump "$XRENDER_ASSET_ROOT/PC/ymir work/pc/warrior/warrior_cheongrin.gr2" --gltf /tmp/warrior.glb
|
||
# → 期望:骨架树打印、mesh 摘要、"bind pose self-check: PASS (max 3.1e-6)"
|
||
# → 把 /tmp/warrior.glb 拖进 Blender,骨架和 T-pose 网格应正常
|
||
|
||
# 渲染(D2–D4)
|
||
./build/app/xrender-demo
|
||
# 拖拽转视角;T 贴图;N 法线;W 线框;Space 播 dance_1(D5)
|
||
```
|
||
|
||
**演示成立的判据**:D0–D4 全绿 = "bgfx 能从 assets 的散装 gr2 + dds 渲出正确的带贴图 warrior,且有自检工具" —— 这就把 PLAN 的 M1 用一个能给人看的东西证了。D5 绿 = M2 的核心(曲线采样 + LBS)也站得住。
|
||
|
||
**下一步**(demo 之后):接 oracle(Windows dump)做 D5 的数值对拍 → 换 iOS/Android toolchain 跑 D2–D4 → 按 PLAN M3 补性能采集。
|