You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按住Ctrl/Shift/Alt键时,预选子装配调用hitTest返回null的问题

解决按住修饰键时无法获取预选节点dbId的问题

当已选中存在父子关系的节点组,且按住Shift/Alt/Ctrl修饰键时,调用Viewer的hitTest会返回null——这是因为Viewer默认会优先处理修饰键对应的选择逻辑(如多选、反选),拦截了常规的命中检测。以下是两种可行的替代方案:

方案一:利用hitTestAll筛选选中节点

hitTestAll不受修饰键影响,会返回光标位置所有命中的节点,我们只需从中筛选出属于当前选中组的节点即可:

private getDbIdAtCoords(x: number, y: number): number {
    const viewer = this.viewerWrapper.viewer;
    const viewport = viewer.container.getBoundingClientRect();
    // 获取所有命中结果
    const hits = viewer.impl.hitTestAll(
        x - viewport.left,
        y - viewport.top,
        false
    );
    // 获取当前已选中的节点ID列表
    const selectedDbIds = viewer.getSelection();
    // 筛选出属于选中组的第一个命中节点
    for (const hit of hits) {
        if (selectedDbIds.includes(hit.dbId)) {
            return hit.dbId;
        }
    }
    return 0;
}

方案二:基于Three.js射线检测直接关联节点

绕开Viewer的默认hitTest逻辑,直接使用Three.js的射线检测,遍历已选中节点对应的几何体,找到光标命中的目标:

private getDbIdAtCoords(x: number, y: number): number {
    const viewer = this.viewerWrapper.viewer;
    const viewport = viewer.container.getBoundingClientRect();
    const canvas = viewer.canvas;

    // 将屏幕坐标转换为WebGL标准设备坐标
    const mouseX = ((x - viewport.left) / canvas.width) * 2 - 1;
    const mouseY = -((y - viewport.top) / canvas.height) * 2 + 1;

    // 创建射线投射器
    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera({ x: mouseX, y: mouseY }, viewer.impl.camera);

    // 收集所有已选中节点对应的Three.js网格
    const selectedDbIds = viewer.getSelection();
    const selectedObjects: THREE.Object3D[] = [];
    const fragList = viewer.model.getFragmentList();

    selectedDbIds.forEach(dbId => {
        const fragIds = fragList.getFragIds(dbId);
        fragIds.forEach(fragId => {
            const mesh = fragList.getVizmesh(fragId);
            if (mesh) {
                selectedObjects.push(mesh);
            }
        });
    });

    // 执行射线检测,获取命中结果
    const intersects = raycaster.intersectObjects(selectedObjects, true);
    if (intersects.length > 0) {
        // 从命中的网格反向获取对应的节点dbId
        const mesh = intersects[0].object as any;
        return mesh.fragId ? fragList.getNodeId(mesh.fragId) : 0;
    }
    return 0;
}

注意事项

  • 方案一的hitTestAll会返回场景中所有层级的命中节点,筛选时确保只保留已选中的节点,避免获取到无关对象。
  • 方案二需要处理模型的碎片(fragId)与节点(dbId)的映射关系,因为单个dbId可能对应多个几何体碎片。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 01:54:59