Raycasting and object selection

Hi there, I am new to three.js and I would like to know something about raycasting. I have a model:

In my model, there are cylinders that I have placed around like beacon readers that can detect the motion of people inside the mine or tunnel. When I export my map, I do get access to these cylinders, and I would like to interact with them one by one. For example, when they’re active, they should light up green, and when they are not working, they should light up red, and when they’re offline, they should be grey. Also, I would like to have event listeners on them. When I click on any one of them, it should tell me what state it’s in: “Active, Offline, or Broken.” I am using raycaster, but I can’t seem to click on any of the points. Here is a piece of my code:

getPoints = sceneObject.children[0].children.filter(i => i.name.indexOf('Cylinder0') !== -1);
                getPoints.forEach(point => {
                    const randomColor = new THREE.Color(0x0000FF);
                    point.material.color.set(randomColor);

                    const emissiveColor = randomColor.clone().multiplyScalar(0.2);
                    point.material.emissive.set(emissiveColor);

                    point.material.envMapIntensity = 1;

                    point.material.metalness = 0.5;
                    point.material.roughness = 0.2;
                    point.material.transparent = true;
                    point.material.opacity = 1;
                });

let onPointerMove = (event) => {
                    pointer.x = (event.clientX / canvas.clientWidth) * 2 - 1;
                    pointer.y = - (event.clientY / canvas.clientHeight) * 2 + 1;
                }

                let onPointerClick = (event) => {
                    debugger;
                    raycaster.setFromCamera(pointer, camera);
                    const intersects = raycaster.intersectObjects(scene.children[0].children[0].children);
                    console.log(intersects);

                    if (intersects.length > 0) {
                        const intersectedPoint = intersects[0].object;
                        console.log('Point Clicked: ', intersectedPoint);
                        intersectedPoint.material.color.set(0xFF0000);
                    } else {
                        console.log('No interactions were found');
                    }
                }
                canvas.addEventListener('pointermove', onPointerMove, false);
                canvas.addEventListener('click', onPointerClick, false);
            }

Since you raycast trough nested children, you may miss the second parameter to indicate you wish for a “traversing raycast”. Just a guess… try this:

raycaster.intersectObjects(scene.children[0].children[0].children, true)

It really depend how your 3d model is constructed.
Another way to make it more clean for three’s raycast, would be to save your checkpoints inside an array then use it as target. The goal is to avoid any chance of parasite object along the way:

raycaster.intersectObjects( myArray, true)

Thank you Oxyn, I have found a solution and it’s working as I intend it to. I just had to create bounding rect like so:

let onPointerMove = (event) => {
         const rect = canvas.getBoundingClientRect();
         pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
         pointer.y = - ((event.clientY - rect.top) / rect.height) * 2 + 1;
};

…and I had to assign new uuid’s for each cylinder’s material because when I was clicking on one, it changed the colours for each and everyone. Reason being that. On my model, I put all the cylinders under one collection. I believe I can write a better code moving forward and create much more simplified models but everything is working as intended for now.

1 Like