如何在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
相关产品推荐
相关产品推荐

