Appearance
ADR-110: IMmdModel 接口类型补全 — 上游 PR 计划
状态: 草案 · 待立项(作为「babylon-mmd 上游贡献登记册」总入口;条目 1 = IMmdModel 接口补全已立项,条目 2–11 = 跨 ADR / 研究候选 / 已否决 / 已延期,统一归集避免散落) 日期: 2026-07-14
决策者: Riku(联邦首席架构师 AI)、Jieling(人类侧首席架构师)
创建日期: 2026-07-14
来源: docs/research/babylon-mmd-api-analysis.md §3.1 接口缺口 / §五 P0
关联: ADR-064(IMmdModel 类型缺口即时止血,本地 module augmentation)、ADR-098(vmd-layers cast 消解,批次一)、ADR-187(babylon-mmd 剩余 API 调研)、ADR-188(PBR 材质构建器,草案)、ADR-056(WASM 运动层合成)、ADR-058(Shift-JIS 纹理解码)、ADR-029(WASM 物理调参)、ADR-083(碰撞摩擦/弹性)、ADR-024(SSS/ PBR 阻塞)、ADR-054(上游阻塞路线图)、ADR-016(gaze 手动计时)、ADR-085(脚部 IK 求解器暴露)
影响面: frontend/src/core/types.ts(本地 augmentation 待移除)、babylon-mmd 上游仓库(noname0310/babylon-mmd)
上游贡献候选登记册(跨 ADR 汇总)
本 ADR 同时作为「向
noname0310/babylon-mmd提 PR 的单一权威登记册」。各源 ADR 正文中的「提上游 PR / 推动上游 / fork」表述均归集于此,避免散落、口径不一。 导航入口见docs/upstream/README.md(上游贡献文档区统一安放点)。 决策语义:✅ 已立项= 采用提 PR;⏸ 延期/评估= 本地已替代,远期可重议;❌ 已否决= 本地方案更优或维护成本不可控;⛔ 上游阻塞= 依赖上游自身路线图,非本项目范畴。
| # | 来源 ADR | 建议的上游 PR / 贡献内容 | 决策 | 上游价值 | 落地难度 |
|---|---|---|---|---|---|
| 1 | ADR-110 | IMmdModel 接口补全 setRuntimeAnimation / createRuntimeAnimation / currentAnimation | ✅ 已立项(本文详述) | 高(消 3 处 cast,社区通用) | 低 |
| 2 | ADR-056 | MmdWasmRuntime 原生支持 MmdCompositeAnimation(合成下沉 WASM) | ⏸ 留作远期(Option A 否决,本地 JS 帧流合成) | 中(去运行时开销) | 中 |
| 3 | ADR-058 | 外层 zip/磁盘文件名 Shift-JIS/GBK 兜底(corruptIndex 修正 U+FFFD 乱码) | ❌ 已否决(本地「损坏映射」兜底,免维护 fork) | 中(根源修) | 中(需 fork) |
| 4 | ADR-029 | 暴露 WASM 物理 stiffness / damping / friction 运行时 API | ❌ 不追(本地仅用 0/1 刚体开关,已覆盖 100% 可用 API) | 中 | 高(WASM 内存 hack) |
| 5 | ADR-083 | 暴露碰撞体 friction / restitution 运行时 API | ❌ 封存(等上游或自行 hack WASM 内存) | 低 | 高 |
| 6 | ADR-024 | PBR 材质 + morph 目标支持(SSS 次表面散射前置) | ⛔ 上游阻塞(非本项目范畴,待上游突破) | 高(解锁 SSS/PBR) | 极高 |
| 7 | ADR-054 | 推动上游贡献(SSS / PBR 整体路线图) | ⏸ 评估中(只能等 / 推动上游) | 高 | 极高 |
| 8 | ADR-016 | 暴露 beforePhysics / afterPhysics 钩子供手动计时(gaze 优化项) | ⏸ 优化项(当前 WASM frontBuffer 直写 + JS linkedBone 双路径已落地) | 低 | 中 |
| 9 | ADR-085 | fork 暴露 ikSolver 字段及 solve()(脚部 IK 统一 JS/WASM 路径) | ⏸ 长期(方案 C WASM 手动 IK 已于 2026-07-26 落地,方案 A 降级) | 中 | 高(fork) |
| 10 | ADR-188 / ADR-187 | PBRMaterialBuilder 落地(morph 兼容 PBR,根治 ADR-024 阻塞) | 📋 草案(P1 长期规划,独立专项 ADR-188) | 高(材质升级) | 大 |
| 11 | 研究 wind-affect-wasm-physics.md | fork babylon-mmd 在 MmdWasmPhysics 加 setWind(windForce: Vector3)(WASM Bullet 刚体受风) | ❌ 不追(本地方案 A+C:runtime 反射 + onBeforeRenderObservable 已覆盖;fork/PR 仅作兜底,与条目 4/5 同桶) | 低(边际收益,仅 MMD 骨髁物理受风) | 高(fork + WASM 内存) |
登记册治理规则
- 唯一入口:任何 ADR 出现「向 babylon-mmd 提 PR」类建议,必须在此登记册追加一行并回链源 ADR,不得在源 ADR 内自成体系。
- 决策冻结点:条目 1 已采纳;条目 3/4/5/11 已否决/封存(本地替代方案已闭环,不再追溯);条目 2/8/9 为远期可重议;条目 6/7/10 受上游路线图或专项 ADR 约束。
- 验收后清理:条目 1 上游合并后,按本文「步骤四」移除
core/types.tsaugmentation;其余条目若未来启动 PR,从本登记册升级为独立实现计划。
问题
IMmdModel 接口(babylon-mmd/esm/Runtime/IMmdModel)缺少运行时方法/属性,导致项目在 3 个位置使用 as any / as unknown as 绕过类型检查(第 2 处已由 ADR-098 消解)。
类型缺口清单
| # | 位置 | 问题代码 | 根因 |
|---|---|---|---|
| 1 | core/types.ts:70-73 | RuntimeModel = IMmdModel & { setRuntimeAnimation(...); createRuntimeAnimation(...); } | IMmdModel 接口缺少 setRuntimeAnimation 和 createRuntimeAnimation 方法 |
| 2 | vmd-layers.ts:577 | composite as unknown as IMmdBindableModelAnimation | ✅ ADR-098 已消解(激活 MmdCompositeRuntimeModelAnimation module augmentation 后直接传入) |
| 3 | vmd-loader.ts:313 | (vmdLoader as unknown as { dispose?: () => void }).dispose?.() | VmdLoader 无实例状态需释放,该 cast 是空调用;已同步删除 |
| 4 | frontend/src/scene/motion/vmd-loader.ts:148-149 | (inst.mmdModel as { currentAnimation?: ... }).currentAnimation | IMmdModel 不含 currentAnimation 属性 |
当前止血方案
core/types.ts 通过 TypeScript module augmentation 本地声明合并:
typescript
// core/types.ts:106-110
export type RuntimeModel = IMmdModel & {
setRuntimeAnimation(handle: Nullable<MmdRuntimeAnimationHandle>, updateMorphTarget?: boolean): void;
createRuntimeAnimation(animation: IMmdBindableModelAnimation, retargetingMap?: { [key: string]: string }): MmdRuntimeAnimationHandle;
currentAnimation?: Nullable<IMmdRuntimeModelAnimation>;
};vmd-loader.ts 中一处 cast 仍存在(#4 currentAnimation),未纳入 augmentation。#3 经核实为无操作空调用,已在 vmd-loader.ts 中直接删除,不依赖上游 PR。
决策
向 noname0310/babylon-mmd 提交 PR,补全 IMmdModel 接口声明,验收合并后移除本地 augmentation。
选项
| 选项 | 结论 | 理由 |
|---|---|---|
| A. 提上游 PR | ✅ 采用 | 根治问题,消除 3 处 cast / augmentation,项目不再依赖本地 augmentation |
| B. 仅本地 augmentation 全覆盖 | ❌ 否决 | 临时止血已完成,长期应推动上游修复 |
| C. 不动(维持现状) | ❌ 否决 | 3 处 cast 在 babylon-mmd 升级时是静默断裂风险点 |
约束
PR 内容范围
| 修改目标 | 需要添加的声明 | 当前状态 |
|---|---|---|
IMmdModel 接口 | setRuntimeAnimation(handle: Nullable<MmdRuntimeAnimationHandle>, updateMorphTarget?: boolean): void | 缺失 |
IMmdModel 接口 | createRuntimeAnimation(animation: IMmdBindableModelAnimation, retargetingMap?: { [key: string]: string }): MmdRuntimeAnimationHandle | 缺失 |
IMmdModel 接口 | currentAnimation?: Nullable<RuntimeModelAnimation> | 缺失 |
不包含的范围
VmdLoader.dispose()——经核实 babylon-mmdVmdLoader的 JS 实现中不存在dispose()方法,vmd-loader.ts中对应的 cast 是空调用(调用了不存在的方法,静默忽略),已在项目内直接删除该 cast,不依赖上游 PR。IMmdRuntimeBone.worldMatrix类型——已在IMmdRuntimeBone中正确定义为Float32Array,proc-motion-bridge.ts中的as any是冗余 cast,属项目内部清理,不涉及上游。MmdCompositeAnimation的IMmdBindableModelAnimation兼容——已在mmdCompositeRuntimeModelAnimation.d.ts中通过 module augmentation 声明,无需重复提交。
⚠️ 架构注意事项
setRuntimeAnimation / createRuntimeAnimation / currentAnimation 目前仅存在于具体类 MmdModel 和 MmdWasmModel 上,IMmdModel 接口上未定义。PR 需与上游作者确认是否愿意将这些方法提升到 IMmdModel 通用接口中。
若上游认为这些是内部实现细节不愿暴露,则回退到本地 augmentation 全覆盖方案(见「回退方案」)。
验证方法
- 提交 PR 后,在项目
package.json中临时指向 PR 分支("babylon-mmd": "noname0310/babylon-mmd#pr-xxx") - 删除
core/types.ts中RuntimeModel的 intersection type,改为type RuntimeModel = IMmdModel - 删除
frontend/src/scene/motion/vmd-loader.ts中currentAnimation的 cast - 运行
npm run check(tsc --noEmit)确认零类型错误 - 运行
npm run build确认构建通过 - 运行
npm run test确认单元测试无回归
实现计划
步骤一:fork 并本地验证(预估 1 天)
bash
# fork noname0310/babylon-mmd 到个人仓库
# clone 到本地
git clone https://github.com/<user>/babylon-mmd.git
cd babylon-mmd
# 在 src/Runtime/IMmdModel.ts 中添加:
# setRuntimeAnimation(handle: Nullable<MmdRuntimeAnimationHandle>, updateMorphTarget?: boolean): void;
# createRuntimeAnimation(animation: IMmdBindableModelAnimation, retargetingMap?: { [key: string]: string }): MmdRuntimeAnimationHandle;
# currentAnimation?: Nullable<RuntimeModelAnimation>;
# 本地构建
npm run build步骤二:项目内临时指向验证(预估 0.5 天)
json
// package.json 临时指向
"babylon-mmd": "file:../babylon-mmd/packages/core"步骤三:提交 PR(预估 0.5 天)
- 提交到
noname0310/babylon-mmd主仓库 - PR 描述中说明:
- 缺失的 3 个声明项
- 项目侧因此产生的 3 处 cast / augmentation(附代码位置)
- 项目侧 module augmentation 的代码(证明最小修复集)
- 类型兼容性验证结果(
tsc --noEmit通过)
步骤四:上游合并后清理(预估 0.5 天)
- 更新
package.json版本号(或指向合并后的 commit) - 删除
core/types.ts中RuntimeModel的 intersection type 定义 - 清理
frontend/src/scene/motion/vmd-loader.ts中currentAnimation的 cast - 删除
(inst.mmdModel as { currentAnimation?: ... }).currentAnimation→ 改为inst.mmdModel.currentAnimation
后果
正面
- ✅ 消除 3 处
as any/as unknown as/ augmentation,类型安全与可维护性提升 - ✅ 移除本地 module augmentation,减少
core/types.ts样板代码 - ✅ 上游社区受益,其他 babylon-mmd 使用者不再遇到相同问题
- ✅ babylon-mmd 升级时不再需要核查 augmentation 是否冲突
负面
- ⚠️ 上游 PR 的合并时间不可控,可能需等待数个版本周期
- ⚠️ 若上游拒绝(如作者认为
setRuntimeAnimation是内部方法),需回退到本地 augmentation 全覆盖方案 - ⚠️ PR 提交后
core/types.ts的 augmentation 需保留直到上游版本发布,期间升级 babylon-mmd 需检查 augmentation 是否与新版冲突
回退方案
若上游 PR 长时间未合并(> 2 个版本周期),回退到本地 augmentation 全覆盖:
- 在
core/types.ts中补全currentAnimation声明 - 不在
vmd-loader.ts中保留as unknown ascast - 添加注释说明上游未合并,待日后清理
与 ADR-064 / ADR-098 的关系
| ADR | 处理的内容 | 与本 ADR 的关系 |
|---|---|---|
| ADR-064 | 本地 module augmentation 止血 RuntimeModel | 本 ADR 的上游 PR 合并后可移除 augmentation |
| ADR-098 | 消解 vmd-layers.ts 的 composite cast | 已消解 1 处,剩余 3 处由本 ADR 处理 |
| 本 ADR | 上游 PR 根治剩余 3 处 + 移除本地 augmentation | 是 ADR-064 的长期方案 + ADR-098 的延续 |