feat(motions): 动作预览——骨架缩略图 + 管理端 3D 回放(契约 data-models 0.6.0)

按《DSH 开发文档 v1.6》4.11(T-WEB-014,用户标注「重要」):

服务端:
- 抽出公共 PNG 编码器 src/common/png.ts(模型缩略图改为复用,零原生依赖不变)
- 新增 src/motions/motion-thumbnail.ts:关键点序列 -> 正视图骨架 PNG
  (复用 33 点 PARENT_TABLE 连线,包围盒按整段计算,低置信度点不画)
- motions 增 thumbnail_asset_id(迁移 AddMotionThumbnail)+ AssetKind.MotionThumbnail
- /preprocess 生成缩略图并落库,失败只记 warning;MotionDto 增 thumbnailUrl(契约 0.6.0)

管理端:
- 新增 MotionPreview:懒加载 NDJSON -> 骨架线框 + 关节实例球体
  支持播放/暂停、上一帧/下一帧、时间轴拖动、0.25x-2x 变速、循环
  叠加帧号/当前时间/总时长,按整段包围盒自动取景
  three.js 单独分包(lazy + Suspense),主包 241 kB 不涨
- 动作列表新增 40px 骨架缩略图列(点击开预览)+ 预览按钮 + 内联预览面板
- 新增 admin/src/lib/keypoint-sequence.ts 与 lib/skeleton-topology.ts

测试:新增 8 项;规模 18 文件 / 237 项。版本 0.5.0。
This commit is contained in:
opendance
2026-09-17 00:21:09 +08:00
parent 9eae6f3e43
commit aad60cbd2e
25 changed files with 1158 additions and 89 deletions
+24 -8
View File
@@ -10,7 +10,23 @@ OpenDance Studio **阶段一**: Web 管理服务端(NestJS + TypeORM + 现有 My
## 状态
**阶段一已完成**: T-WEB-001 -> T-WEB-010 全部落地,当前版本 **v0.3.0**(契约 `rest-api / data-models / model-formats 0.4.0`、`database 0.3.0`)。
**阶段一已完成**: T-WEB-001 -> T-WEB-010 全部落地,当前版本 **v0.5.0**(契约 `rest-api 0.4.0`、`data-models 0.6.0`、`model-formats 0.5.0`、`skeleton-topology 0.2.0`、`database 0.3.0`)。
v0.5.0 修订(**动作预览**,契约 data-models -> 0.6.0):
1. **动作骨架缩略图(服务端)**:新增 `motions/motion-thumbnail.ts`——把关键点序列画成正视图骨架 PNG(零原生依赖,复用抽出的 `common/png.ts`);随 `/preprocess` 生成并落库(`motions.thumbnail_asset_id`,迁移 `AddMotionThumbnail`),失败只记 warning 不影响预处理;
2. **动作 3D 回放(管理端,重要)**:新增 `MotionPreview`——懒加载 `GET /api/motions/:id/keypoints`(NDJSON),骨架线框 + 关节球体,支持**播放/暂停、上一帧/下一帧、时间轴拖动、0.25x-2x 变速、循环**,叠加"第 N/M 帧 · 当前时间 · 总时长",按整段包围盒自动取景;three.js 单独分包,主包体积不涨;
3. **动作管理列表**:新增 40px 骨架缩略图列(点击开预览)+「预览/收起预览」按钮;
4. 关键点解析与拓扑(管理端 `lib/keypoint-sequence.ts` / `lib/skeleton-topology.ts`)纯函数化,便于测试;
5. 新增 8 项测试;规模 **18 文件 / 237 项**。
v0.4.0 修订(**骨骼重绑定**:把"一律转 VRM"真正可用,契约 skeleton-topology -> 0.2.0):
1. **修复 humanoid 覆盖率恒为 0/22 的根因**:`normalizeBoneName()` 原先只保留 `a-z0-9`,**中日文字符被整个丢掉**(`上半身`/`左ひじ`/`首` 全变空串);现在保留 CJK/假名,并把全角 ASCII(`足IK`)折成半角;
2. **候选表补齐四类命名**:VRoid / Mixamo / **MMD 标准骨架**(`下半身`/`上半身`/`上半身2`/`首`/`頭`/`左肩`/`左腕`/`左ひじ`/`左手首`/`左足`/`左ひざ`/`左足首`/`左つま先`)/ 常见中文名;精确匹配保证 `左足IK` 不抢占 `左足`;
3. **服务端与 Windows 客户端逐字一致**:`humanoid_map.gd` 的 `CANDIDATES` 由服务端表生成写入,parity 测试断言;
4. **实测**:奥黛塔 PMX **0/22 → 21/22**、初音未来 PMX **0/22 → 19/22**,均 `drivable=true` 且产出 VRM;
5. 契约 `skeleton-topology` -> **0.2.0**:新增 §6「内部动作标准与模型骨骼重绑定」。
v0.3.0 修订(**VRM 主力化**,契约 data-models / model-formats -> 0.5.0):
@@ -85,15 +101,15 @@ v0.2.0 主要变更:
3. 取得发布包并校验(地址用你们自己的制品库替换; 下面是本仓库的发布地址)
```bash
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.4.0/opendance-web-v0.4.0.tar.gz
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.4.0/SHA256SUMS
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.5.0/opendance-web-v0.5.0.tar.gz
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.5.0/SHA256SUMS
sha256sum -c SHA256SUMS # 期望输出 "OK"
```
4. 解压并生成配置(交互式引导,逐项问答,口令不回显)
```bash
tar -xzf opendance-web-v0.4.0.tar.gz && cd opendance-web-v0.4.0
tar -xzf opendance-web-v0.5.0.tar.gz && cd opendance-web-v0.5.0
bash deploy/init-env.sh
```
@@ -163,8 +179,8 @@ FLUSH PRIVILEGES;
### 5. 部署步骤
```bash
tar -xzf opendance-web-v0.4.0.tar.gz
cd opendance-web-v0.4.0
tar -xzf opendance-web-v0.5.0.tar.gz
cd opendance-web-v0.5.0
bash deploy/init-env.sh # 首次启动引导: 逐项问答生成 deploy/.env(见下)
bash deploy/install.sh # 已以 root 登录, 不需要 sudo
```
@@ -337,12 +353,12 @@ npm run dev # http://localhost:5173(/api 与 /health 代理到 3000)
cd web-server
npm run contracts:check # 契约版本一致性
npm run typecheck # TypeScript 严格检查
npm run test # 229 项单元测试 / 17 个文件(VMD 31、模型压缩包 27、
npm run test # 237 项单元测试 / 18 个文件(VMD 31、模型压缩包 27、
# 模型检查器 25、缩略图 8、骨骼过滤 8、
# 预处理 25、init-env 16、admin-port 16、
# access-log 9、VRM 检查器 6、.env 预检 12、
# Assimp 定位与安装 13、stats 容量与编码 12、
# 缩略图贴图采样 6、MMD 材质对齐 9、模型可用性 5、MMD 骨名重绑定 4)
# 缩略图贴图采样 6、MMD 材质对齐 9、模型可用性 5、MMD 骨名重绑定 4、动作预览 8)
npm run build # 生产构建
npm run start:prod & # 启动
npm run test:smoke # 57 项端到端冒烟(需要数据库可连通)