feat(models): 发布 v0.2.0 —— GLB/VRM 主力格式、自动缩略图与分析、模型审核与骨骼预览
需求 1:系统确定以 GLB / VRM 为模型主力格式
- 服务端新增支持库:
- model-inspector.ts:解析 GLB/VRM 的面数、顶点、材质、贴图、形变目标、
**骨骼树(名称/层级/是否蒙皮/humanoid 映射/子节点数)**、humanoid 覆盖率与可驱动性
- model-thumbnail.ts:**纯 TS 软件光栅化**(z-buffer + Lambert + 完整蒙皮矩阵)
生成 512×512 PNG 缩略图,零原生依赖、不装 headless-gl
- vrm-writer.ts:GLB → VRM 1.0(写入 VRMC_vrm 扩展与 humanoid 映射)
- gltf-packer.ts:glTF(.gltf + .bin + 贴图)→ 单文件 GLB
- humanoid 候选表与 Godot 客户端 humanoid_map.gd **逐字对齐**,并有单测断言两边一致
(服务端判定的"可驱动" = 客户端实际能否驱动)
需求 2:模型管理
- .glb / .vrm / .gltf **直接导入**(不重编码,GLB 含 VRM 扩展会自动识别为 VRM)
- .pmx / .fbx 服务端转换:assimp → .glb →(失败)→ .gltf 后自行打包成 GLB
→(MODEL_CONVERT_FORMAT=vrm 时)补 VRMC_vrm 产出 VRM 1.0;每步 assimp info 回读校验
- .zip **自动搜索包内可用模型**:.vrm → .glb → .gltf(直接导入)优先,
其次 .pmx → .fbx(转换);同优先级内按"指名字 → 目录最浅 → 文件最大"
- 入库后自动分析(models.stats)与生成缩略图(models.thumbnail_asset_id),
失败均不阻断入库;管理端列表显示小缩略图、预览放开到 vrm/glb/gltf
需求 3:审核功能
- 上传的模型状态**一律 pending**(不再自动发布、不再自动设默认)
- 审核界面新增模型面板:体积、面数、顶点、材质、贴图、骨骼、形变目标等指标,
humanoid 覆盖进度、可驱动性结论(缺哪些必需骨骼)、warnings,以及
**骨骼详细预览**(层级缩进、蒙皮/Humanoid 徽章、子节点数、搜索保留祖先链、
仅看蒙皮、限高滚动 + 只渲染前 400 行)
- 审核通过可带 setPrimary=true 一并设为默认模型(此时才写源码种子);
POST /api/models/:id/primary 仅允许 published(否则 409)
- scripts/import-default-model.mjs 改为"上传 → 审核通过 + 设默认"流程
其它:
- models 新增 stats 列(迁移 AddModelStats1789460000000,已在真实库执行)
- 契约升至 rest-api/data-models/model-formats 0.4.0、database 0.3.0(另两库已同步)
- 版本号 0.1.0 → 0.2.0(package.json ×2 + lockfile + project.yaml)
- 文档:README.txt / DSH.md / project.yaml / tasks 005·007·010 全面更新;
顶层开发书更新为 v1.4
验证:168 项单测通过(10 文件);契约 6 项与 29 条路由一致;typecheck/build 通过;
真实端到端:MMD 压缩包 → GLB + 分析(面数 9071/骨骼 238/humanoid 0-22) + 缩略图(15KB PNG);
GLB 直接导入 sha256 与源文件一致;审核通过后 defaultModelId 由 VRM 变为该模型
This commit is contained in:
+25
-19
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: OpenDance Studio Web 管理服务端 API
|
||||
version: 0.3.0
|
||||
version: 0.4.0
|
||||
description: |
|
||||
OpenDance Studio 的唯一数据源与契约权威。
|
||||
契约版本见 contracts/VERSION。字段语义见 contracts/data-models.md。
|
||||
@@ -317,24 +317,26 @@ paths:
|
||||
schema: { $ref: '#/components/schemas/ModelPage' }
|
||||
post:
|
||||
tags: [models]
|
||||
summary: 上传模型 + 来源信息(VRM 优先;支持 MMD 模型压缩包自动转换)
|
||||
summary: 上传模型(GLB/VRM 直接导入;PMX/FBX 服务端转 GLB 或 VRM)
|
||||
description: |
|
||||
`file` 为单个文件:
|
||||
系统以 **GLB / VRM 为主力格式**(两个客户端都原生加载):
|
||||
|
||||
- `.vrm` / `.glb` / `.gltf` / `.fbx`:直接入库,`format` 由扩展名决定
|
||||
- `.zip`:视为 MMD 模型包。服务端解压 → 选取 `.pmx`
|
||||
(优先 `MODEL_CONVERT_PMX` 指定的名字,默认 `Hatsune Miku.pmx`;
|
||||
否则取目录最浅、文件最大者)→ 调 Assimp 导出 **FBX**,
|
||||
导出后**回读校验**(Assimp 的 FBX 导出器对带蒙皮的大骨架模型会写出损坏文件),
|
||||
失败自动回退 **GLB**,并把过程写入 `conversionNote`。
|
||||
GLB 产物会把 Assimp 写成外部 URI 的贴图内嵌进二进制块,保证单文件可用。
|
||||
压缩包原件作为 `model-archive` 资产留存以便溯源。
|
||||
- `.glb` / `.vrm` / `.gltf`:**直接导入**,不重编码
|
||||
- `.pmx` / `.fbx`:服务端转换,**优先 GLB、其次 VRM**
|
||||
- `.zip`:**自动搜索包内可用模型** —— `.vrm` → `.glb` → `.gltf`(直接导入)
|
||||
优先,其次 `.pmx` → `.fbx`(转换)。压缩包原件作为 `model-archive` 资产留存。
|
||||
|
||||
转换成功且 `autoDefault` 不为 false 时,该模型会被置为全局默认模型
|
||||
(`isPrimary=1` + `status=published`),并把身份(名称/格式/**压缩包 sha256**)
|
||||
写进源码 `web-server/src/config/default-model.ts`。
|
||||
注意:**转换产物不是字节级确定的**(同一份 zip 每次转换的产物 sha256 都不同),
|
||||
所以默认模型种子以压缩包 sha256 为匹配键,产物 sha256 只作记录。
|
||||
转换链路(`model-convert.ts`):`assimp export → .glb` →(失败)`→ .gltf` 后
|
||||
由服务端自行打包成 GLB →(`MODEL_CONVERT_FORMAT=vrm` 时)在 GLB 之上补
|
||||
`VRMC_vrm` 扩展产出 VRM 1.0。每一步都 `assimp info` **回读校验**,
|
||||
并把过程写进 `conversionNote`;贴图会内嵌进 GLB,保证单文件可用。
|
||||
|
||||
入库时自动做两件事并写入 `stats` / `thumbnailUrl`:模型分析(面数、骨骼树、
|
||||
humanoid 覆盖、可驱动性)与缩略图生成。
|
||||
|
||||
**状态一律 `pending`(待审核)**:模型必须审核通过后才可能被客户端消费;
|
||||
设为默认模型请在审核通过时带 `setPrimary=true`,或调用
|
||||
`POST /api/models/{id}/primary`(仅已发布模型可设)。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -346,9 +348,7 @@ paths:
|
||||
file: { type: string, format: binary }
|
||||
name: { type: string }
|
||||
isPrimary: { type: boolean }
|
||||
autoDefault:
|
||||
type: boolean
|
||||
description: 仅对 `.zip` 生效;转换成功后是否自动设为默认模型,默认 true
|
||||
# 注意:上传不再提供"自动发布/自动设为默认",模型一律进入待审核
|
||||
sourceType: { $ref: '#/components/schemas/SourceType' }
|
||||
sourceName: { type: string }
|
||||
sourceUrl: { type: string }
|
||||
@@ -777,6 +777,12 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
description: 模型文件(转换产物)的 sha256 —— 每次转换都会变(Assimp 导出非字节级确定),仅作记录
|
||||
stats:
|
||||
type: object
|
||||
nullable: true
|
||||
description: >-
|
||||
模型分析结果(面数 / 顶点数 / 贴图 / 骨骼树 / humanoid 覆盖 / 可驱动性),
|
||||
审核界面据此判断"能不能驱动与评分"。结构见 contracts/model-formats.md §1.1
|
||||
sourceSha256:
|
||||
type: string
|
||||
nullable: true
|
||||
|
||||
Reference in New Issue
Block a user