Appearance
Viewer 入口
设计意图
Viewer 是 .fmbv 渲染引擎的唯一公开入口:它持有 WebGL 渲染管线与场景,把能力按域拆给 19 个只读 manager 属性——入口本体只剩生命周期(create / dispose)、事件订阅(on / off / once)与两个逃生口(resize / invalidate)。这种「单一入口 + 域 manager」的形态让每个域的 API 各自内聚,也让公开面不暴露任何底层渲染实现的类型。
构造入口只有一个:Viewer.create(canvas, options?)(构造函数私有,不可 new)。它在给定 canvas 上建立渲染管线并开启渲染循环;若指定了 options.environment(HDRI),会等环境加载完成后才 resolve。事件订阅采用退订函数模式:on() / once() 返回取消订阅函数,组件卸载时调用即可,不必保存 handler 引用去 off。
何时用它:集成的第一步(创建实例 / 传构造选项)与最后一步(dispose 释放 GPU 资源),以及跨域的事件总线接入;具体能力操作都在各 manager,见下表。
manager 一览
| 属性 | 职责 |
|---|---|
viewer.model | 模型加载 / 卸载、结构树查询与节点级外观控制 |
viewer.camera | 相机位姿、标准视图、zoom-to-fit 与交互控制 |
viewer.selection | 点击 / 框选 / 程序化选中与 hover 高亮 |
viewer.edges | 全局边线显示开关、颜色与不透明度 |
viewer.renderMode | 全局与按节点的渲染模式(实色线框 / 线框 / 实色 / 消隐) |
viewer.lights | 场景光源的增删与强度调整 |
viewer.stats | 加载与渲染统计快照(fps / 帧时 / chunk 进度等) |
viewer.navcube | 导航立方体的开关 / 停靠 / 尺寸 |
viewer.background | 画布背景(纯色 / 渐变 / 图片 / 透明) |
viewer.export | 矢量 SVG 工程图导出(消隐线条) |
viewer.explode | 零件爆炸(散开 / 锁定 / 动画) |
viewer.section | 剖切 section 与盖面 / gizmo 交互 |
viewer.quality | 渲染质量(抗锯齿 / AO / 色调映射 / 描边 / 质量档) |
viewer.views | 视图捕获 / 应用与视图集(.fmbview.json)导航 |
viewer.transform | 零部件位置编辑(平移 / 旋转 / gizmo) |
viewer.history | 统一编辑撤销栈(变换 + 外观 + 可见性 + 标签共栈) |
viewer.explodeEdit | 编辑态爆炸(写进变换通道,可撤销、可存盘) |
viewer.labels | 序号标签(编号 / 成员 / 气泡落点 / 引线锚点) |
viewer.diagnostics | LOD 等调试诊断——实验性,不建议外部集成使用 |
前 13 个是浏览能力,任何集成都会用到;transform / history / explodeEdit / labels 是编辑能力,只在需要修改并保存模型呈现的宿主(如编辑器)里用得上,见编辑能力。
构造选项(ViewerOptions)
全部可选,逐项一句话(精确类型见 ViewerOptions):
background— 初始背景:纯色或"transparent";不传用引擎默认浅灰垂直渐变。antialias— 硬件 MSAA,传给 WebGLRenderer,构造后不可变(默认true);运行时抗锯齿是另一回事,见viewer.quality。pixelRatio— 渲染像素比;默认取window.devicePixelRatio(非浏览器环境为 1)。workerPoolSize— 几何解码 worker 池大小;默认按 CPU 核数减一,钳制在 1–12。大模型(数千 chunk)下这是加载吞吐的主要杠杆。initialCamera— 初始相机位姿(位置 / 目标点);不传用引擎默认位姿。initialProjection— 初始投影方式(透视 / 正交);不传默认透视。会话内状态、不持久化,运行时用viewer.camera.setProjection切换。navCube— 导航立方体初始配置:enabled默认true、anchor默认"top-right"、size默认 185(CSS 像素)。debug— 调试开关;当前版本未接线,预留。quality— 渲染质量初值(抗锯齿 / AO / 色调映射 / 质量档),逐项默认值见QualityOptions。lod— LOD 与小件代理初始化策略;常用mode: "auto" | "quality" | "performance" | "off",细项见下文。environment— HDRI 环境贴图 URL(.hdrequirect),用于 IBL;不传用内置程序化环境。batchedFaces— 面几何是否走批处理基座(按材质 × 空间区域分桶),把 draw call 从「可见件数量级」压到「桶数量级」。默认开,显式传false才退回逐 mesh 路径;正常集成无需理会。
LOD 初始化策略
lod 控制模型加载后的默认 LOD 选择与小件代理策略,适合在创建 viewer 时按业务场景定调:
ts
const viewer = await Viewer.create(canvas, {
lod: { mode: "performance", proxyBelowPx: 32 },
});mode: "auto"— 默认自适应策略,兼顾质量与交互流畅度。mode: "quality"— 更偏细节,降低 SSE 与小件代理阈值。mode: "performance"— 更偏流畅,允许更早使用粗 LOD / 小件代理。mode: "off"— 尽量使用原始 LOD0,并关闭小件代理与动态预算。
需要精调时可覆盖 screenSpaceErrorPx、cullBelowPx、proxyBelowPx、interactiveDegrade,以及两个与边线相关的旋钮:
prominentFootprintPx(默认 256)—— 屏幕足迹达到这个像素数的零件算「显眼件」,引擎为它画精确的烘焙边线,并把面几何档位锁到最精细的 LOD-0。调大 = 更少零件享受这个待遇(更快),调小 = 更多(更清晰)。edgeVisibility—— 远处密集边线的降噪策略。默认auto按屏幕足迹与线密度隐藏远处边线;always则始终按全局边线开关显示,不做距离降噪。
这些参数只决定初始化策略;模型来源仍通过 viewer.model.load(source) 传入。
典型用法
完整生命周期(创建 → 加载 → 取景 → 销毁):
ts
import { Viewer } from "@modelcubes/viewer-core";
const viewer = await Viewer.create(canvas);
await viewer.model.load("/models/assembly.fmbv"); // 骨架就绪即 resolve
viewer.camera.fitView();
// ……不再使用时(如组件卸载)
viewer.dispose();事件订阅与退订(on 返回退订函数,不必保存 handler):
ts
const unsub = viewer.on("selection-changed", (e) => panel.show(e.current));
viewer.once("model-loaded", () => spinner.hide()); // 只触发一次
// 组件卸载时
unsub();按宿主环境定制构造(透明背景嵌入页面 + 关内建导航立方):
ts
const viewer = await Viewer.create(canvas, {
background: "transparent",
navCube: { enabled: false }, // 改用 <fmb-view-cube> 组件时关掉内建
quality: { tier: "high" },
lod: { mode: "quality" },
});注意事项
Viewer.create是唯一构造入口,构造函数私有;指定options.environment时create会等 HDRI 加载完成后才 resolve,加载失败会 reject。antialias(MSAA)构造后不可变;运行时可开关的是 SMAA 后处理(viewer.quality.setAntialias),两者独立叠加。lod是初始化策略,不是加载入口;不要把模型 URL 放进构造参数,仍用viewer.model.load()加载模型。off需要与on时相同的 handler 引用——匿名箭头函数没法退订,优先用on/once返回的退订函数。- 画布尺寸变化引擎自动跟踪(ResizeObserver),宿主通常无需理会;
resize()是显式逃生口,供宿主在浏览器完成布局前就已知晓布局变化时立即同步渲染器与相机宽高比,已销毁时静默返回。 - 渲染是按需的(render-on-demand):场景静止时引擎不重绘。所有经公开 API 的状态变更(选中 / hover / 外观 / 剖切 / 显隐 / 变换 / 标签 / 视图 / 投影 / 结构编辑 / 相机)都已接线到失效检测,会自动触发下一帧。
invalidate()是兜底逃生口——只在绕过公开 API 直接改了影响画面的状态时才需要,已销毁时静默返回。 dispose()幂等(重复调用静默返回):停渲染循环、释放全部 GPU 资源与事件订阅;销毁后再调各 manager 的方法抛ViewerError(Disposed),on/once也抛,off静默返回。debug选项当前未接线,传了不生效。
完整签名与延伸
Viewer—create/on/off/once/resize/invalidate/dispose的精确签名。ViewerOptions/ViewerEvents— 构造选项与全事件 payload 类型。- 指南:加载与流式 — 创建后第一件事(加载模型)的事件时序。
- 概念:架构总览(单一入口与分层)、事件系统(事件清单与订阅模式)。