在工程三维查看器里,“点击到一个构件”只是交互的起点。真正难的是让同一个构件在 GPU pick、结构树、属性面板、隐藏/显示和协同同步之间保持稳定身份。
很多实现会直接把 GPU buffer 的地址或某个临时索引当作构件 ID。模型切换、LOD 重建、资源回收或 Worker 重新上传之后,这些值都可能变化。结果是:画面里选中了 A,属性面板显示 B;结构树刷新后高亮丢失;同一个构件在不同客户端的选择结果也无法对齐。
我更倾向于把身份拆成三层:
- 业务身份:来自模型数据的稳定 nodeId,用于结构树、属性和跨端同步。
- 渲染身份:当前 renderCache 中的批次、实例和几何引用,只服务于本次绘制和 pick。
- GPU 引用:buffer、texture 或 bind group 的运行时句柄,只能在当前设备和当前资源代际内使用。
GPU pick 返回结果后,渲染层先通过渲染身份查到业务 nodeId,再把 nodeId 交给上层。资源重建时只替换渲染身份和 GPU 引用,不修改业务身份。这样,LOD 切换、模型重新加载和显存回收就不会破坏结构树与属性面板的语义。
还要特别处理两个边界:
- nodeId 为 0 可能是合法的根节点或第一个节点,不能用 if (nodeId) 判断是否命中。
- GPU 指针为 0 通常表示无效引用,不能把“空指针”和合法业务 ID 混成一个判断。
选择状态最好带上资源 generation。渲染引擎每次完成模型切换或缓存重建就递增 generation;异步 pick、Worker 回写和协同事件到达时,先检查 generation 是否仍然匹配。过期结果直接丢弃,避免旧模型的异步任务覆盖新模型的选择。
一个可验收的流程是:
- 点击构件后,结构树和属性面板都显示同一个 nodeId;
- 隐藏、显示和测量只接受业务 nodeId,不接受 GPU 句柄;
- LOD 切换后再次 pick,身份保持一致;
- 快速切换两个模型时,旧模型的异步结果不会改变新模型;
- 协同同步只传递业务选择,不传递本机 GPU 引用。
这套分层对 BIM、机械装配和工厂模型都适用。它不会替代具体格式的解析工作,但能把“模型数据身份”和“本机渲染资源生命周期”隔离开,减少大型工程模型审阅中最难复现的一类错选问题。
ModelView 是这类工程模型渲染与协同底座的一个实际实现方向,支持结构树、属性、选择、隐藏、测量、剖切和协同审阅。
可以直接下载,用自己的真实模型测试: gitee.com/onedraw/mod…
我是项目开发者,欢迎用具体模型和部署环境反馈问题。