Using Raycaster in ThreeJS to pick out faces from models loaded with STL Loader

I am currently developing a .stl viewer using Three.js with the objective of selecting and calculating specific areas within the model. To achieve this area calculation, I need the capability to select faces (e.g. change color).

Although I came across something similar, my research indicates that it only works with pre-made meshes (like the cube in the example).

I aim to incorporate this example into my own code.

While there are existing solutions, I am struggling to implement a functional method in my code:

My current code includes a fully operational .stl loader and viewer. The raycaster exists, but it's not functioning correctly, so I have temporarily disabled it. Credits to Mugen87 for the fix!

You can access my code and an example .stl file on GitHub. It only requires a Live Server environment, which can easily be set up using VSCode (refer to the readme).

Here's a snippet of 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;
            }

            // Basic setup
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xdddddd);


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

            // Renderer setup
            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);

            }

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

            // Orbit control for camera
            let controls = new THREE.OrbitControls(camera, renderer.domElement);

            // Ambient lighting
            hlight = new THREE.AmbientLight(0xffffff, 5.3);
            scene.add(hlight);

            // Point lights
            light = new THREE.PointLight(0xffffff, 1, 10000);
            light.position.set(0, 300, 500);
            scene.add(light);

            controls.update();
            // Animation
            function animate() {
                raycaster.setFromCamera(mouse, camera);
                scene.children[2].material.color.set(0x1313)
                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);
            }

            // STLLoader setup
            let loader = new THREE.STLLoader();
            loader.load('../converter/output/output.stl', function (geometry) {

                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: ", )
            });


        }

        // Initialize method
        init();
    </script>

</body>

</html>

Answer №1

I have temporarily disabled the raycaster as it seems to be malfunctioning at the moment.

After debugging your code on my local machine, it is apparent that the raycasting feature is not functioning due to the onMouseMove() event not being called. To fix this, you need to register it as an event listener first. Add the following line to your code:

document.addEventListener( 'mousemove', onMouseMove, false );

Once you have done this and uncommented the raycasting code within the animation loop, the model should turn red when the mouse hovers over it.

Answer №2

Utilizing a raycaster to specifically select faces, as inquired in my previous question, involves extracting the data from the raycaster's intersects property. This allows for the manipulation of the face, essentially enabling its 'selection'.

For instance:

for (var i = 0; i < intersects.length; i++) {
    console.log(intersects[i].face)
}

The face object offers numerous built-in methods that empower users to manipulate the face and retrieve information from it. The following snippet, directly copied from my console.log(intersects[i].face) output, pertains to a single face only (keep in mind that these are just the primary methods, and many more exist as well).

Ac {a: 5307, b: 5308, c: 5309, normal: p, vertexNormals: Array(0), …}
    a: 5307
    b: 5308
    c: 5309
color: D
    // Other properties and methods listed here
    __proto__: Object
materialIndex: 0
normal: p
    // Other properties and methods listed here
    __proto__: Object
vertexColors: Array(0)
    length: 0
    __proto__: Array(0)
vertexNormals: Array(0)
    length: 0
    __proto__: Array(0)
// Additional methods and properties
__proto__:
    // Additional methods and properties
    __proto__: Object

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering a malfunction while executing an npm command specified in the package.json file

Currently, I am following a tutorial on Node, React, and Express on Udemy. In the tutorial, when I execute the command npm run data:import I encounter the following error: undefined npm ERR! code ELIFECYCLE npm ERR! errno 1 ...

Choosing multiple lists in Angular 2 can be achieved through a simple process

I am looking to create a functionality where, upon clicking on multiple lists, the color changes from grey to pink. Clicking again will revert the color back to grey. How can I achieve this using class binding? Below is the code snippet I have tried with ...

What is the speed of communication for Web Worker messages?

One thing I've been pondering is whether transmission to or from a web worker could potentially create a bottleneck. Should we send messages every time an event is triggered, or should we be mindful and try to minimize communication between the two? ...

difficulty in detecting cordova deviceready

When developing my iOS app using JavaScript, I included cordova.js in my index.html file. The deviceready function is called correctly upon first login, but when I logout and then login again, the device ready event does not trigger. document.addEventLi ...

Retrieve an item from a table in VUE upon clicking

I am currently using Vue Bootstrap and I want to be able to access the item when a row in the table is clicked. I have set up a table and a clickmeRow method to handle the action on the clicked item. <b-table-lite hover :items="it ...

Is it possible for the 'error' event to be triggered on the 'http.IncomingMessage' object during a node.js http.request?

There are 4 ways to encounter errors when calling http.get(url, cb): httpThrows() This error can occur due to a wrong format of the url or incorrect callback. function httpThrows() { try { http.get("www.missing-protocol.com", res => { ...

Fetch data dynamically with jQuery AJAX

I am working on a jQuery Ajax form submission to a PHP page with the goal of dynamically returning values instead of all at once. For instance, in my jQuery code: jQuery.ajax({ type: "POST", url: "$PathToActions/Accounts.php", dataType: ...

Delete items within the first 10 minutes of shutting it down

Is there a way to temporarily remove a newsletter element for 10 minutes after closing it on a webpage? The idea is that once the panel is closed, it should stay hidden even if the page is refreshed within that timeframe. I was considering using local stor ...

Ways to confirm if there have been any updates in Kendo Observable

Hey there! I have a form with specific fields that I've converted to Kendo Observable like this: var TITLE = $("#TITLE").val().trim(); var DESC = $("#DESC").val().trim(); Analysis.Kendo_VM = kendo.observable({ TITLE: TITLE != null ? TITLE : ...

"Error TS2339: The property specified does not exist within type definition", located on the input field

When a user clicks a specific button, I need an input field to be focused with its text value selected entirely to allow users to replace the entire value while typing. This is the markup for the input field: <input type="text" id="descriptionField" c ...

Creating a JavaScript class definition without the need for instantiation

I am looking to create an empty data class in JavaScript that can later be filled with JSON data. In C#, I would typically do something like this: class Person { string name; int age; Person[] children; var whatever; } However, when I try ...

Utilize client-side script in nodejs to share module functionalities

I created a function within the user controller module to verify if a user is logged in on the site: exports.isLoggedIn = function(req, res, next) { if (req.user) { return true; } else { return false; } }; I'm unsure of h ...

Exploring the PayPal Checkout JavaScript SDK's CreateOrder call and interpreting the response

I am currently exploring how to use the JavaScript SDK to display PayPal payment buttons on a website. I am new to working with JSON and REST API calls, so I am facing some challenges in implementing this. The createOrder function is running smoothly and ...

My goal is to retrieve the top three highest rated products

// @route GET /api/products/top // @desc Retrieve top-rated products // @access Available to the public router.get( '/best', asyncHandler(async (req, res) => { const bestProducts = await Product.find({}).sort({ rating: -1 }).limi ...

JSON API WebConnector for Tableau

After creating a tableau webconnector to extract data from an internal API, I used earthquakeUSGS.html as a reference (https://github.com/tableau/webdataconnector). The API responds with json data (see code below). Utilizing the "Web data connector simulat ...

showing the angular response data in a HTML table layout within a specific div element

I'm currently using the angular-fullstack generator and I've received data in response to a GET request in the form of integer values (1, 2 5, 6 134, 245 236, 567 415, 234 and so on). I'd like to display these values in an HTML t ...

How can JavaScriptExecutor be used to stop searching for an element on a constantly scrolling interface, such as Facebook, after a specific time period?

Imagine that I am trying to locate my post on a Facebook page by continuously scrolling down. Specifically, I am searching for my post based on my profile name. To achieve this, I utilize JavascriptExecutor to initiate the scrolling process until it locate ...

Transforming an HTML Attribute into a JavaScript Object

I'm encountering an issue with converting an HTML-data attribute into a JavaScript Object. Here is my approach: The data Attribute appears as: <a id="stringObj" data-string="{'Foo':'Bar'}">Some A-Tag</a> In JavaScri ...

Changing a property of an object in Angular using a dynamic variable

It seems like I may be overlooking a crucial aspect of Angular rendering and assignment. I was under the impression that when a variable is updated within a controller's scope, any related areas would automatically be re-evaluated. However, this doesn ...

Angular's getter value triggers the ExpressionChangedAfterItHasBeenCheckedError

I'm encountering the ExpressionChangedAfterItHasBeenCheckedError due to my getter function, selectedRows, in my component. public get selectedRows() { if (this.gridApi) { return this.gridApi.getSelectedRows(); } else { return null; } } ...