diff --git a/DSH.md b/DSH.md index 4f49df8..8e0e22f 100644 --- a/DSH.md +++ b/DSH.md @@ -35,7 +35,7 @@ opendance-web ←── 本仓库 | 模型检查 / 缩略图 | `models/model-inspector.ts`(面数 / 顶点 / 贴图 / **骨骼树** / humanoid 覆盖 / 可驱动性)、`models/model-thumbnail.ts`(纯 TS 光栅化 PNG,零原生依赖) | | 模型压缩包 | 纯 TS ZIP 读取器(`common/zip-reader.ts`)+ 自动搜索包内模型 + 外部 Assimp 转换(`models/model-convert.ts`);GLB 打包 `models/gltf-packer.ts`、贴图内嵌 `models/glb-textures.ts`、GLB->VRM 封装 `models/vrm-writer.ts` | | 默认模型种子 | `web-server/src/config/default-model.ts`(服务端/CLI 写入,运行期只读) | -| 认证 | **本版本不含认证**(内网部署);上线前须补 ≥ T-WEB-011 | +| 认证 | **本版本不含认证**(内网部署);上线前须补 >= T-WEB-011 | ## 3. 单次会话加载范围 @@ -58,9 +58,9 @@ opendance-web ←── 本仓库 | 项 | 建议值 | |--|--| -| 单任务文件数 | ≤ 6 | -| 单任务代码行数 | ≤ 1500 | -| 单任务 Token | ≤ 12K | +| 单任务文件数 | <= 6 | +| 单任务代码行数 | <= 1500 | +| 单任务 Token | <= 12K | | 契约引用 | 只引用锚点,不加载全文 | ## 5. 目录结构 @@ -88,7 +88,7 @@ opendance-web/ │ │ │ ├── glb-textures.ts # 把外部贴图内嵌进 GLB 的 BIN chunk │ │ │ ├── vrm-writer.ts # GLB -> VRM 1.0(写 VRMC_vrm 扩展) │ │ │ ├── model-inspector.ts # 面数 / 顶点 / 骨骼树 / humanoid 覆盖 / 可驱动性 -│ │ │ ├── model-thumbnail.ts # 纯 TS 光栅化 512×512 PNG(含蒙皮,零原生依赖) +│ │ │ ├── model-thumbnail.ts # 纯 TS 光栅化 512x512 PNG(含蒙皮,零原生依赖) │ │ │ ├── default-model-source.ts # 渲染 / 写入 src/config/default-model.ts │ │ │ └── vrm-inspector.ts │ │ ├── config/default-model.ts # 默认模型种子(由审核 / CLI 写入,运行期只读) @@ -270,7 +270,7 @@ POST /api/models ─┬─.glb /.vrm /.gltf ─▶ importModelFile() 直接导 不做任何几何/材质损失。 - `models/model-inspector.ts`:解析 GLB/VRM,算面数 / 顶点 / 贴图 / **骨骼树** / humanoid 覆盖 / 可驱动性(缺哪些必需骨骼)/ warnings。 - - `models/model-thumbnail.ts`:纯 TS 软件光栅化 512×512 PNG(含蒙皮),零原生依赖。 + - `models/model-thumbnail.ts`:纯 TS 软件光栅化 512x512 PNG(含蒙皮),零原生依赖。 - `models/default-model-source.ts`:渲染 / 写入 `src/config/default-model.ts`。 - `config/default-model.ts`:默认模型种子(审核 / CLI 写入,运行期只读)。 - **压缩包自动搜索**:优先级 `.vrm` -> `.glb` -> `.gltf` -> `.pmx` -> `.fbx`;同一优先级内 @@ -329,7 +329,7 @@ POST /api/models ─┬─.glb /.vrm /.gltf ─▶ importModelFile() 直接导 maxBoneDepth=23`,`missingRequiredBones` 为全部 16 根(日文 MMD 骨名无法映射)。 - 直接上传 `.glb`:**HTTP 201 / 0.36s**、`conversionNote=直接导入(GLB,未做重编码)`, 入库文件 sha256 与源文件**一致**。 -- 缩略图:512×512 PNG / 15,043 字节,纯 TS 光栅化(含蒙皮),目视确认为居中的 +- 缩略图:512x512 PNG / 15,043 字节,纯 TS 光栅化(含蒙皮),目视确认为居中的 T-pose 角色图;真实 10MB VRM(83 关节、5307 顶点)单张耗时 60-121ms(中位 79ms)。 - 管理端 3D 预览放开到 `vrm/glb/gltf`(`fbx` 仍提示"仅归档,请先转换为 GLB/VRM"); 列表页显示 40px 缩略图。 diff --git a/contracts/data-models.md b/contracts/data-models.md index 978f5f3..f377aea 100644 --- a/contracts/data-models.md +++ b/contracts/data-models.md @@ -54,9 +54,9 @@ rejected 已驳回 | 字段 | 类型 | 必填 | 说明 | |--|--|--|--| | `id` | number | 读 | | -| `title` | string | ✅ | 曲名,≤200 | -| `artist` | string | ✅ | 演唱者,≤200 | -| `album` | string | | 专辑,≤200 | +| `title` | string | ✅ | 曲名,<=200 | +| `artist` | string | ✅ | 演唱者,<=200 | +| `album` | string | | 专辑,<=200 | | `language` | string | | `zh` / `en` / `ja` / `ko` / `instrumental` | | `durationMs` | number | | 时长毫秒 | | `bpm` | number | | 节拍 | @@ -74,7 +74,7 @@ rejected 已驳回 |--|--|--|--| | `id` | number | 读 | | | `songId` | number\|null | | 关联曲目 | -| `name` | string | ✅ | 动作名称,≤200 | +| `name` | string | ✅ | 动作名称,<=200 | | `format` | enum | ✅ | `bvh` / `vmd` / `fbx` / `gltf` / `glb` / `keypoints-json` | | `fileUrl` | string | 读 | 原始动作文件 | | `fileSize` | number | 读 | 字节 | @@ -95,7 +95,7 @@ rejected 已驳回 | 字段 | 类型 | 必填 | 说明 | |--|--|--|--| | `id` | number | 读 | | -| `name` | string | ✅ | 模型名称,≤200 | +| `name` | string | ✅ | 模型名称,<=200 | | `format` | enum | ✅ | `glb` / `vrm`(主力)/ `gltf` / `fbx`(历史数据)。上传的 PMX/FBX 会被服务端转换成 glb 或 vrm 再入库,见 model-formats.md 1.1 | | `vrmVersion` | string\|null | 读 | `0.x` / `1.0`,非 VRM 为 null | | `isPrimary` | boolean | | 全局默认模型;**同一时刻全库至多一个 `true`** | diff --git a/contracts/model-formats.md b/contracts/model-formats.md index c8db38d..4155755 100644 --- a/contracts/model-formats.md +++ b/contracts/model-formats.md @@ -126,7 +126,7 @@ VRM 版本识别:读取 GLB 的 JSON chunk,检查 `extensions.VRM`(0.x) VMD 是二进制格式:文件头 `Vocaloid Motion Data 0002`(30 字节)+ 模型名(20 字节, Shift-JIS)+ 骨骼关键帧数组(每条 **111 字节**:15 字节骨骼名、uint32 帧号、 -float32×3 位移、float32×4 旋转四元数、64 字节贝塞尔插值参数)+ morph / 相机 / +float32x3 位移、float32x4 旋转四元数、64 字节贝塞尔插值参数)+ morph / 相机 / 灯光 / 自阴影 / IK / 可见性段落。**VMD 不含骨骼长度**,只有旋转(少数骨骼带位移), 因此服务端必须自带一套标准 MMD 骨架才能做 FK。 @@ -161,7 +161,7 @@ FK 旋转)。两份示例文件分别命中两种路径。 **标准骨架数值来源**:解析参考模型的 PMX 骨骼段实测得到(而非凭经验估计), 再去掉模型自身原点偏移并抹平左右不对称。关键比例:足首 1.43 / ひざ 6.57 / 髋 11.50 / センター 10.88 / 首 16.21 / 頭 17.16(总高约 19.4,髋高比约 59%); -大腿 4.94 + 小腿 5.15 ≈ 腿长 10.07。 +大腿 4.94 + 小腿 5.15 ~ 腿长 10.07。 ## 3. 上传限制 diff --git a/contracts/skeleton-topology.md b/contracts/skeleton-topology.md index d8c9db0..2fd6624 100644 --- a/contracts/skeleton-topology.md +++ b/contracts/skeleton-topology.md @@ -121,5 +121,5 @@ spine = angle(neck, pelvis, midpoint(left_ankle, right_ankle)) `angle(a, b, c)` = 向量 `ba` 与 `bc` 的夹角余弦,即 `dot(normalize(a-b), normalize(c-b))`,落在 `[-1, 1]`。 -**DTW 序列结构(冻结)**:每帧 = `[33 × (x, y, z), 9 个关节角余弦] = 108 维`。 +**DTW 序列结构(冻结)**:每帧 = `[33 x (x, y, z), 9 个关节角余弦] = 108 维`。 低置信度点(`visibility < 0.5`)在插值阶段由相邻帧线性补全,不参与代价计算。 diff --git a/contracts/websocket-keypoints.md b/contracts/websocket-keypoints.md index fd40f48..ce1ec53 100644 --- a/contracts/websocket-keypoints.md +++ b/contracts/websocket-keypoints.md @@ -27,7 +27,7 @@ ws://:8765/pose bit2 = 上半身模式(仅 0..22 有效) bit3-7 保留,必须为 0 4 4 frame_id u32 LE 单调递增,从 0 开始 -8 231 keypoints 33 × 7B 见下 +8 231 keypoints 33 x 7B 见下 -- 合计 239 ``` diff --git a/tasks/005-model-upload.md b/tasks/005-model-upload.md index ec5262d..98847bc 100644 --- a/tasks/005-model-upload.md +++ b/tasks/005-model-upload.md @@ -89,7 +89,7 @@ web-server/scripts/import-default-model.mjs | 项 | 实现 | 说明 | |--|--|--| | 分析 | `model-inspector.ts` -> `models.stats` | 面数 / 顶点 / 网格 / 材质 / 贴图 / 节点 / 形变目标 / 动画 / 蒙皮骨骼数 / **骨骼树** / humanoid 覆盖 / **可驱动性(缺哪些必需骨骼)** / warnings;迁移 `AddModelStats1789460000000` 新增 `models.stats TEXT` | -| 缩略图 | `model-thumbnail.ts` -> `models.thumbnail_asset_id` | 纯 TS 软件光栅化 512×512 PNG(读 GLB/glTF -> 节点层级与蒙皮 -> 包围盒构图 -> 透视投影 -> z-buffer 平面着色 -> 手写 PNG),**零原生依赖**;失败或非 glTF 容器留空,前端退化为格式占位图 | +| 缩略图 | `model-thumbnail.ts` -> `models.thumbnail_asset_id` | 纯 TS 软件光栅化 512x512 PNG(读 GLB/glTF -> 节点层级与蒙皮 -> 包围盒构图 -> 透视投影 -> z-buffer 平面着色 -> 手写 PNG),**零原生依赖**;失败或非 glTF 容器留空,前端退化为格式占位图 | ## 状态与默认模型(硬性) @@ -129,7 +129,7 @@ web-server/scripts/import-default-model.mjs `missingRequiredBones` 为**全部 16 根**(日文 MMD 骨名无法映射到 humanoid)。 - 直接上传 `.glb` -> **HTTP 201 / 0.36s**、`conversionNote=直接导入(GLB,未做重编码)`, 入库文件 sha256 与源文件**一致**(不重编码)。 -- 缩略图:512×512 PNG / 15,043 字节,服务端纯 TS 光栅化(含蒙皮),目视确认为居中的 +- 缩略图:512x512 PNG / 15,043 字节,服务端纯 TS 光栅化(含蒙皮),目视确认为居中的 T-pose 角色图;真实 10MB VRM(83 关节、5307 顶点)单张耗时 60-121ms(中位 79ms)。 - 审核:`POST /api/reviews/model/22/approve` 带 `setPrimary:true` -> 200, 模型 `status=published`、`isPrimary=true`,源码种子写入 `sourceSha256=72eb6882...`; @@ -172,7 +172,7 @@ npm run test:smoke # 冒烟 [4] 段落 | GLB 失败回退 glTF->GLB 打包 | ✅ GLB 直出失败时 `gltf-packer.ts` 自行打包;每步 `assimp info` 回读校验 | | `MODEL_CONVERT_FORMAT=vrm` 封装 | ✅ `vrm-writer.ts` 补 `VRMC_vrm`;humanoid 不全回退 GLB | | 自动分析写入 `models.stats` | ✅ 实测 `triangles=9071 bones=238 humanoidMapped=0/22 drivable=false` | -| 缩略图生成且不影响入库 | ✅ 512×512 / 15,043 B;失败只告警 | +| 缩略图生成且不影响入库 | ✅ 512x512 / 15,043 B;失败只告警 | | 上传一律 pending、不自动设默认 | ✅ `create()` 固定 `PublishStatus.Pending` + `isPrimary=false` | | `setPrimary` 审核通过时生效 | ✅ `POST /api/reviews/model/22/approve` + `setPrimary:true` -> 200、`isPrimary=true` | | 非 published 设默认 | ✅ `POST /api/models/:id/primary` -> 409 | @@ -199,7 +199,7 @@ node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000 --dry-run # 只列 ``` 实测(真实 21MB VRM,290 骨骼):回填出面数 53552 / 顶点 44662 / 骨骼 290 / -humanoid 7/22,并重建 512×512 缩略图。 +humanoid 7/22,并重建 512x512 缩略图。 文件在存储中丢失时返回 **409** 并说明原因,不会用全 0 结果覆盖既有分析数据。 另外 `StorageService.persistBuffer` 在 sha256 去重命中但文件不存在时会**自愈补写**, 避免换过 `STORAGE_DIR` 或只恢复数据库后出现"静默全 0 分析"。 diff --git a/tasks/006-keypoint-preprocess.md b/tasks/006-keypoint-preprocess.md index 031690c..36e4e51 100644 --- a/tasks/006-keypoint-preprocess.md +++ b/tasks/006-keypoint-preprocess.md @@ -43,7 +43,7 @@ web-server/src/preprocess/* - 33 点顺序与父节点表来自 `contracts/skeleton-topology.md`,虚拟关节 `neck=33`、`pelvis=34` 只参与关节角计算,**不进入输出序列**。 -- 投影:`x_img = 0.5 + x·s`,`y_img = 0.5 − y·s`,`z_img = −z·s`, +- 投影:`x_img = 0.5 + x s`,`y_img = 0.5 - y s`,`z_img = -z s`, `s = 0.35 / max(|x|,|y|,|z|)`(按整段动作自适应,保证不出画)。 - 置信度分层:直接映射 **1.0**、按层级推算 **0.5**、无法推算 **0.0**。 @@ -84,7 +84,7 @@ npm run test # 85 例(含 31 例 VMD):解析 / FK / 重定向 / npm run test:smoke # 冒烟 [3] 段落 ``` -覆盖:60 帧样例经预处理后 **60 帧 × 33 点 × 9 关节角**、坐标落在 +覆盖:60 帧样例经预处理后 **60 帧 x 33 点 x 9 关节角**、坐标落在 MediaPipe 归一化范围、置信度分层、时间戳按帧率推进、序列随时间变化、 NDJSON 往返、解码器拒绝错误长度与 magic。