84 lines
4.1 KiB
Markdown
84 lines
4.1 KiB
Markdown
# contracts/model-formats.md — 模型与动作格式契约 v0.1.0
|
||
|
||
## 1. 角色模型(models)
|
||
|
||
| 格式 | 扩展名 | 支持级别 | 说明 |
|
||
|---|---|---|---|
|
||
| VRM | `.vrm` | **首选(P0)** | glTF 2.0 二进制 + VRM 扩展;Windows 端 godot-vrm 直接加载 |
|
||
| glTF Binary | `.glb` | P1 | 需含骨骼(skin);无骨骼则仅作静态展示 |
|
||
| glTF | `.gltf` (+ `.bin`, 贴图) | P2 | 多文件,管理端需打包为 zip 上传 |
|
||
| FBX | `.fbx` | P2 | 需 Assimp 转换,管理端仅归档,不保证重定向质量 |
|
||
|
||
**VRM 优先**的选取顺序见 `data-models.md` §4.1。
|
||
|
||
VRM 版本识别:读取 GLB 的 JSON chunk,检查 `extensions.VRM`(0.x)或
|
||
`extensions.VRMC_vrm`(1.0),写入 `models.vrm_version`。
|
||
|
||
## 2. 动作数据(motions)
|
||
|
||
| 格式 | 扩展名 | 预处理支持 | 说明 |
|
||
|---|---|---|---|
|
||
| BVH | `.bvh` | ✅ 内置纯 TS 解析器 | 文本格式,服务端直接解析 → FK → MediaPipe33 |
|
||
| FBX | `.fbx` | ⚠️ 需 Assimp | 服务端调用 `assimp` 可执行文件导出为 BVH/glTF 后再解析;未安装则 `preprocessState=failed` |
|
||
| glTF/GLB | `.gltf` / `.glb` | ⚠️ 需 Assimp | 同上 |
|
||
| 关键点 JSON | `.json` | ✅ 直通校验 | 已是 `mediapipe33@0.1.0` 结构,仅做校验与归一化 |
|
||
|
||
### 2.1 预处理产物(冻结)
|
||
|
||
- 格式:NDJSON(`application/x-ndjson`),每行一帧,UTF-8 无 BOM
|
||
- 一行结构:
|
||
|
||
```json
|
||
{"frame":0,"timeMs":0.0,"points":[[0.5,0.5,0.0,1.0],"…33 项"],"angles":[1.0,"…9 项"]}
|
||
```
|
||
|
||
- `points[i] = [x, y, z, confidence]`,语义与 `websocket-keypoints.md` 解码后完全一致
|
||
- `angles` 为 `skeleton-topology.md` §5 定义的 9 个关节角余弦,顺序固定:
|
||
`[shoulder_L, shoulder_R, elbow_L, elbow_R, hip_L, hip_R, knee_L, knee_R, spine]`
|
||
- 帧率:保留源帧率;`fps` 写入 `motions.fps`,缺省 30
|
||
- 坐标系换算:所有 3D 动作数据统一转到 **Y 轴向上、右手系、骨盆为原点**,
|
||
再投影到 MediaPipe 归一化图像空间:
|
||
`x_img = 0.5 + x_world * scale`,`y_img = 0.5 - y_world * scale`,`z_img = -z_world * scale`,
|
||
`scale = 0.35 / max(|x|,|y|,|z|)`(按整段动作的最大范围自适应,保证不出画)
|
||
- 缺失关节(BVH 中不存在对应骨骼):`confidence = 0.0`,坐标沿用最近可用父节点
|
||
|
||
### 2.2 BVH 关节名映射(默认表,可在 `web-server/src/preprocess/joint-map.ts` 扩展)
|
||
|
||
| MediaPipe 点 | 常见 BVH 关节名(不区分大小写,按序匹配) |
|
||
|---|---|
|
||
| 0 nose | `Head`, `Neck1`, `Head_Nub` |
|
||
| 11 left_shoulder | `LeftShoulder`, `LeftArm`, `LShoulder` |
|
||
| 12 right_shoulder | `RightShoulder`, `RightArm`, `RShoulder` |
|
||
| 13 left_elbow | `LeftForeArm`, `LeftElbow`, `LElbow` |
|
||
| 14 right_elbow | `RightForeArm`, `RightElbow`, `RElbow` |
|
||
| 15 left_wrist | `LeftHand`, `LeftWrist`, `LHand` |
|
||
| 16 right_wrist | `RightHand`, `RightWrist`, `RHand` |
|
||
| 23 left_hip | `LeftUpLeg`, `LeftHip`, `LHipJoint` |
|
||
| 24 right_hip | `RightUpLeg`, `RightHip`, `RHipJoint` |
|
||
| 25 left_knee | `LeftLeg`, `LeftKnee`, `LKnee` |
|
||
| 26 right_knee | `RightLeg`, `RightKnee`, `RKnee` |
|
||
| 27 left_ankle | `LeftFoot`, `LeftAnkle`, `LAnkle` |
|
||
| 28 right_ankle | `RightFoot`, `RightAnkle`, `RAnkle` |
|
||
| 31 left_foot_index | `LeftToeBase`, `LeftToe`, `LToe` |
|
||
| 32 right_foot_index | `RightToeBase`, `RightToe`, `RToe` |
|
||
|
||
未映射到的点按 `skeleton-topology.md` §5 的层级由父节点推算,
|
||
并置 `confidence` 降低(`0.5`),表示估算而非实测。
|
||
|
||
## 3. 上传限制
|
||
|
||
| 项 | 值 |
|
||
|---|---|
|
||
| 单文件上限 | 512 MiB(`UPLOAD_MAX_BYTES` 可覆盖) |
|
||
| 允许扩展名 | 上表全部;其他一律 422 |
|
||
| 存储路径 | `${STORAGE_DIR}/{kind}/{yyyy}/{mm}/{sha256前2位}/{sha256}.{ext}` |
|
||
| 去重 | 相同 sha256 复用同一 `assets` 记录,不重复落盘 |
|
||
| 文件名 | 服务端不使用原始文件名做路径,只存 `assets.original_name` |
|
||
|
||
## 4. 开源依赖与许可证(引用 `open-source-credits.md`)
|
||
|
||
- `bvh`(MIT)— BVH 解析参考实现
|
||
- `Assimp`(BSD-3-Clause)— FBX/glTF 转换,**可选外部依赖**
|
||
- `@pixiv/three-vrm`(MIT)— 管理端 VRM 预览
|
||
- `three`(MIT)— 管理端 3D 预览
|