Anjalee
Anjalee

Reputation: 11

How to raycast onto a pointcloud in ThreeJs R71

I am working in autodesk forge which includes Threejs r71 and I want to use a raycaster to detect clicks on different elements within a pointcloud.

Sample code for how to do this with ThreeJs r71 be appreciated.

Right now, I register an extension with the forge api and run the code below within it. It creates creates a pointcloud and positions the points at predetermined locations (saved within the cameraInfo array).

let geometry = new THREE.Geometry();
this.cameraInfo.forEach( function(e) {
        geometry.vertices.push(e.position);
    }
)
const material = new THREE.PointCloudMaterial( { size: 150, color: 0Xff0000, sizeAttenuation: true } );
this.points = new THREE.PointCloud( geometry, material );
this.scene.add(this.points);

/* Set up event listeners */
document.addEventListener('mousemove', event => {
    // console.log('mouse move!');
    let mouse = {
        x: ( event.clientX / window.innerWidth ) * 2 - 1,
        y: - ( event.clientY / window.innerHeight ) * 2 + 1
    };

    let raycaster = new THREE.Raycaster();

    raycaster.params.PointCloud.threshold = 15;
    let vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(this.camera);
    raycaster.ray.set(this.camera.position, vector.sub(this.camera.position).normalize());
    this.scene.updateMatrixWorld();
    let intersects = raycaster.intersectObject(this.points);


    if (intersects.length > 0) {
        const hitIndex = intersects[0].index;
        const hitPoint = this.points.geometry.vertices[ hitIndex ];
        console.log(hitIndex);
        console.log(hitPoint);
    }

}, false);

The output seems to be illogical. At certain camera positions, it will constantly tell me that it is intersecting an item in the pointcloud (regardless of where the mouse is). And at certain camera positions, it won't detect an intersection at all.

TLDR: it doesn't actually detect an intersection b/w my pointcloud and the mouse.

Upvotes: 1

Views: 807

Answers (1)

Petr Broz
Petr Broz

Reputation: 9942

I've simplified the code a bit, using some of the viewer APIs (using a couple of sample points in the point cloud):

  const viewer = NOP_VIEWER;
  const geometry = new THREE.Geometry();
  for (let i = -100; i <= 100; i += 10) {
    geometry.vertices.push(new THREE.Vector3(i, i, i));
  }
  const material = new THREE.PointCloudMaterial({ size: 50, color: 0Xff0000, sizeAttenuation: true });
  const points = new THREE.PointCloud(geometry, material);
  viewer.impl.scene.add(points);

  const raycaster = new THREE.Raycaster();
  raycaster.params.PointCloud.threshold = 50;
  document.addEventListener('mousemove', function(event) {
    const ray = viewer.impl.viewportToRay(viewer.impl.clientToViewport(event.clientX, event.clientY));
    raycaster.ray.set(ray.origin, ray.direction);
    let intersects = raycaster.intersectObject(viewer.impl.scene, true);
    if (intersects.length > 0) {
      console.log(intersects[0]);
    }
  });

I believe you'll need to tweak the raycaster.params.PointCloud.threshold value. The ray casting logic in three.js doesn't actually intersect the point "boxes" that you see rendered on the screen. It only computes distance between the ray and the point (in the world coordinate system), and only outputs an intersection when the distance is under the threshold value. In my example I tried setting the threshold to 50, and the intersection results were somewhat better.

As a side note, if you don't necessarily need point clouds inside the scene, consider overlaying HTML elements over the 3D view instead. We're using the approach in the https://forge-digital-twin.autodesk.io demo (source) to show rich annotations attached to specific positions in the 3D space. With this approach, you don't have to worry about custom intersections - the browser handles everything for you.

Upvotes: 2

Related Questions