Selecting faces with Raycaster in ThreeJS with STL Loader

I am currently working on a .stl viewer using Three.js. The goal is select and calculate certain areas.
For this area calculation I need to be able to select (e.g. change the color) faces.

I have found something similar, but from what I have seen in my own research this only seems to be working with ready made meshes (like the cube in the example).

I am looking to implement this example in my own code.

It is evident that something like this has been done before, but I just can’t seem to implement any kind of working way in my own code:

My current code has a fully functional .stl loader and viewer. The raycaster is there, but doesn’t seem to be working properly so I have commented it out for the moment. Mugen87, thank you for the fix!

You can download my code and an example .stl file from github. Which only requires a Live Server environment which can easily be ran using VSCode (see readme).

My current code:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3d viewer tjalle</title>
    <link rel="stylesheet" type="text/css" href="../style/render.css">
</head>

<body>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r117/build/three.min.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r117/examples/js/loaders/STLLoader.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r117/examples/js/controls/OrbitControls.js"></script>

    <script>
        function init() {
            var raycaster = new THREE.Raycaster();
            var mouse = new THREE.Vector2();
            document.addEventListener( 'mousemove', onMouseMove, false );


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

            // Setup some basic stuff
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xdddddd);


            // Setup Camera 
            camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 5000);

            // Setup renerer and add to page
            renderer = new THREE.WebGLRenderer({
                antialias: true
            });
            renderer.setSize(window.innerWidth, window.innerHeight);

            document.body.appendChild(renderer.domElement);


            window.addEventListener('resize', onWindowResize, false);

            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize(window.innerWidth, window.innerHeight);

            }

            // Setup Camera Position
            camera.rotation.y = 45 / 180 * Math.PI;
            camera.position.x = 800;
            camera.position.y = 100;
            camera.position.z = 1000;

            // Add Camera Control through orbit.js
            let controls = new THREE.OrbitControls(camera, renderer.domElement);

            // Add some basic ambient lighting (Does light all parts equally and does not cast shadows)
            hlight = new THREE.AmbientLight(0xffffff, 5.3);
            scene.add(hlight);


            //Add some point lights to simulate real lights
            light = new THREE.PointLight(0xffffff, 1, 10000);
            light.position.set(0, 300, 500);
            scene.add(light);

            controls.update();
            // Animation Script
            function animate() {
                raycaster.setFromCamera(mouse, camera);
                scene.children[2].material.color.set(0x1313)
                // calculate objects intersecting the picking ray
                var intersects = raycaster.intersectObjects(scene.children);
                for (var i = 0; i < intersects.length; i++) {
                    intersects[i].object.material.color.set(0xff0000);
                }
                

                renderer.render(scene, camera);
                requestAnimationFrame(animate);
            }

            // Setup GLTF Loader and load in car
            let loader = new THREE.STLLoader();
            loader.load('../converter/output/output.stl', function (geometry) {

                // console.log(gltf);
                var material = new THREE.MeshLambertMaterial({
                    color: 0x1313,
                    wireframe: false
                });
                var mesh = new THREE.Mesh(geometry, material);
                mesh.castShadow = true;
                mesh.receiveShadow = true;
                mesh.position.set(0, 0, 0);

                scene.add(mesh);
                renderer.render(scene, camera)
                animate();
                console.log("SCene: ", )
            });


        }

        // Call method for starting init
        init();
    </script>

</body>

</html>
  1. The example you’ve shown selects faces, not vertices. Do you mean to select triangles or points that build the triangles?
  2. If you’d like to select and interact with vertices, I don’t think you can use Raycaster directly on them. What you can do is use a point cloud created from a geometry.
    You can see an example here (source code; ignore the shaders etc, only casting geometry to points and raycasting onto them is important in this case.)

You can find a few more examples of raycasting on points here -> https://threejs.org/examples/?q=interactive

You are right, I am trying to select triangles, so faces. Will edit this accordingly. Also the raycaster is now working, also has been posted in the edit.