架构总览
Engine 3.0 的目标不是包住 Three.js,而是在其原生对象之上增加三个领域能力:ECS binding、渲染计划与鼠标事件。
唯一事实来源
| 领域 | 唯一事实 | 业务入口 |
|---|---|---|
| 设备 | Engine 创建的 Canvas 与原生 WebGLRenderer | engine.canvas、engine.renderer |
| 场景层级 | Object3D scene graph | Entity 层级方法 |
| ECS 身份 | Entity/Object3D 一对一 binding | createEntity、getEntity |
| World membership | Engine-owned Scene ancestry | entity.world、world.createQuery |
| 表现 | RenderView 的 World/Camera/viewport/order | engine.pipeline.addView |
| 渲染扩展 | Pass Definition 的 stage/order/enabled | createPass、engine.pipeline |
| 交互 | 当前 View snapshot + Raycaster 命中 | entity.onPointer、engine.pointer |
| 跨模块消息 | EngineEvent token identity | createEvent、onEvent、emitEvent |
| Undo/Redo | 独立 History 双栈 | @tripo3d/engine/history |
Engine 不维护第二棵 Entity 树,也不提供 Camera、Scene、Renderer、Pass wrapper identity。
组合关系
Engine唯一组合根与 mutation authorityCanvas deviceCanvas、WebGLRenderer、ResizeObserver、DPR 与 RAF/Three TimerECSWorld、Entity/Object3D binding、Component presence、System 与 Script ownersPipelineRenderView、两阶段 Pass Definition、固定 main/OutputPass 与私有 EffectComposerPointer原生鼠标输入、topmost View、Raycaster 与 Entity routeEventsopaque token 驱动的同步 Engine-local 事件Plugins仅构造期静态安装的 setup/dispose 扩展
History 不属于 Engine core。应用可创建 standalone History,并用 createHistoryPlugin(history) 在 Engine dispose 时自动清栈。Vue 也只存在于 @tripo3d/engine/vue 子入口。
Entity 与 Object3D
engine.createEntity(root) 对一个 parentless raw Object3D 子树执行全量 binding。每个 Object3D 恰有一个同 Engine Entity;以后使用 engine.getEntity(object) 反查。
接纳后的层级 mutation 必须走 Entity facade:
parent.add(child):采用 Three.jsadd的世界变换语义;parent.attach(child):采用 Three.jsattach,尽量保持世界变换;parent.remove(child)、child.removeFromParent()、parent.clear()。
Object3D scene graph 仍是层级事实,但 Engine 需要在同一 mutation 边界同步 World membership、Query、Script 与 Pointer 派生状态,因此业务不能绕过 Entity 直接改 scene graph。
帧顺序
每帧入口只捕获一次有效 RenderView snapshot,System、Script、Pipeline 与 Pointer 共用该 identity:
frame entry捕获 time、size 与 ViewSnapshotPointer flush消费离散 FIFO 与最新 pointermoveSystem update按 Definition order 与注册顺序Script update按当前 World enrollmentScript lateUpdateEntity-local late phaseSystem lateUpdate场景级 late phasePipeline prepare + render复用同一 views/size/timeframe complete结束同步帧并调度下一次 RAF
Engine 使用浏览器 requestAnimationFrame 和 Three.js Timer。首帧、resume 后首帧以及可见性中断后的首帧 delta = 0。
正向 enrollment 在 active frame 中延后到下一帧,负向 enabled/remove/destroy 事实立即阻止后续回调。World 和 Pipeline topology mutation 在 active frame 中直接拒绝,避免同一帧出现两套事实。
生命周期与所有权
- Engine 创建:Canvas、renderer、默认 World/View/Camera、内部 Composer 与默认 Pass;
- Engine 释放:上述对象、所有 Entity 子树及其标准资源;
- 业务创建:额外 Camera、借入的原生 Three Pass、custom RenderTarget 与非标准资源;
- 业务释放:自己的 Camera 附属资源、原生 Pass、uniform/userData/custom material 等 Engine 不知道的边。
dispose() 先发布终态,再对所有 owner 做 best-effort 清理;单错原样抛出,多错按发生顺序形成 AggregateError,renderer 固定最后尝试释放。普通帧回调仍 fail-fast,不引入 recovery 或 retry 状态机。
如何选择扩展点
| 需求 | 选择 |
|---|---|
| Entity-local 状态建立与资源 cleanup | Component setup Definition |
| 单个 Entity 的局部行为 | Script |
| 跨实体或场景级能力 | System |
| 渲染链贡献 | Pass Definition + 原生 Three Pass |
| World/Camera/viewport 的表现绑定 | RenderView |
| 同步解耦消息 | EngineEvent |
| 构造期横切安装与最终清理 | Plugin |
| Undo/Redo | standalone History |
| Vue DOM 生命周期 | Canvas |