Appearance
骨骼覆盖核心 API
系统概览
骨骼覆盖核心 API(ADR-061/116/123/126/186)。提供骨骼覆盖的增删改查接口,是 UI 编辑和 动作模块的底层入口。与 bone-override-store 协作:本模块负责覆盖数据的管理和应用, bone-override-store 负责多模块的所有权仲裁。
核心职责
bone-override.ts— 骨骼覆盖数据管理、应用、序列化、帧钩子注册、IK 保护、运行时驱动
对外 API
type BoneOverrideEntry— 单条骨骼覆盖描述(欧拉角 + 权重 + 绝对模式 + 可选位置)computeOverride(boneName, euler, weight, modelId?)— 计算覆盖setBoneOverride(boneName, euler, weight, absolute?, modelId?)— 设置骨骼覆盖(欧拉角路径)setBoneOverrideQuat(boneName, quat, weight, absolute?, modelId?)— 设置骨骼覆盖(四元数路径,内部高效)setBoneOverridePosition(boneName, pos, modelId?)— 仅设置位置覆盖(保留动画旋转)applyBoneOverrideIK()— 应用覆盖到 IK 系统clearBoneOverride(boneName, modelId?)— 清除指定骨骼覆盖getOverride(boneName, modelId?)— 取指定骨骼覆盖getOverrideType(boneName, modelId?)— 取覆盖类型(rotation/position/both/none,覆盖着色诊断用)clearAllOverrides(modelId?)— 清除全部覆盖protectIkPosition(boneName)— 注册 IK 位置保护(覆盖传播时撤销对 IK 目标的偏移)registerBoneOverrideFrameHook(order, source, hook)— 注册帧钩子(按 order 升序执行,source 用于 UI 管线时序一览展示)getFrameHooksSnapshot()— 取所有帧钩子快照(UI 时序图展示用)getAllOverrides(modelId?)— 取所有覆盖(序列化用)restoreOverrides(entries, modelId?)— 恢复覆盖(反序列化用)startBoneOverride(modelId, getRuntimeBones, scene)— 启动帧驱动(绑定 onBeforeRenderObservable)stopBoneOverride()— 停止帧驱动 + 释放资源dumpBoneHierarchy(modelId?)— dump 骨骼层级(UI 调试 / 骨骼选择器用)
与其他子系统关系
- 所有权仲裁:
bone-override-store(ADR-084) - 被 motion-modules 子模块引用(body-posture/hand-modules/foot-modules/riding-model 等,6 个子模块注册帧钩子)
- UI 编辑:
menus/motion-override-levels.ts(renderOverrideCard / buildAdvancedBoneOverrideLevel) - 类型定义:
../core/types.ts(BoneOverrideEntry) - WASM/JS 双路径:通过
isWasmRuntime()分流,JS 路径跳过 IK 保护快照(与 ADR-186 时序图一致)
不变量
- 覆盖数据按模型 ID 隔离(
_overrideMaps: Map<modelId, Map<boneName, _OverrideSlot>>) - 应用覆盖时不改变骨骼原始值,使用
setOverride机制 - 帧钩子按
order升序执行(顺序由声明决定,与注册时序解耦,ADR-186 R2 修复) absolute=true仅对叶骨使用(中间层级骨骼会丢弃父骨传播,导致子骨视觉跳跃,审计 P2 风险)- IK 保护仅 WASM 路径生效(JS 路径跳过
_snapshotProtectedPositions/_restoreProtectedPositions,ADR-186 时序图步骤③⑤一致)
帧内时序(ADR-186)
bone-override stage 内部按以下顺序执行:
_runFrameHooks()— 帧钩子按 order 升序写入 overrideMap(foot-modules:0 → body-posture:5 → riding:10 → sway:20 → hand-symmetry:30)- 构建 boneMap + IK 保护快照(
_snapshotProtectedPositions) - 覆盖循环 — 逐骨
_applyWasmOverride,每骨处理后立即_propagateChildrenWasm传播 - IK 保护恢复(
_restoreProtectedPositions)— 撤销传播对 IK 目标的偏移,保留自身 slot 覆盖 - feet-adjustment 层(order=5,独立层)读取修复后的 IK 目标位置
⚠️ 传播是「每骨立即」而非「批量」。新增帧钩子若写入会被传播到 IK 目标的骨骼,需调用
protectIkPosition()注册保护。