# forge-three **Repository Path**: feng62/forge-three ## Basic Information - **Project Name**: forge-three - **Description**: threejs编辑器 无头话设计,核心功能呢基于原生ts, - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-09 - **Last Updated**: 2026-04-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 🚀 工业级 Web 3D 核心引擎架构设计白皮书 (终极全能完整版) > **架构定位**:基于微内核、无头架构(Headless)与命令模式,打造跨平台、高扩展、支持撤销/重做与复杂交互的工业级 Web 3D 编辑器底座。 --- ## 💡 一、 核心设计思想 (Design Philosophy) 本架构致力于解决复杂三维场景(如数字孪生、BIM/CAD 预览、在线 3D 编辑)中状态同步混乱、高度耦合 UI 框架、模块难以复用的痛点,核心坚持以下四大原则: 1. **唯一事实来源 (Single Source of Truth)**:彻底摒弃在各个 UI 组件中零散维护 3D 状态。建立统一的 Vanilla JS 状态机,所有功能模块共享同一份场景数据。UI 层不直接操作 Three.js,而是将指令发送给功能层,功能层处理后更新状态,UI 再通过监听状态变化进行视图更新。 2. **Headless (无头) 与框架无关**:核心引擎完全与 React/Vue 等视图层剥离。UI 层仅作为“视觉皮肤”,确保底层引擎极高的跨平台能力。 3. **微内核与插件化 (Microkernel & Plugins)**:系统分为“宿主(底座)”与“插件(集装箱)”。业务功能皆为独立插件,支持按需加载与无痕卸载。 4. **命令模式与集中管控 (Command Pattern & Centralization)**:对于所有具有破坏性的场景编辑操作(位移、显隐等),必须通过封装的 Command 执行,实现无缝的 Undo/Redo。交互输入(键鼠)、相机、控制器、环境光影由内核统一接管,杜绝模块间的冲突。 --- ## 📂 二、 引擎文件结构 (File Structure) 采用典型的 Monorepo 思想,将核心、管理器、功能插件严格物理隔离。核心包内绝对不允许出现视图层文件。 ```text packages/core-engine/src/ ├── index.ts # 🌟 SDK 统一暴露入口 (Facade API & Proxy) ├── core/ # 🧠 【基础内核】不可拔插的物理底座 │ ├── engine/ │ │ ├── ThreeContext.ts # 封装 WebGLRenderer, Scene, Camera │ │ └── GlobalTicker.ts # 全局渲染循环与动画驱动中心 │ ├── managers/ # 🌟 【底层管理器】引擎的核心器官 │ │ ├── ObjectManager.ts # 📦 场景实体生命周期与 O(1) 极速检索大管家 │ │ ├── ResourceManager.ts # ♻️ 显存调度与垃圾回收大管家 │ │ ├── AssetManager.ts # 📥 模型解析、纹理加载与进度管控大管家 │ │ ├── CameraManager.ts # 📷 相机大管家 (透视/正交切换、相机阵列) │ │ ├── ControlsManager.ts # 🎮 漫游控制器自动重绑管家 (Orbit/Map/FirstPerson) │ │ ├── InputManager.ts # ⌨️ 全局键盘与快捷键注册中心 │ │ ├── SelectionManager.ts # 🖱️ 射线拾取与单/多选逻辑中心 │ │ ├── TransformManager.ts # 🔀 包装 TransformControls 操作器 │ │ ├── HistoryManager.ts # ⏪ 历史记录栈 (维护 Undo/Redo) │ │ ├── EnvironmentManager.ts# 🌤️ 光影、天空盒、阴影与环境预设管家 │ │ ├── PostProcessManager.ts# 🎇 后期特效、高亮描边与泛光管家 │ │ └── GizmoManager.ts # 📐 网格、轴辅助等非业务实体管家 │ ├── EventBus.ts # 强类型底层事件派发器 │ └── PluginManager.ts # 插件安装与卸载的调度中心 ├── store/ # 🗃️ 【框架无关的状态中心】 │ ├── StateManager.ts # 纯 TS 发布/订阅状态机 │ ├── ViewState.ts # 当前相机/控制器模式状态 │ └── EditorState.ts # 当前选中的模型节点状态 ├── commands/ # 📝 【命令模式指令集】 │ ├── BaseCommand.ts # 命令抽象基类 (execute / undo) │ └── TransformCommand.ts # 记录模型变换前后的矩阵状态 ├── features/ # ⚙️ 【纯逻辑插件层】业务微模块 │ └── BaseFeature.ts # 插件标准接口定义 └── shared/ # 🛠️ 【共享层】 ├── types/ # 全局类型契约 └── abstract/ └── BaseAnimationController.ts # 🌟 全局动画多态基石契约 ``` --- ## ⚙️ 三、 核心模块详解与联动协作网络 系统通过解耦各个模块的职责,并利用状态(Store)和事件(EventBus)将它们无缝串联。 ### 1. 实体基建与变换协同 (ObjectManager 🤝 TransformManager) * **模块作用**: * `ObjectManager`:整个引擎的“实体户籍科”。负责给进入场景的业务模型打上唯一的 `editorId`,并在内部维护一个 `Map` 的 O(1) 极速检索字典。 * `TransformManager / SelectionBox`:负责实例化和管理 `THREE.TransformControls`,提供位移、旋转、缩放操作。 * **联动机制**:当用户在 UI 层触发选中操作时,状态中心 (`EditorState`) 记录目标 ID 数组 (`selectedIds`)。`TransformManager` 监听到状态改变,绝对不需要去遍历庞大的场景树,而是直接向 `ObjectManager` 发起 `getModelById` 查询,瞬间将控制轴绑定到目标或包裹临时 `Group` 上。这彻底消灭了选中与拖拽时的运算卡顿。 ### 2. 交互拾取与撤销恢复系统 (InputManager 🤝 SelectionManager 🤝 HistoryManager) * **模块作用**: * `InputManager`:拦截全局按键,维护如 `isShiftDown` 等修饰键状态,暴露快捷键注册接口。 * `SelectionManager`:封装射线拾取 (Raycaster) 逻辑,负责将屏幕 2D 坐标转换为 3D 碰撞检测。 * `HistoryManager`:维护 Undo/Redo 的历史栈,通过 Command 模式记录轻量级的操作差量。 * **联动机制 (以 Shift 多选并撤销为例)**: 1. 用户按下 Shift 键,`InputManager` 标记状态为 true。 2. 用户鼠标点击场景,`SelectionManager` 触发射线,命中模型后,向 `InputManager` 查询是否按下了 Shift,从而决定是覆盖选中还是追加多选。 3. 修改完成后,用户按下 `Ctrl+Z`,`InputManager` 捕获快捷键并调用 `HistoryManager.undo()`。`HistoryManager` 基于 `BaseCommand` 契约,弹出最后一步极其轻量的 `TransformCommand` 执行复原逻辑,实现瞬间回退。 ### 3. 视口与控制器自动重绑 (CameraManager 🤝 ControlsManager) * **模块作用**: * `CameraManager`:维护场景中的相机对象,提供正交/透视平滑切换机制与相机阵列管理。 * `ControlsManager`:管理场景的视角漫游,如 Orbit、Map 或 FirstPerson 漫游操作。 * **联动机制**:`OrbitControls` 是强依赖具体相机的。当 `CameraManager` 发生相机切换时(如透视切正交),会自动触发一个全局相机变更事件。`ControlsManager` 在内部拦截该事件,自动将控制器解除与旧相机的绑定,并瞬间重绑到新相机上,上层业务零感知。 ### 4. 资产管线与内存闭环 (AssetManager 🤝 ResourceManager) * **模块作用**: * `AssetManager`:统筹模型下载、Draco 解压与加载进度反馈。 * `ResourceManager`:统一的资源池,显存调度与垃圾回收大管家。 * **联动机制**:模型被 `ObjectManager` 从场景彻底移除后,抛出移除事件。`ResourceManager` 接收事件并基于引用计数 (Reference Counting) 决定是否执行清理。一旦材质和几何体的引用归零,立刻强制调用底层的 `.dispose()`,彻底杜绝长时间挂机导致的 WebGL 显存溢出奔溃。 ### 5. 视觉工程协同 (PostProcessManager 🤝 SelectionManager 🤝 GizmoManager) * **后期特效 (`PostProcessManager`)**:管理渲染通道(如 Bloom, SSAO)。当 `SelectionManager` 触发选中时,自动将目标同步给后期管线实现描边高亮。 * **辅助视觉 (`GizmoManager`)**:管理非业务对象。在射线拾取与序列化导出时,系统会自动过滤带 `isGizmo` 标签的对象,确保数据纯净。 ### 6. 数据闭环与序列化 (Features 🤝 Store 🤝 Serializer) * **联动机制**:UI 层发起“导入/导出 JSON”的指令给序列化模块。功能模块(如动画编辑器、场景模型显隐)处理完业务逻辑后,将各自产生的数据推送到“序列化数据存储”中心。最终,所有孤立的功能状态在这里拼装成一个完整的工程文件 (Project File) 结构,统一与服务端或本地进行存取互通。 ### 7. 多态全局动画契约与驱动 (GlobalTicker 🤝 BaseAnimationController) * **联动机制**:`shared/abstract/` 提供了包含播放、暂停、进度、循环等标准协议的 `BaseAnimationController` 基石契约。各个具体的动画功能插件(如水流材质动画、机械旋转动画)只需实现该接口,并注册给核心层的 `GlobalTicker`。核心渲染循环 (`GlobalTicker`) 无需关心具体的业务动画逻辑,统一在帧循环内部调用 `anim.update(delta)` 实现无差别的多态驱动。 --- ## 🛡️ 四、 工业级护城河设计 (Industrial-Grade Safeguards) 为确保引擎能在极为严酷的场景(如南水北调泵站、海量建筑 BIM 展示等)稳定运行,核心层植入了强大的护城河: 1. **多线程任务调度总线 (Web Worker Bus)**:将庞大 CAD / DXF 图纸解析、巨型 JSON 场景结构反序列化等高耗能、CPU 密集型任务,全部剥离丢入 Web Worker 处理,返回处理好的底层 Float32Array 数据给主线程,彻底杜绝浏览器前台 UI 卡死假死。 2. **空间索引加速 (SelectionManager + BVH Raycasting)**:针对数百万三角面的超大模型,在 Worker 线程中预构建 BVH (Bounding Volume Hierarchy) 空间索引树。将原本极其耗时的射线遍历复杂度从 O(n) 直接降维至 O(log n),实现纳秒级的点击、框选瞬间响应。 3. **大坐标精度劫持 (RTC & GIS 融合)**:当引擎需要加载宏观的地理坐标(如 ArcGIS 投影系),原生 Three.js 的 `Float32` 会导致极其严重的大坐标精度丢失(模型剧烈抖动)。架构底层提供坐标转换适配器,采用 RTC (Relative To Center) 或双精度模拟渲染接口,完美抹平 GIS 系统与三维引擎的坐标差异。 --- ## 🎩 五、 插件设计与 DX 极致优化 (Plugin & DX) 1. **垃圾袋模式 (Disposable Pattern)**:针对功能插件(如可见性控制、视角漫游控制器插件),必须提供 `install(ctx)` 和 `uninstall()` 钩子。在安装时,将所有的事件监听器、辅助对象、闭包引用推入一个垃圾袋数组。卸载时无脑循环执行销毁,实现插拔过程的零内存泄漏。 2. **声明合并黑魔法 (Declaration Merging)**:为彻底解耦,核心引擎 `index.ts` 通过 ES6 Proxy 将暴露出去的 API 命名空间拦截并代理到真实的插件实例上。配合 TypeScript 的接口合并,让外部 Vue/React 开发者无需泛型转换,直接获得完美的代码智能补全和提示: ```typescript // UI 开发者直接调用,IDE 自动补全命名空间和方法! engine.api.visibility.toggleNode('pump-01', false); engine.api.env.setPreset('daylight'); ```