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

如何在Autodesk Viewer中用Three.js捕获Mesh点击事件?

Forge Viewer 自定义球体点击事件解决方案

问题背景

在Revit模型中添加了自定义球体标记,需要捕获球体的点击事件以展示详情。使用Three.js原生Raycaster时遇到报错:THREE.Raycaster: Unsupported camera type.;改用camera.perspectiveCamera后错误消失,但点击任意区域都会触发点击回调,无法准确识别球体点击。

问题代码

issues.results.map((issue) => {

    //ADDING SPHERES INTO MODEL

    const x = issue.linkedDocuments[0].details.position.x;
    const y = issue.linkedDocuments[0].details.position.y;
    const z = issue.linkedDocuments[0].details.position.z;

    let geom = new THREE.SphereGeometry(2, 32, 16);
    let material = new THREE.MeshBasicMaterial({
      color: 0xff8000,
    });

    let circle = new THREE.Mesh(geom, material);
    circle.position.set(x, y, z);
    viewer_exports.overlays.addMesh(circle, "custom-scene");
    
    // TRYING TO CAPTURE ON CLICK

    let camera = viewer_exports.getCamera();
    let raycaster = new THREE.Raycaster();
    let mouse = new THREE.Vector2();

    function onMouseMove(event) {
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
    }

    function onMouseClick(event) {
      raycaster.setFromCamera(mouse, camera);
      let isIntersected = raycaster.intersectObject(circle);
      if (isIntersected) {
        console.log("Mesh clicked!");
      }
    }

    window.addEventListener("click", onMouseClick, false);
    window.addEventListener("mousemove", onMouseMove, false);
  });

解决方案

1. 移除重复的事件绑定

当前代码在issues.results.map循环内重复绑定click和mousemove事件,会导致每创建一个球体就添加一次事件监听,引发重复执行问题。需将事件监听移至循环外部,仅绑定一次。

2. 适配Viewer的Unified Camera

Forge Viewer默认使用的UnifiedCamera并非Three.js原生相机,无法直接被THREE.Raycaster识别。可通过以下两种方式处理:

方式一:手动传递相机矩阵

获取相机的投影矩阵和视图矩阵,手动配置Raycaster:

const camera = viewer_exports.getCamera();
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// 鼠标移动事件(仅绑定一次)
function onMouseMove(event) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}

// 点击事件(仅绑定一次)
function onMouseClick(event) {
  // 手动设置Raycaster的射线参数
  const projectionMatrix = camera.projectionMatrix.clone();
  const matrixWorldInverse = camera.matrixWorldInverse.clone();
  
  raycaster.setFromCamera(mouse, null);
  raycaster.ray.origin.applyMatrix4(matrixWorldInverse);
  raycaster.ray.direction.transformDirection(matrixWorldInverse);
  raycaster.ray.direction.applyMatrix4(projectionMatrix).normalize();
  
  // 检测所有自定义球体
  const overlayScene = viewer_exports.overlays.getScene('custom-scene');
  const intersects = raycaster.intersectObjects(overlayScene.children, true);
  
  if (intersects.length > 0) {
    console.log("Mesh clicked!", intersects[0].object);
  }
}

window.addEventListener("click", onMouseClick, false);
window.addEventListener("mousemove", onMouseMove, false);

方式二:使用Viewer内置相机实例

直接获取Viewer内部的透视/正交相机实例传递给Raycaster:

function onMouseClick(event) {
  const activeCamera = camera.perspectiveCamera || camera.orthographicCamera;
  raycaster.setFromCamera(mouse, activeCamera);
  
  const overlayScene = viewer_exports.overlays.getScene('custom-scene');
  const intersects = raycaster.intersectObjects(overlayScene.children, true);
  
  if (intersects.length > 0) {
    console.log("Mesh clicked!", intersects[0].object);
  }
}

3. 修正交点判断逻辑

raycaster.intersectObject返回的是数组,空数组在if判断中会被视为true(因为数组是引用类型),需判断数组长度是否大于0来确认是否命中物体。

4. 绑定自定义数据到球体

创建球体时,可将对应的issue详情存入userData属性,方便点击时直接获取:

let circle = new THREE.Mesh(geom, material);
circle.position.set(x, y, z);
circle.userData.issue = issue; // 绑定issue数据
viewer_exports.overlays.addMesh(circle, "custom-scene");

点击时获取详情:

if (intersects.length > 0) {
  const clickedIssue = intersects[0].object.userData.issue;
  console.log("球体详情:", clickedIssue);
}

内容的提问来源于stack exchange,提问作者Gustavo da Costa e Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 04:00:03