-
released this
2026-09-16 18:55:25 +08:00 | 22 commits to main since this releaseOpenDance Studio -- Web 管理服务端 v0.2.5
MMD 材质/着色对齐: 修复 PMX 转换后"颜色/贴图看起来不对"。契约不变
(rest-api / data-models / model-formats 0.4.0、database 0.3.0)。诊断(本地真 Assimp 6.0.4 + 样本逐项核对)
PMX 的着色模型(卡通阶梯 + 球面贴图 + 平坦环境光)与 glTF PBR 不是一回事。
样本【奥黛塔】_by_原神.zip(39 个材质)核对结果:项目 结论 双面(不剔除背面) PMX 里 36 个材质有该标志; 导出的 GLB 里 doubleSided一个都没有 -> 头发/裙摆/睫毛这类薄片被单面剔除, 这是"贴图/颜色不对"的主因specular / shininess PMX 为 (0,0,0)/1.0; Assimp 写出KHR_materials_specular(黑色)与KHR_materials_volume等与 MMD 无关的扩展环境光 / toon MMD 的平坦环境光与 toon 阶梯在 glTF core 里没有对应项 球面贴图(SPH/SPA) 该模型 15 项; glTF core 无槽位 accessor.min/maxAssimp 写坏(实测 min = 2.2250738585072014e-308), Godot 用它算包围盒本次改动
- 新增
models/pmx-materials.ts: 只读 PMX 材质段(布局与权威库 mmd-parser 一致,
注意贴图索引宽度是 1 字节), 拿到 diffuse / specular / shininess / ambient /
drawFlag / 球面贴图 / toon / faceCount; - 新增
models/glb-materials.ts: 转换后按索引把 PMX 参数补回 GLB ——
doubleSided(按 drawFlag bit0)、metallicFactor=0、
roughnessFactor=clamp(1-shininess/100, 0.5, 1)、baseColorFactor x diffuse(含 alpha);
清掉KHR_materials_specular/volume/transmission/ior/emissive_strength;
修正全部浮点 accessor 的 min/max; - 新增
MODEL_SHADING(默认pbr, 可选unlit):unlit给每个材质写
KHR_materials_unlit, 颜色 = 贴图 xbaseColorFactor、与场景光照无关,
最接近 MMD 的平涂观感(three.js 与 Godot 都支持); - 能力边界(不假装保留): 球面贴图与 toon 阶梯无法用 glTF core 表达, 数量写进
模型的conversion_note, 审核面板可见; - 新增 9 项测试; 测试规模 15 文件 / 220 项。
关于"增加支持的库进行转换"
换一个 PMX->glTF 转换器不能解决这件事: 球面贴图(matcap)与 toon 阶梯是
视点相关/光照相关的着色, 无法烘进 baseColorTexture; glTF 2.0 core 也没有这两个
槽位。行业做法只有三条: ①按 glTF 能力补齐(本次做的双面/PBR 规范化),
②交付KHR_materials_unlit的平涂颜色(本次的MODEL_SHADING=unlit),
③只在支持 MToon 的查看器里用 VRM(VRMC_materials_mtoon有 matcapTexture)——
但 Godot 客户端不认 MToon, 所以服务端仍以 GLB 为准。升级后需要重新导入模型
材质修正在转换时生效:
- 重新上传该 PMX 压缩包(或在管理端删除后重传);
reanalyze-models.mjs不会重转材质, 它只刷新分析与缩略图。
想用平涂颜色: 在
deploy/.env里加MODEL_SHADING=unlit后重跑deploy/install.sh
并重新导入模型。校验
opendance-web-v0.2.5.tar.gzsha256:c1988dd4e9fceff48c8ae368e50adc0d6ba1275aa7f7abe643d1bbab7ddb3c4fDownloads
- 新增
-
released this
2026-09-16 04:02:35 +08:00 | 23 commits to main since this releaseOpenDance Studio -- Web 管理服务端 v0.2.4
修复「导入 PMX 后模型显示为白模、脸部颜色丢失」。 契约与 v0.2.3 相同
(rest-api / data-models / model-formats 0.4.0、database 0.3.0)。定位结论:GLB 数据没问题,问题在缩略图渲染器
本地装真 Assimp 6.0.4 完整复现该模型(
【奥黛塔】_by_原神.zip)后逐层核对:检查项 结论 转换 正常: 23.7 MB GLB、643 骨骼、93,350 面、1.2 s 贴图内嵌 18 张贴图与压缩包原图逐字节一致 材质 -> 贴图对应 用权威 PMX 解析器比对, 39 个材质完全一致(不是映射错) UV 采样 正确: 脸部材质取到的就是皮肤色 (246,221,216) 缩略图渲染器 它刻意不解码 baseColorTexture, 只用 baseColorFactor上色; 而 MMD 模型的 baseColorFactor 基本都是白色 -> 缩略图必然全白管理端 3D 预览(three.js)本来就有贴图, 白模只出现在列表页与审核面板的缩略图。
本次改动
model-thumbnail.ts内置极简 PNG 解码器(zlib inflate + 逐行反滤波, 8 位非隔行,
colorType 0/2/3/4/6), 按baseColorFactor x 贴图逐像素采样(透视校正 UV);
解码失败 / 非 PNG / 隔行扫描时回退纯色, 绝不抛错;- 按
alphaMode处理透明度: OPAQUE 忽略贴图 alpha(MMD 语义)、MASK 按
alphaCutoff丢弃、BLEND 做 source-over; - 取景收紧(FRAMING 1.6 -> 1.3), 40px 列表缩略图主体更清楚;
- 新增 6 项测试(含"白模"回归: baseColorFactor 全白 + 红贴图必须输出偏红);
测试规模 14 文件 / 211 项。
关于"要不要默认转 VRM"
不必, 也解决不了这个问题:VRM 就是 GLB + humanoid 元数据, 材质管线与 GLB
完全相同(同样只有 baseColorTexture, 没有 MMD 的球面/卡通贴图槽位);
而该模型 humanoid 映射为 0/22, 转 VRM 也会按设计回退交付 GLB。
真正让贴图显示出来的是本次对渲染端的修复。这个模型本来也不用球面贴图
(diffuse 全白、无 sphere), 所以核心 GLB 没有"丢贴图"。升级后请重新生成缩略图
已有模型的缩略图是旧渲染器生成的, 升级后执行一次(v0.2.3 起服务端已带该脚本):
cd /opt/opendance/web-server node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000 --force只重算单个:
--model-id 22; 先看清单:--dry-run。校验
opendance-web-v0.2.4.tar.gzsha256:138f31220afe5455cda187ca64c6b45624b35e4865eecf6af4d86e0f3284399cDownloads
-
released this
2026-09-16 03:30:12 +08:00 | 24 commits to main since this releaseOpenDance Studio -- Web 管理服务端 v0.2.3
修复导入 PMX 模型返回 500,以及 GBK 压缩包丢贴图。 契约与 v0.2.2 相同
(rest-api / data-models / model-formats 0.4.0、database 0.3.0)。根因一:500 Internal server error
样本
【奥黛塔】_by_原神.zip(643 根骨骼、93,350 面)转换本身成功
(GLB 23.7MB、1.2s),但分析结果里的骨骼树按骨骼逐条展开,JSON 实测 70,980 字节,
而models.stats是TEXT(上限 65,535)。MySQL / MariaDB 严格模式直接拒绝写入
(ER_DATA_TOO_LONG / 1406), 而这一步没有兜底 -> 未捕获 -> 500。根因二:18 张贴图只内嵌了 17 张
tex/髮.png的 GBK 字节是F3 8C。文件名解码先试 Shift-JIS, 而 WHATWG 的
shift_jis把未定义码位映射成私用区不可见字符(不是 U+FFFD), 于是被判为
「解码成功」, 永不回退 GBK; 贴图名对不上就丢图。本次改动
models.stats-> MEDIUMTEXT(实体mediumtext+ transformer 保留 JSON 语义;
不用simple-json以免实体元数据与库列型不一致), 新增迁移
AddModelStatsMediumText1789550000000(install.sh会自动执行);- 新增
shrinkStatsForStorage()兜底: 即使库没迁移, 也只截断骨骼树
(父在前, 保留祖先链), 面数 / 骨骼数 / humanoid 覆盖 / 可驱动性 / warnings
全部保留; 服务层只在 1406 / DATA_TOO_LONG 时裁剪重试一次, 其它 DB 错误照旧抛出; - 压缩包文件名解码把「私用区字符」也算解错信号并回退 GBK —— 实测该模型贴图
由 17/18 变为 18/18 全部内嵌; - 新增 12 项测试; 测试规模 13 文件 / 205 项。
已经在跑旧版本的服务器
不改代码也能立刻解除 500(把列扩到 16MB 即可, 旧代码写入的数据不变):
ALTER TABLE models MODIFY stats MEDIUMTEXT NULL;执行的数据库与账号就是
deploy/.env里的DB_NAME/DB_USER:mysql -h 127.0.0.1 -u "$DB_USER" -p "$DB_NAME" \ -e "ALTER TABLE models MODIFY stats MEDIUMTEXT NULL;"改完直接重新导入该压缩包即可(无需重启服务)。
想同时拿到「18/18 贴图」与后续兜底, 再部署 v0.2.3:cd /root curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.3/opendance-web-v0.2.3.tar.gz curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.3/SHA256SUMS sha256sum -c SHA256SUMS tar -xzf opendance-web-v0.2.3.tar.gz && cd opendance-web-v0.2.3 bash deploy/install.sh # 自动执行 AddModelStatsMediumText 迁移并重启服务迁移完成后
GET /health的database.migrationsPending应为 0。校验
opendance-web-v0.2.3.tar.gzsha256:d8aefad75baa8080e47c72a95b7990ca15af04d9e58c8492dea5c0ae5c0d3a2dDownloads
-
released this
2026-09-16 02:56:50 +08:00 | 25 commits to main since this releaseOpenDance Studio -- Web 管理服务端 v0.2.2
修复「上传 PMX 压缩包报 Assimp 缺失」的报错与部署体验。 功能与契约与 v0.2.1 相同
(rest-api / data-models / model-formats 0.4.0、database 0.3.0)。修复的问题
服务器没装 Assimp 时, 上传 .pmx / .fbx(或含它们的压缩包)只得到两条:
GLB 直出:导出失败(ENOENT):spawn assimp ENOENT glTF 直出:导出失败(ENOENT):spawn assimp ENOENT用户看不出「缺什么、怎么装」, 也不知道只上传 .glb / .vrm 其实不需要它。
本次改动
- 新增
src/common/assimp.ts: 按ASSIMP_BIN->PATH->/usr/bin、/usr/local/bin
等常见目录定位可执行文件(只认文件, 不认同名目录), 转换前先预检; 找不到就一次性
给出各发行版安装命令、查过的路径与替代办法, 不再打两条 ENOENT;
动作预处理(FBX / glTF -> BVH)复用同一文案。 - 新增
deploy/assimp-lib.sh:install.sh按发行版自动安装
(apt-get/dnf/yum/zypper/apk/pacman); 装在非常规目录时把绝对路径
写入deploy/.env的ASSIMP_BIN; 装不上只告警不中止, 可用SKIP_ASSIMP_INSTALL=1跳过。
Debian / Ubuntu 只装assimp-utils(不写死 libassimp5/6, 由发行版决定)。
check-access.sh自检增加 Assimp 一行。 /health增加assimp.path; 管理端「概览 -> 服务健康」显示 Assimp 状态, 不可用时提示。- 新增 13 项测试
test/assimp-locate.spec.ts; 测试规模 12 文件 / 193 项。
已经在跑 v0.2.0 / v0.2.1 的服务器
不用重新部署, 装一次 Assimp 即可, 服务无需重启:
# Debian / Ubuntu(让 apt 自己选 libassimp5 或 6) apt-get update && apt-get install -y assimp-utils # RHEL / CentOS / Rocky / Fedora # dnf install -y assimp assimp version # 能打印版本即成功 curl -fsS http://127.0.0.1:3000/health # 其中 assimp.available 应为 true装好后重新上传该压缩包即可。也可运行 v0.2.2 的
bash deploy/install.sh,
它会自动检查/安装 Assimp 并把结论写进安装日志。校验
opendance-web-v0.2.2.tar.gzsha256:0a42313fb31624647cd2fe4dce77c69ef8941c60066522d05a6dbd7ed66a61afDownloads
- 新增
-
released this
2026-09-15 22:27:16 +08:00 | 27 commits to main since this releaseOpenDance Studio -- Web 管理服务端 v0.2.1
v0.2.0 的补丁版: 修复首次部署必然失败的问题。功能与契约与 v0.2.0 相同
(rest-api / data-models / model-formats 0.4.0、database 0.3.0)。修复的问题
现象(在干净服务器上按 README 部署 v0.2.0 时):
./deploy/.env: line 53: Miku.pmx: command not found [install] 第 53 行执行失败(退出码 127)根因:
deploy/.env.example里MODEL_CONVERT_PMX=Hatsune Miku.pmx的值含空格却没加引号。
install.sh用set -a; . deploy/.env载入配置时会真的执行这个文件, 这一行被切成
「赋值 + 命令」,Miku.pmx被当成命令执行。本次改动
- 模板改为
MODEL_CONVERT_PMX="Hatsune Miku.pmx"(占位符值同样加引号); - 新增
deploy/env-lib.sh语法预检:install.sh与ctl.sh在 source 之前检查,
报错直接指出哪一行、原文、改法;$( )与反引号会建议改用单引号
(双引号里的它们仍会被执行); install.sh的失败提示补上出错文件名(.env被 source 时行号属于.env,
只报行号无法定位);- 新增 12 项回归测试
web-server/test/env-file.spec.ts, 锁住「模板可被 shell 原样载入」; - 文档补充
.env写法约定与 FAQ; 测试规模 11 文件 / 180 项。
已经部署到一半(v0.2.0)怎么继续
不必重新下载, 在解压目录里给这一行加上引号再重跑安装:
sed -i 's|^MODEL_CONVERT_PMX=Hatsune Miku.pmx$|MODEL_CONVERT_PMX="Hatsune Miku.pmx"|' deploy/.env bash deploy/install.sh校验
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.1/SHA256SUMS sha256sum -c SHA256SUMS # 期望 OKopendance-web-v0.2.1.tar.gzsha256:f574c82f4c9f7ac705ebd1099510bd863010b7f2d5d43fac43800b705ee9eaf0Downloads
- 模板改为
-
released this
2026-09-15 21:53:29 +08:00 | 42 commits to main since this releaseOpenDance Studio — Web 管理服务端 v0.2.0
三端开发顺序中的阶段一交付物:NestJS + TypeORM + 现有 MySQL 实例。
v0.2.0 主要变更
- GLB / VRM 确定为模型主力格式,服务端与客户端分别落实支持库;
- 模型管理:
.glb/.vrm直接导入;.pmx/.fbx服务端优先转 GLB、其次 VRM;
压缩包自动搜索包内可用模型;入库自动生成缩略图与模型分析; - 模型审核:上传一律
pending,审核界面含骨骼详细预览(评分基础); - 新增历史模型回填(
POST /api/models/:id/reanalyze+scripts/reanalyze-models.mjs); - 契约升至
rest-api/data-models/model-formats 0.4.0、database 0.3.0。
1. 模型格式策略
上传 服务端行为 .glb/.vrm/.gltf直接导入,不重编码(GLB 含 VRM 扩展会自动识别为 VRM) .pmx/.fbx转换: assimp → .glb→(失败)→ .gltf后自行打包成 GLB →(MODEL_CONVERT_FORMAT=vrm时)补VRMC_vrm扩展产出 VRM 1.0.zip自动搜索包内可用模型: .vrm→.glb→.gltf(直接导入)优先,其次.pmx→.fbx(转换)。同优先级内:指名字 → 目录最浅 → 文件最大每一步都用
assimp info回读校验(Assimp 会写出损坏文件),贴图内嵌进 GLB;
压缩包原件作为model-archive资产留存溯源。客户端支持库:Windows 客户端用 Godot 内置
GLTFDocument加载 glTF/GLB/VRM;
服务端的 humanoid 候选表与客户端humanoid_map.gd逐字对齐,并有单测断言两边一致。2. 自动缩略图与模型分析
- 缩略图(
model-thumbnail.ts):纯 TypeScript 软件光栅化(z-buffer + Lambert +
完整蒙皮矩阵),零原生依赖。512×512 PNG 写入models.thumbnail_asset_id。 - 模型分析(
model-inspector.ts):面数 / 顶点 / 网格 / 材质 / 贴图 / 形变目标 /
动画 / 节点 / 骨骼与骨骼树 / humanoid 覆盖 / 可驱动性,写入models.stats。 - 两者失败都不阻断入库。
3. 模型审核(本次重点)
- 上传的模型状态一律
pending:不再自动发布、不再自动设默认。 - 审核界面新增模型面板:体积、格式、容器、VRM 版本、面数/顶点/网格/材质/贴图/
骨骼/节点/形变目标/动画、humanoid 覆盖进度、可驱动性结论(缺哪些必需骨骼)、
warnings,以及骨骼详细预览(层级缩进、蒙皮/humanoid 徽章、子节点数、
搜索保留祖先链、仅看蒙皮、限高滚动 + 只渲染前 400 行)。 - 通过审核时可勾选**「同时设为全局默认模型」**(
setPrimary=true);单独设置用
POST /api/models/:id/primary(仅published,否则 409)。
为什么骨骼是审核重点:客户端按骨骼名映射 humanoid 规范(22 根,16 根为驱动
33 点所必需)。实测日文 MMD 骨名的 PMX 转 GLB 后覆盖 0/22 → 无法驱动;
某 VRM 只有 7/22;Bip001*命名的 FBX 转 GLB 后 17/22。审核时就能拦下来。4. 历史模型回填(升级必做一次)
升级后旧模型还没有分析与预览图:
cd /opt/opendance/web-server node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000 # 回填全部缺 stats 的 node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000 --dry-run # 只列清单 node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000 --force # 全部重算实测(真实 21 MB VRM / 290 骨骼):回填出面数 53552、顶点 44662、骨骼 290、
humanoid 7/22,并重建 512×512 缩略图。
文件在存储中丢失时接口返回 409 并说明原因(不会用全 0 结果覆盖已有数据);
StorageService.persistBuffer在 sha256 去重命中但文件不存在时会自愈补写
(避免换过STORAGE_DIR、或只恢复数据库后出现"静默全 0 分析")。5. 实测数据
上传 MMD 压缩包(含 Hatsune Miku.pmx) → HTTP 201 / 0.86s / format=glb / status=pending → conversionNote = Hatsune Miku.pmx → GLB,已内嵌 2 张贴图 → 分析:vertices=7155 triangles=9071 bones=238 humanoid=0/22 drivable=false → 缩略图:512×512 PNG / 15,043 字节(目视确认为带蒙皮的 T-pose 角色图) 直接上传 GLB → HTTP 201 / 0.36s / conversionNote = 直接导入(GLB,未做重编码) → 入库文件 sha256 与源文件完全一致(证明没有重编码) 审核 → POST /api/reviews/model/22/approve { setPrimary: true } → 200 → status=published、isPrimary=true;源码种子写入 sourceSha256=72eb6882… → 重建后 GET /api/catalog/export 的 defaultModelId 由 VRM 变为 22 缩略图性能(真实 10MB VRM / 83 关节 / 5307 顶点):60–121ms(中位 79ms) 契约校验 ✓ rest-api/data-models/model-formats 0.4.0、database 0.3.0;路由 30 条一致 类型检查 ✓ tsc --noEmit 通过(strict) 单元测试 ✓ 168 / 168 通过(10 个文件) 生产构建 ✓ nest build + vite build 通过6. 部署更新
cd /root curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.0/opendance-web-v0.2.0.tar.gz curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.0/SHA256SUMS sha256sum -c SHA256SUMS cp /opt/opendance/deploy/.env /root/opendance-env.bak.$(date +%s) # 先备份现网配置 tar -xzf opendance-web-v0.2.0.tar.gz && cd opendance-web-v0.2.0 cp /opt/opendance/deploy/.env deploy/.env grep -q '^ADMIN_PORT=' deploy/.env || echo 'ADMIN_PORT=3001' >> deploy/.env grep -q '^HOST=' deploy/.env || echo 'HOST=0.0.0.0' >> deploy/.env grep -q '^MODEL_CONVERT_FORMAT=' deploy/.env || echo 'MODEL_CONVERT_FORMAT=glb' >> deploy/.env bash deploy/install.sh # 会执行新迁移 AddModelStats # 回填历史模型(一次即可) cd /opt/opendance/web-server && node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000前置:PMX/FBX 转换需要 Assimp(
apt-get install -y assimp-utils libassimp6);
.glb/.vrm直接导入不需要 Assimp。已知事项
- 缩略图是纯色块着色的 T-pose 轮廓图,用于列表页快速辨识;精细预览由管理端
three.js 实时渲染(已放开vrm/glb/gltf,FBX 仍不支持)。 - PMX → GLB 会丢失物理、IK、toon/sphere 贴图、面部表情等 MMD 专有特性,
属格式能力差异(见contracts/model-formats.md§1.1)。 - 模型授权信息需部署方核实后再通过审核。
Downloads
-
released this
2026-09-14 19:02:20 +08:00 | 63 commits to main since this releaseOpenDance Studio — Web 管理服务端 v0.1.0
三端开发顺序中的阶段一交付物:NestJS + TypeORM + 现有 MySQL 实例。
本次修订(三条需求)
- 模型上传支持压缩包自动转换:上传 MMD 模型包
.zip→ 服务端解压选.pmx
→ Assimp 转 FBX(导出后回读校验,失败自动回退 GLB 并把贴图内嵌)
→ 入库;转换成功后设为默认模型并把身份写进源码; - 首次启动的 .env 引导流程:
deploy/init-env.sh交互式逐项填写、校验、
可做连通性自检,非交互场景绝不挂住; - 默认监听
0.0.0.0(代码、两份.env模板、安装脚本与引导脚本四处对齐并有测试守护)。
之前的双端口分离、VMD 支持、不使用 Docker / 不部署反代等变更继续有效。
1. 模型压缩包自动转换(
.zip)POST /api/models的file现在也接受.zip(MMD 模型包):.zip ─▶ ZIP 读取器 ─▶ 选主模型(.pmx) ─▶ Assimp 导出 FBX ─▶ 回读校验 ─┬─通过─▶ 入库 FBX (STORE/DEFLATE, (指名字/最浅/最大) └─失败─▶ 导出 GLB zip-slip 防护) (贴图内嵌) ─▶ 入库 GLB- 回读校验是必须的:Assimp 的 FBX 导出器对带蒙皮的大骨架模型会写出损坏文件。
实测本包(714 骨骼的 PMX):导出退出码 11、产物回读报
FBX-DOM no Objects dictionary found。只看退出码会把坏文件入库。 - 回退结果:
Hatsune Miku.pmx → GLB,1,152,464 字节,2 张贴图已内嵌,238 关节,
1 网格 / 240 节点 / 3 材质。转换耗时 < 1s(含解压与回读校验)。 - 贴图内嵌:Assimp 在 GLB 里把贴图写成外部 URI(还带
\反斜杠),
服务端把压缩包内的贴图搬进 GLB 的 BIN chunk,保证单文件可加载(否则是白模)。 - 溯源:压缩包原件存为
model-archive资产并记入models.source_asset_id;
转换说明写入models.conversion_note。 - 安全:解压前逐条校验路径(拒绝绝对路径与
..)、拒绝加密条目、
只支持 STORE/DEFLATE,不支持的方式明确报错。 - 失败语义:包内无
.pmx、Assimp 不可用、两种格式都失败 → HTTP 422,不产生记录。 - 顺带修掉了 multipart 中文文件名乱码(
模å_…→模型_…)。
默认模型写进源码
转换成功且
autoDefault不为 false(默认)时:- 把该模型置为
is_primary=1+status=published; - 把身份写进
web-server/src/config/default-model.ts(名称 / 格式 / 产物 sha256 / 转换来源)。
GET /api/catalog/export的defaultModelId解析顺序:0. DEFAULT_MODEL_ID 环境变量(存在且已发布) 1. 源码种子 DEFAULT_MODEL_SEED(按产物 sha256 匹配,换库/重建库仍然有效) 2. 已发布且 is_primary 的 VRM 3. 任意已发布 VRM 4. id 最小的已发布模型实测:库中存在 2 个 VRM 模型时,
defaultModelId解析为 16(初音未来 Hatsune Miku / glb)。运维可用 CLI 完成同样的事(在 systemd 沙箱外,因此能写源码):
cd /opt/opendance/web-server node scripts/import-default-model.mjs 模型.zip --url http://127.0.0.1:3000 \ --name "初音未来 Hatsune Miku" --author "..." --license "..." \ --source-name "..." --source-url "https://..." # 或只补写源码:node scripts/import-default-model.mjs --write-source-only --model-id 162. 首次启动的 .env 引导
bash deploy/install.sh # 缺 deploy/.env 时自动进入引导 bash deploy/init-env.sh # 也可以单独跑 bash deploy/init-env.sh --check # 只校验不改动 bash deploy/init-env.sh --force # 已有 .env 也重新问一遍逐项询问
DB_HOST / DB_PORT / DB_USER / DB_PASS / DB_NAME / PORT / ADMIN_PORT / HOST,
回车保留默认;口令不回显、写盘不加引号;校验必填与端口范围(1..65535);
可用 Node + mysql2 做数据库连通性自检(口令只经环境变量传给子进程,失败仅告警);
只替换这 8 个键、保留模板注释,写完chmod 600并回读校验;结尾打印摘要(口令***)。非交互场景绝不挂住:
-y/--yes/--non-interactive、
OPENDANCE_NONINTERACTIVE=1、stdin 为/dev/null或关闭时只校验不提问;
所有读取都带超时,EOF/超时立即退出。3. 默认监听 0.0.0.0
代码默认值、
deploy/.env.example、web-server/.env.example、install.sh与
init-env.sh五处一致为0.0.0.0,并有单元测试守护(含"两份模板都写着
HOST=0.0.0.0"的断言)。反代与后端同机时可改为127.0.0.1,
管理端还可用ADMIN_HOST单独限制来源。交付与验收
契约校验 ✓ rest-api 0.3.0 / data-models 0.3.0 / model-formats 0.3.0 / database 0.2.0;路由 29 条一致 类型检查 ✓ tsc --noEmit 通过(strict) 单元测试 ✓ 125 / 125 通过(7 个文件) 生产构建 ✓ nest build + vite build 通过 压缩包转换 ✓ 真实 MMD 包 → HTTP 201 / 0.89s / format=glb / isPrimary=true / status=published 默认模型 ✓ 源码种子生效,defaultModelId 从 VRM 回落到 #16 GLB 可用性 ✓ 下载 1,152,464 字节;1 mesh / 240 nodes / 1 skin(238 joints) / 2 张内嵌贴图 .env 引导 ✓ 交互与非交互两条路径均实测;管道交互可写值且保留注释 打包完整性 ✓ 产物含 ZIP 读取器、转换模块、迁移、引导脚本与 CLI部署更新步骤
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.1.0/opendance-web-v0.1.0.tar.gz tar -xzf opendance-web-v0.1.0.tar.gz && cd opendance-web-v0.1.0 cp /opt/opendance/deploy/.env deploy/.env grep -q '^ADMIN_PORT=' deploy/.env || echo 'ADMIN_PORT=3001' >> deploy/.env grep -q '^HOST=' deploy/.env || echo 'HOST=0.0.0.0' >> deploy/.env bash deploy/install.sh # 会执行新的迁移 AddModelConversionFields前置:转换功能需要服务器能调用 Assimp(
ASSIMP_BIN,默认assimp)。
未安装时压缩包上传会返回 422 并提示安装;apt install assimp-utils libassimp6即可。附:模型格式在本系统中的实际能力(实测)
原生可加载的只有 glTF 系:Windows 客户端(Godot
GLTFDocument)与管理端预览
(three.jsGLTFLoader+VRMLoaderPlugin)都只认 GLB / glTF / VRM;
FBX 与 PMX 两个客户端都不能直接加载(管理端对 fbx 明确给"无法解析"提示)。转换方向 结果(Assimp 6.0.4 实测) PMX → GLB ✅ 可用(本项目通路,贴图会内嵌) PMX → glTF ✅ 可用 PMX → FBX ❌ 失败(退出码 11,产物回读报 FBX-DOM no Objects dictionary found)VRM → GLB ✅ 可用(17 MB→17 MB,822 ms) VRM → FBX ✅ 可用(33 MB,1030 ms) FBX → GLB ✅ 可用(21 MB,1847 ms) GLB → FBX ⚠️ 视模型而定;本项目来源(PMX)的 GLB 必失败 转换不是无损的(实测):
- PMX → GLB 丢失:物理刚体/关节、IK 链、付与/D 骨、卡通(toon)与球面(sphere)贴图、
轮廓线参数、顶点色、多套 UV;骨骼 714 → 蒙皮用 238;面部表情(morph) 0 个
(源 PMX 里まばたき/ウィンク/にこり等表情确实存在);材质 2 → 3。 - VRM → GLB 丢失:
extensionsUsed里的VRM消失 → humanoid 骨骼语义、
expression 绑定、spring bone、meta(许可/作者)、first-person 设置全部丢失;
92 个 blend shape 作为 glTF morph target 保留,但 VRM 语义名丢失。
→ 系统不再把它识别为 VRM(vrmVersion变 null,VRM 优先规则不再适用)。 - GLB → VRM 做不到:需要 VRM 专用工具写入 humanoid/expression/springBone/meta,
Assimp 不会写 VRM 扩展。本系统没有这个方向的实现。 - FBX → GLB:几何/蒙皮/形态目标保留;若 FBX 引用外部贴图,单文件上传时贴图缺失
(实测 #17 转换后 images=0);材质被近似拆分(1 → 7)。 - 任何目标格式都无法承载 PMX 的物理与卡通渲染参数。
更关键的是骨骼命名:模型能被驱动的前提是骨骼名能映射到 humanoid 规范
(客户端humanoid_map.gd的候选名以英文/VRoid/3ds Max Biped 为主)。实测覆盖率:模型 humanoid 覆盖 能否驱动(16 根必需骨) PMX→GLB(Hatsune Miku,日文 MMD 骨名) 0 / 22 ❌ 不能 VRM #18(中文骨名) 7 / 22 ❌ 不能 FBX #17 → GLB(Bip001 命名) 17 / 22 ⚠️ 只缺左右小腿 结论:格式对了不等于能用,还要有 humanoid 骨骼命名(或补一张映射表)。
已知设计取舍
- 默认模型种子以上传压缩包的 sha256 为匹配键:实测 Assimp 的 GLB 导出不是字节级
确定的(同一份 zip 连续转换三次得到三个不同 sha256,大小一致、BIN chunk 有十几字节浮动),
用产物 sha256 当键会失效。产物 sha256 仍写入fileSha256字段但仅作记录。
已知事项
- 模型压缩包里的授权信息:本仓库内置的默认模型(初音未来 Hatsune Miku,
Boku Wa Luka)
压缩包内没有许可文件,且疑似游戏提取资源。已按"未标注、对外使用前请确认授权"
记入license字段;对外发布前请核实并更新来源五元组。 - 未内置管理端登录认证,请在反代层加 Basic Auth / IP 白名单,或只在内网暴露管理端端口。
- Assimp 的 FBX 导出器对复杂蒙皮模型不可用(本项目已自动回退 GLB);
若必须得到 FBX,需要 Blender + mmd_tools 之类的专用转换器,当前未纳入。
Downloads
- 模型上传支持压缩包自动转换:上传 MMD 模型包