You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用VertexBufferReader访问特定部件几何(Forge Viewer)

解决Forge Viewer读取部件几何与多边形相交检测的问题

嘿,我看你在尝试用VertexBufferReader读取特定dbId部件的几何时遇到了麻烦,而且手里的代码已经是2.5年前的了,想找更完善的方案对吧?咱们先拆解下旧代码里可能踩的坑,再聊聊当前更可靠的做法。

旧代码的潜在问题

你的旧实现里有几个容易出问题的点:

  • 一个dbId可能对应多个fragId(复杂部件会被拆分成多个渲染片段),旧代码只取了第一个,会漏掉部分几何
  • VertexBufferReader属于Viewer的Private API,Autodesk不保证长期兼容,2.5年过去Viewer版本迭代,部分方法逻辑可能已经变化
  • 回调里拿到的坐标是模型本地空间坐标,如果你的自定义多边形是屏幕空间或者世界空间的,还需要做坐标转换,否则相交判断会出错

当前更可靠的几何读取方案

现在Forge Viewer有更稳定的方式获取部件几何,推荐结合官方API和一些工具类来实现,同时处理好多fragId的情况:

改进后的几何读取代码

/**
 * 读取指定dbId部件的所有世界空间几何点
 * @param {Autodesk.Viewing.Viewer} viewer
 * @param {number} dbId
 * @returns {Promise<Array<{x: number, y: number, z: number}>>}
 */
async function getDbIdWorldPoints(viewer, dbId) {
    const model = viewer.model;
    const fragList = model.getFragmentList();
    // 注意:一个dbId可能对应多个fragId,必须遍历所有
    let fragIds = fragList.fragments.dbId2fragId[dbId];
    if (!Array.isArray(fragIds)) {
        fragIds = fragIds ? [fragIds] : [];
    }

    const allWorldPoints = [];

    for (const fragId of fragIds) {
        const fragProxy = viewer.impl.getFragmentProxy(model, fragId);
        await fragProxy.getAnimTransform(); // 获取片段的世界变换矩阵

        const geometry = fragList.getGeometry(fragId);
        if (!geometry) continue;

        // 使用VertexBufferReader读取几何
        const reader = new Autodesk.Viewing.Private.VertexBufferReader(geometry);
        const geomCallback = {
            // 处理线段
            onLineSegment: (x0, y0, x1, y1) => {
                const pt0 = new THREE.Vector3(x0, y0, 0).applyMatrix4(fragProxy.matrixWorld);
                const pt1 = new THREE.Vector3(x1, y1, 0).applyMatrix4(fragProxy.matrixWorld);
                allWorldPoints.push({x: pt0.x, y: pt0.y, z: pt0.z});
                allWorldPoints.push({x: pt1.x, y: pt1.y, z: pt1.z});
            },
            // 处理三角形顶点
            onTriangleVertex: (x, y) => {
                const pt = new THREE.Vector3(x, y, 0).applyMatrix4(fragProxy.matrixWorld);
                allWorldPoints.push({x: pt.x, y: pt.y, z: pt.z});
            },
            // 处理圆弧(可选,根据需求采样点)
            onCircularArc: (centerX, centerY, startAngle, endAngle, radius) => {
                const step = (endAngle - startAngle) / 10; // 分10段采样
                for (let angle = startAngle; angle <= endAngle; angle += step) {
                    const x = centerX + radius * Math.cos(angle);
                    const y = centerY + radius * Math.sin(angle);
                    const pt = new THREE.Vector3(x, y, 0).applyMatrix4(fragProxy.matrixWorld);
                    allWorldPoints.push({x: pt.x, y: pt.y, z: pt.z});
                }
            }
        };

        reader.enumGeomsForObject(dbId, geomCallback);
    }

    return allWorldPoints;
}

多边形相交检测的优化

拿到世界空间的点之后,你可以用THREE.js的工具类来简化相交判断:

  1. 把自定义多边形转换成THREE.Shape对象
  2. 遍历部件的点,用shape.containsPoint()判断点是否在多边形内(如果只要判断是否有交集,只要有一个点在里面即可)
  3. 或者用ConvexHull生成部件的凸包,再和多边形做相交检测,这样性能会更好,尤其是复杂部件

更高效的替代方案:用Viewer内置选择API

如果你的核心需求只是检测部件是否和自定义多边形相交,不需要精确的几何点,其实可以直接用Viewer的内置选择功能:

  • 把自定义多边形的屏幕坐标转换成Viewer的拾取区域
  • 调用viewer.impl.selectionHelper.selectFromArea()来获取相交的部件dbId,这种方式不用手动处理几何,性能更优,也更稳定

关于IFC数据的补充

你提到之前弃用Forge处理IFC是因为不稳定,现在Autodesk Forge的IFC支持已经有了很大提升:

  • 支持更完整的IFC分类和属性读取
  • 几何加载和渲染稳定性大幅提高
  • 兼容IFC4等新版本标准
    不过如果你们已经有自研的专业IFC阅读器和3D工具,那继续用自研方案肯定更贴合业务需求。

内容的提问来源于stack exchange,提问作者Daniel Ignjat

火山引擎 最新活动