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
21 KiB
Demo 开发方案 —— bgfx 渲染 assets 下的 gr2 资源
本文件把
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 优先)
# 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 薄封装:
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)
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 的验收)
// 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 · 怎么跑 / 演示检查点
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 补性能采集。