feat(web): 端口分离 + VMD 动作支持;契约升至 0.2.0
需求 1:管理端端口与软件 API 端口分离 - 软件 API 端口 PORT(默认 3000)只提供 /api、/health、/static、/contracts, 不提供管理页面(未命中路径 404);管理端端口 ADMIN_PORT(默认 3001) 提供管理页面与全部接口;ADMIN_HOST 可单独限制管理端来源 - main.ts 同进程监听两个端口,用 req.socket.localPort 区分端口行为; ADMIN_PORT=PORT 为单端口兼容模式(只创建一个 server,无 EADDRINUSE) - 端口非法在生产环境中止启动;ADMIN_PORT=PORT 只 warn - 启动日志分别打印两个端口的访问地址 需求 2:动作数据支持 VMD(MikuMikuDance) - vmd-parser.ts:二进制解析(30 字节头 + Shift-JIS 模型名 + 111 字节/条骨骼 关键帧 + morph 段)、MMD 贝塞尔插值、GBK 模型名回退、固定 30fps - mmd-skeleton.ts:标准 MMD 骨架;静止比例从参考 PMX 骨骼段实测得到 (足首 1.43 / 膝 6.57 / 髋 11.50 / 首 16.21 / 頭 17.16,腿长 10.09) - mmd-pose.ts:FK + 两骨骼解析腿部 IK(膝盖朝正面,不可达收敛为伸直) + 左手系→右手系 Z 镜像(由真实数据对照实验确定:94.1% vs 5.9%) - retarget.ts:抽出 worldFramesToKeypointFrames(),BVH 与 VMD 共用重定向 - MotionFormat 增 vmd、扩展名映射增 .vmd;管理端上传支持 .vmd - scripts/inspect-vmd.mjs:上传前自查工具 契约:rest-api / data-models / model-formats → 0.2.0(另两库已同步逐字节一致) 验证:85 项单测通过(VMD 31 项);契约与 29 条路由一致;typecheck/build 通过; 真实样本经 HTTP 端到端:霜雪千年 2901 帧(IK 腿)、恋爱循环 4604 帧(FK 腿) 均 preprocessState=done、0 NaN、0 越界(测试数据已从数据库清理)。
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
# T-WEB-006 BVH/FBX → 关键点序列预处理
|
||||
# T-WEB-006 BVH/VMD/FBX → 关键点序列预处理
|
||||
|
||||
- **状态**:done
|
||||
- **依赖**:T-WEB-004
|
||||
|
||||
## 目标
|
||||
|
||||
把上传的 3D 动作数据转换为与 Android 端输出**同构**的 MediaPipe 33 点关键点序列,
|
||||
使 Windows 端 DTW 引擎只有一条输入通路。
|
||||
把上传的 3D 动作数据(BVH / VMD / FBX / glTF / keypoints-json)转换为与 Android
|
||||
端输出**同构**的 MediaPipe 33 点关键点序列,使 Windows 端 DTW 引擎只有一条输入通路。
|
||||
|
||||
## load
|
||||
|
||||
```
|
||||
DSH.md
|
||||
contracts/model-formats.md # 锚点:§2 动作格式、§2.1 预处理产物、§2.2 关节名映射
|
||||
contracts/model-formats.md # 锚点:§2 动作格式、§2.1 预处理产物、§2.2 关节名映射、§2.3 VMD
|
||||
contracts/skeleton-topology.md # 锚点:§1 33 点、§3 父节点表、§5 关节角
|
||||
web-server/src/preprocess/*
|
||||
```
|
||||
@@ -29,9 +29,12 @@ web-server/src/preprocess/*
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `bvh-parser.ts` | 纯 TS BVH 词法/语法解析 + 逐帧前向运动学(FK) |
|
||||
| `vmd-parser.ts` | VMD 二进制解析:30 字节头 + Shift-JIS 模型名 + 111 字节/条骨骼关键帧 + morph 段;MMD 贝塞尔插值,固定 30fps |
|
||||
| `mmd-skeleton.ts` | 标准 MMD 骨架(含全角/半角 `IK`/`IK` 别名归一);静止比例从参考 PMX 实测 |
|
||||
| `mmd-pose.ts` | MMD FK + 两骨骼解析腿部 IK + 左手系→右手系 Z 镜像,输出逐帧世界坐标 |
|
||||
| `math3d.ts` | 向量/矩阵工具、旋转合成、关节角余弦 |
|
||||
| `joint-map.ts` | BVH 关节名 → MediaPipe 点索引的默认映射表 |
|
||||
| `retarget.ts` | 单帧解算 35 项(33 点 + neck/pelvis 虚拟关节)→ 骨盆归零 → 自适应缩放 → 投影 |
|
||||
| `joint-map.ts` | 关节名 → MediaPipe 点索引的默认映射表 |
|
||||
| `retarget.ts` | 导出的 `worldFramesToKeypointFrames()`:任意骨架世界坐标 → 骨盆归零 → 自适应缩放 → 投影 → 9 个关节角;BVH 与 VMD 共用 |
|
||||
| `keypoint-codec.ts` | 239 字节二进制帧编解码、NDJSON 序列化/解析、关键点 JSON 校验 |
|
||||
| `assimp.ts` | 可选外部 Assimp 调用(FBX/glTF → BVH) |
|
||||
| `preprocess.service.ts` | 编排、落盘、状态与错误记录 |
|
||||
@@ -49,13 +52,35 @@ web-server/src/preprocess/*
|
||||
| 格式 | 处理 |
|
||||
|---|---|
|
||||
| `.bvh` | 内置解析器,无外部依赖 |
|
||||
| `.vmd` | 内置解析器,无外部依赖;固定 30fps,见「VMD 通路」 |
|
||||
| `.json` | 直通校验与规范化(须符合 `mediapipe33@0.1.0`) |
|
||||
| `.fbx` `.gltf` `.glb` | 调用可选 Assimp 转 BVH;未安装 Assimp 时写入可读错误并置 `preprocessState=failed` |
|
||||
|
||||
## VMD 通路
|
||||
|
||||
```
|
||||
.vmd ─▶ vmd-parser.ts ─▶ mmd-skeleton.ts ─▶ mmd-pose.ts ─▶ retarget.ts
|
||||
二进制 + Shift-JIS 标准 MMD 骨架 FK + 腿部 IK worldFramesToKeypointFrames()
|
||||
+ 贝塞尔插值 + Z 镜像 (与 BVH 共用)
|
||||
```
|
||||
|
||||
- VMD 不含骨骼长度、只有旋转(少数骨骼带位移),必须靠 `mmd-skeleton.ts` 的
|
||||
标准骨架做 FK;骨骼名按别名表归一(全角 `IK` / 半角 `IK`),未纳入标准骨架的
|
||||
骨骼(裙摆 / 物理 / 表情等)被忽略但计数。
|
||||
- **腿部驱动自动判定**:`足IK` 轨道存在非零位移 → 两骨骼解析 IK(膝盖朝角色
|
||||
正面弯,目标不可达时收敛为伸直,不产生 NaN);否则走 FK 旋转(部分动作数据把
|
||||
腿烘焙成 FK)。
|
||||
- **手性**:MMD 为左手系,统一做 Z 镜像(旋转 `(x,y,z,w) → (-x,-y,z,w)`、位移 z
|
||||
取反)后进入右手系通路。由真实数据对照实验确定:腿部烘焙为 FK 的样本中,
|
||||
`zMirrored` 下「膝盖朝前」占 94.1%,不转换仅 5.9%(见 `test/mmd-vmd.spec.ts`)。
|
||||
- 重定向**完全复用 BVH 通路**:`worldFramesToKeypointFrames()` 负责骨盆归零 /
|
||||
自适应缩放 / 图像空间投影 / 9 个关节角,因此两种来源的产物结构一致。
|
||||
- 固定 **30 fps**,上传时填写的 `fps` 被忽略。
|
||||
|
||||
## 验收
|
||||
|
||||
```bash
|
||||
npm run test # 25 例:解析 / FK / 重定向 / 编解码 / 拓扑 / 契约版本
|
||||
npm run test # 85 例(含 31 例 VMD):解析 / FK / 重定向 / 编解码 / 拓扑 / 契约版本
|
||||
npm run test:smoke # 冒烟 [3] 段落
|
||||
```
|
||||
|
||||
@@ -63,5 +88,11 @@ npm run test:smoke # 冒烟 [3] 段落
|
||||
MediaPipe 归一化范围、置信度分层、时间戳按帧率推进、序列随时间变化、
|
||||
NDJSON 往返、解码器拒绝错误长度与 magic。
|
||||
|
||||
VMD 侧另测:二进制解析与截断报错、贝塞尔/四元数插值、标准骨架比例与镜像、
|
||||
FK 与腿部 IK(可达/不可达/退化)、手性对照、合成 VMD 端到端 0 NaN / 0 越界。
|
||||
|
||||
两个真实样本实测(`../示例文件/`):`【动作】霜雪千年.vmd` 2901 帧(腿部 IK)
|
||||
约 0.4s、`恋爱循环.vmd` 4604 帧(烘焙 FK)约 2.5s,均 0 NaN / 0 越界。
|
||||
|
||||
样例数据由 `test/fixtures/generate-sample-bvh.mjs` 生成(60 帧挥手 + 深蹲,
|
||||
21 关节 / 63 通道),可复现。
|
||||
21 关节 / 63 通道),可复现;VMD 诊断可用 `scripts/inspect-vmd.mjs`。
|
||||
|
||||
Reference in New Issue
Block a user