test(real-samples): 用真实素材端到端验证 ①模型→VRM 与 ②动作内部标准
`model-archive.spec.ts` / `mmd-vmd.spec.ts` 用的都是合成夹具与假 Assimp,
证明得了边界,证明不了「真素材能出片」。新增 opt-in 的
`test/real-samples.spec.ts`(6 项),样例或 Assimp 缺失时整体跳过,
CI 与无素材的开发机不受影响:
① 模型 → VRM(两个真实 PMX 压缩包)
- 初音未来 1.0 MB → 1.10 MB VRM,humanoid 19/22,2 张贴图全内嵌;
- 奥黛塔 28.1 MB → 26.08 MB VRM,humanoid 21/22,18 张贴图全内嵌,
39 材质(36 项双面已补回);两者 drivable = true;
- 断言 VRMC_vrm 1.0、所有 images 带 bufferView 且无 uri、无 MMD 专属
材质扩展泄漏、蒙皮下标不越界,并**逐字节复算 POSITION 访问器的
min/max**(glTF 唯一强制填写的边界,客户端据此做视锥剔除与 AABB;
读访问器必须按 byteStride 走,否则交错缓冲里的 u16 会被读成乱码)。
② 动作内部标准 + 骨骼重绑定(真实 VMD,0.8 MB / 96.7 s)
- maxFrame 2900 → 2901 帧、标准骨架 31/31 命中、双侧腿部 IK 链齐备;
- MMD 骨骼名 → joint-map 候选名的重绑定结果逐一比对(15 个键);
- 15 个直接关节的置信度恒为 1.0(绝不退化成 0.5 的推算值),
其余 18 点为 0.5,缺失为 0;骨盆归零后投影 max|xy| = 0.85 ≤ 1;
- NDJSON 往返一致、缩略图 PNG 有效、手腕摆幅非退化。
顺带修 `assimp-locate.spec.ts` 的非密闭性:其用例断言的是 PATH/常见系统
目录查找,但会继承外部 ASSIMP_BIN(assimp-lib.sh 中 ASSIMP_BIN 优先级高于
PATH),开发者按本项目文档设过 ASSIMP_BIN 时 npm test 会假失败。
规模 24 文件 / 289 项 → 25 文件 / 295 项。产物与已发布的 v0.7.4 无关
(发布包不含 web-server/test/),故不新发版本。
This commit is contained in:
@@ -30,3 +30,36 @@
|
||||
| 非 VRM 模型不出现在客户端目录 | ✅ `catalog/export` 过滤 + 指纹过滤;`test/model-usability.spec.ts` 覆盖 |
|
||||
| 不可用模型有明确原因 | ✅ DTO 增 `usable` / `unusableReason` 字段 |
|
||||
| 三端契约一致 | ✅ `data-models 0.7.0` / `model-formats 0.5.0` 快照三端逐字节一致 |
|
||||
|
||||
## 真实样例回归(T-WEB-003 / T-WEB-011)
|
||||
|
||||
`test/real-samples.spec.ts` 是 **opt-in** 的真实素材端到端用例:合成夹具证明不了
|
||||
「真的 MMD 素材能出片」,而这个文件直接用仓库根 `示例文件/` 下的真模型跑完整转换管线。
|
||||
样例与 Assimp 任一缺失时整个 describe 跳过,因此无素材的开发机与 CI 依旧全绿。
|
||||
|
||||
```bash
|
||||
# 跑真实素材(Assimp 不在 PATH 时用 ASSIMP_BIN 指绝对路径)
|
||||
ASSIMP_BIN=/usr/bin/assimp npm test -- test/real-samples.spec.ts
|
||||
# 样例放在别处时
|
||||
OPENDANCE_SAMPLES_DIR=/path/to/示例文件 ASSIMP_BIN=/usr/bin/assimp npm test
|
||||
```
|
||||
|
||||
实测(Assimp 6.0,2024 版素材):
|
||||
|
||||
| 样例 | 输入 | 产物 | humanoid | 贴图 | 材质 |
|
||||
|--|--|--|--|--|--|
|
||||
| 初音未来(PMX in zip) | 1.0 MB | 1.10 MB VRM | 19/22,`drivable` | 2 张全内嵌 | 3 |
|
||||
| 奥黛塔(PMX in zip) | 28.1 MB | 26.08 MB VRM | 21/22,`drivable` | 18 张全内嵌 | 39(36 双面已补回) |
|
||||
|
||||
用例断言的不变量(与具体模型无关):
|
||||
|
||||
- `extensions.VRMC_vrm.specVersion === "1.0"`,`asset.version === "2.0"`
|
||||
- **所有** `images` 都带 `bufferView`、无 `uri`(外链贴图离开原目录即坏图)
|
||||
- 无 `KHR_materials_specular/volume/transmission/ior/emissive_strength` 泄漏(MMD 专属参数不能进标准 PBR 产物)
|
||||
- **POSITION 访问器的 `min`/`max` 与逐元素读出的真实包围盒一致** —— 这是 glTF 规范唯一强制填写的边界,客户端拿它做视锥剔除与 AABB,填错会导致模型被整体剔除。读访问器时必须按 `byteStride` 走,否则交错缓冲里的 u16 数据会被读成乱码
|
||||
- 蒙皮下标 `< skin.joints.length`(越界会让客户端取到错误矩阵)
|
||||
|
||||
> **已知的上游瑕疵(不影响可用性)**:Assimp 6.0 的 glTF2 导出器会给
|
||||
> `JOINTS_0` 与 `inverseBindMatrices` 写偏保守的 `min`(前者把真实最小值 6 写成 0,
|
||||
> 后者对角元 1 写成 `2.2e-308`)。两者都是**合法下界**(`min ≤ 真实值 ≤ max`),
|
||||
> 没有任何运行时会据此做剔除,因此不改产物、只在用例里锁定真正要紧的 POSITION 边界。
|
||||
|
||||
@@ -40,3 +40,30 @@ DTW 的两侧输入是同一套特征。
|
||||
| MMD(日文骨骼名)模型可驱动 | ✅ 实测 21/22、19/22 覆盖;客户端可正常驱动 |
|
||||
| Godot 端候选表与服务端一致 | ✅ 一致性测试(跨仓库生成 + 校验) |
|
||||
| 动作统一为 33 点 | ✅ `keypoint_format = mediapipe33@0.1.0`;Android 端推流同构 |
|
||||
|
||||
## 真实样例回归(T-WEB-006)
|
||||
|
||||
`test/real-samples.spec.ts` 用仓库根 `示例文件/【动作】霜雪千年.vmd`(0.8 MB,真实舞蹈,
|
||||
96.7 s)跑完整 `parseVmd → vmdToWorldFrames → worldFramesToKeypointFrames` 管线。
|
||||
VMD 通路不依赖 Assimp,因此只要样例在就会真跑;样例缺失时整个 describe 跳过。
|
||||
|
||||
实测:`maxFrame = 2900` → **2901 帧**(30 fps / 96.7 s),轨道 31 条,
|
||||
标准骨架 `MMD_SKELETON` 的 **31 根全部命中**(缺失 0),双侧腿部 IK 链
|
||||
(`左/右足IK` + `ひざ` + `足首` + `つま先`)齐备,因此 IK 解算路径生效;
|
||||
重定向 135 ms(0.05 ms/帧),产出 33 点 + 2 虚拟点 + 9 关节角,NDJSON 6.51 MB。
|
||||
|
||||
用例锁定的重绑定不变量:
|
||||
|
||||
| 断言 | 实测 |
|
||||
|--|--|
|
||||
| 世界帧键名恰为 joint-map 候选名(MMD 名 → 英文规范名的重绑定结果) | 15 个键逐一比对 |
|
||||
| 直接绑定的 15 个索引置信度**只能**是 `1.0`(不许退化成推算值) | 0/11/12/13/14/15/16/23/24/25/26/27/28/31/32 → 全部 `[1]` |
|
||||
| 其余 18 个点由层级推算 | 全部 `[0.5]`,缺失(`0`)为 0 |
|
||||
| 骨盆归零 + 图像空间投影 | `max|xy| = 0.85 ≤ 1`,越界 0 |
|
||||
| 关节角(cos 值)有限且 ∈ [-1,1] | 范围 `[-1.000, 0.995]` |
|
||||
| NDJSON 往返一致 / 缩略图有效 | 2901 行往返一致;PNG magic `89504e470d0a1a0a` |
|
||||
| 动作非退化 | 左右腕 x 方向摆幅 > 0.1,鼻尖摆幅显著小于手腕 |
|
||||
|
||||
> 性能注意:2901 帧 × 33 点逐元素 `expect` 会拖满 vitest 默认 5 s 超时。
|
||||
> 用例改成先聚合违规项、最后只断言一次(保留具体帧号与下标),并显式给
|
||||
> 大用例 120–300 s 超时。
|
||||
|
||||
Reference in New Issue
Block a user