- extension/src/{port,platform,codepage} -> src/; native_render -> src/host
(+ dxt, shaders/); libgr2 -> src/gr2; extension/third_party -> third_party
- extension/tests -> tests/port, libgr2/tests -> tests/gr2
- android-native -> android (build.sh, push-client.sh moved in)
- script -> scripts; tools/40250 -> tools/server; oracle -> tools/granny-oracle;
perf tools -> tools/perf
- all build trees under build/ (native, release, android, port-gate)
- xrender:: CMake aliases -> mt::; port-map ledger impl paths rewritten
No code changes. ctest 15/15, port_gate macos+android PASS, port_map check 0 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.3 KiB
oracle 运行手册(Oracle-Lite)
oracle.c 只用 granny2 的公开 C API(granny.h):文件 IO + 数学 + 写二进制,不碰 GPU / D3D / EterGrnLib。
所以除了原生 Windows,Wine 也能跑。
依赖
granny.h+granny2_x64.lib+granny2_x64.dll—— 泄露 SDK 里就有:MobileSource/Cross Platform/Granny-3D-SDK-main/{include,lib/win64}/(32 位版在lib/win32/。)- 这套 DLL 是 SDK 2.9.12。资产由 2.4.0.7 导出(M0 T2 从解压后的 gr2 里读到)。
容器格式 + 组合公式(
BuildCompositeTransform4x4/ColumnMatrixMultiply4x3/ B 样条系数) 在 2.4–2.11 稳定,libgr2 的 self-check 6.1e-5 已印证。差异风险在 B 样条边界 / 少数曲线细节。 → 先跑selfcheck(下)确认坐标约定和 t=0 == bind pose,再决定是否换 2.4.x runtime(见GRANNY-VERSION.md)。
A. 原生 Windows(MSVC)
cmake -S oracle -B oracle\build -DGRANNY_SDK="C:\path\to\Granny-3D-SDK-main"
cmake --build oracle\build --config Release
oracle\build\Release\oracle.exe probe "assets\PC\ymir work\pc\warrior\warrior_cheongrin.gr2"
B. macOS + Wine —— ✅ 已验证(macOS 26.4 / Apple Silicon)
brew install mingw-w64
# wine-stable cask 需要 sudo 装 gstreamer 依赖;无 sudo 时直接解包用:
tar -xf "$(ls ~/Library/Caches/Homebrew/downloads/*wine-stable*.tar.xz)" -C /tmp/wine
xattr -rc "/tmp/wine/Wine Stable.app"
WINE="/tmp/wine/Wine Stable.app/Contents/Resources/wine/bin/wine"
# 编译(脚本封装了 MinGW 的坑:granny.h 的 __int64 关键字)
tools/granny-oracle/build-wine.sh # → tools/granny-oracle/oracle.exe + tools/granny-oracle/granny2_x64.dll
# ⚠ WINEPREFIX 不能在 /tmp(Wine 拒绝在非属主目录建 prefix)
export WINEPREFIX="$HOME/.wine-oracle" WINEDEBUG=-all
"$WINE" wineboot --init # 首次,Rosetta 下 3–8 分钟
cd oracle
GR2='Z:\Users\me\...\assets\PC\ymir work\pc\warrior\warrior_cheongrin.gr2'
"$WINE" oracle.exe probe "$GR2"
"$WINE" oracle.exe dump "$GR2" - 0.0 \
-o 'Z:\Users\me\...\xrender-poc\test\oracle_dumps\cheongrin_bind_t0.bin' --mesh 4
"$WINE" oracle.exe dump "$GR2" 'Z:\...\action\dance_1.gr2' 11.333 \
-o 'Z:\...\test\oracle_dumps\cheongrin_dance1_t11.333.bin' --mesh 4
坑:① MinGW GCC 不认 granny.h 里的 __int64 → build-wine.sh 用 -D__int64="long long";
② Wine prefix 不能放 /tmp(非属主);③ wine-stable 首启会刷一大堆 MoltenVK 日志到 stderr,无害,WINEDEBUG=-all 挡不住它,grep 掉即可。
C.(stretch)从 SDK source/ 移植 POSIX libgranny
Granny-3D-SDK-main/source/ 有全套源码(62k 行)+ CMakeLists,但重度 Windows 化
(winxx/win64/*.cpp、winxx_granny_windows.cpp、WINDOWS_IGNORE_PACKING_MISMATCH)。
wii/、wiiu/、ps3/ 目录里有 ansi_* 的通用实现可参考。真要做是几小时的平台层移植,
不在关键路径 —— 先走 A / B。
对拍(tools/oracle_diff)
build/tools/oracle_diff <model.gr2> <anim.gr2|-> <dump.bin> [--t <sec>] [--mesh N]
libgr2 侧用 gr2::sample_pose / bind_pose + 手写 LBS 算一遍,报:
- gate 1:骨骼世界矩阵 vs oracle 层①
max|Δ| - gate 2:mesh N 蒙皮顶点 vs oracle
max‖Δ‖ - verdict:阈值 1e-3(noise_floor 就绪前)
已跑结果(Granny 2.9.12.0 vs libgr2):
| 用例 | mat max|Δ| | vtx max‖Δ‖ |
|---|---|---|
warrior_cheongrin bind |
1.5e-5 | 4.6e-5 |
warrior_cheongrin + dance_1 @ t=0/4.7/11.333/20 |
≤ 4.1e-5 | ≤ 6.5e-5 |
assassin bind(双 root + 非单位 InitialPlacement) |
3.1e-5 | 4.6e-5 |
全部 ≈ float32 累积误差 → libgr2 的类型树 / 世界姿势 / OldCurve 求值 / CPU LBS 与真 Granny 数值一致。
关键:
oracle.c的GrannyBuildWorldPose要把 model 的InitialPlacement当Offset4x4传 (GrannyGetModelInitialPlacement4x4),否则非单位 InitialPlacement 的模型(如 assassin)会差一个平移。 libgr2 的skeleton.initial_placement做同一件事。踩过一次。
产出
- dump →
test/oracle_dumps/(布局见FORMAT.md) - 噪声地板 →
test/noise_floor.json(oracle dump跑同(model,anim,t)100 次 + 换一版 相邻 DLL 一次,取矩阵 / 顶点的max|Δ|;M2 的 ε = 这个 ×10)