The positioning of the Three.js THREE.Projector has been adjusted to a new location

It has come to my understanding that in version 71, the THREE.projector is no longer available (refer to the deprecated list). However, I am uncertain about the replacement process, especially in this code snippet designed to identify the clicked object:

var vector = new THREE.Vector3(
  (event.clientX / window.innerWidth) * 2 - 1,
  -(event.clientY / window.innerHeight) * 2 + 1,
  0.5
);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(
  camera.position,
  vector.sub(camera.position).normalize()
);
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
  clicked = intersects[0];
  console.log("my clicked object:", clicked);
}

Answer №1

Here is a convenient pattern that now works with both orthographic and perspective camera types:

const raycaster = new THREE.Raycaster(); // create only once
const mouse = new THREE.Vector2(); // create only once

...

mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;

raycaster.setFromCamera( mouse, camera );

const intersects = raycaster.intersectObjects( objects, recursiveFlag );

Using three.js version r.84

Answer №2

If you are struggling to understand the concept of raycasting in THREE.JS, the official THREE.JS raycaster documentation provides a comprehensive explanation. It covers all the necessary information along with addressing any potential confusion you may have.

var raycaster = new THREE.Raycaster(); 
var mouse = new THREE.Vector2(); 

function onMouseMove( event ) { 
  // calculating the mouse position in normalized device coordinates 
  // (-1 to +1) for both components 
  mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; 
  mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; 
} 

function render() { 
  // updating the picking ray with the camera and mouse position
  raycaster.setFromCamera( mouse, camera ); 
  // finding 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 ); 
} 
window.addEventListener( 'mousemove', onMouseMove, false );        
window.requestAnimationFrame(render);`

This detailed explanation should make the concept clearer for you.

Answer №3

Make sure to follow the recommended version mentioned above for optimal performance.

If you encounter any issues with your code, simply substitute:

projector.unprojectVector( vector, camera );

with

vector.unproject(camera);

Answer №4

It has come to my attention that the information provided earlier works well when the window is maximized, however, if there are other elements on the page in addition to a canvas, it is necessary to adjust the click events target's offset accordingly.

In this context, 'e' represents the event and 'mVec' is a THREE.Vector2 object.

    let et = e.target, de = renderer.domElement;
    let trueX = (e.pageX - et.offsetLeft);
    let trueY = (e.pageY - et.offsetTop);
    mVec.x = (((trueX / de.width) * 2) - 1);
    mVec.y = (((trueY / de.height) * -2) + 1);
    wup.raycaster.setFromCamera( mVec, camera );
    [Next step: Check for any intersections, etc.]

It is important to account for dragging (mouse movements) as well, as releasing a drag might inadvertently trigger a click event when using window.addEventListener( 'click', function(e) {<code>});

[It is worth noting that the negative sign placement has been adjusted for clarity.]

Answer №5

Visit this link for more information.

const vector = new THREE.Vector3();
const raycaster = new THREE.Raycaster();
const dir = new THREE.Vector3();

...

if ( camera instanceof THREE.OrthographicCamera ) {

    vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, - 1 ); // z = - 1 important!

    vector.unproject( camera );

    dir.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );

    raycaster.set( vector, dir );

} else if ( camera instanceof THREE.PerspectiveCamera ) {

    vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 ); // z = 0.5 important!

    vector.unproject( camera );

    raycaster.set( camera.position, vector.sub( camera.position ).normalize() );

}

const intersects = raycaster.intersectObjects( objects, recursiveFlag );

Objects = array of objects of type Object3D to check for intersection with the ray. Can be everything in your scene, but might be inefficient if you have a lot of stuff there.

recursiveFlag = If true, it also checks all descendants. Otherwise it only checks intersection with the object. Default is true.

Read the documentation for more details.

Answer №6

I wanted to share a more detailed account of my experiences in implementing this process, as I believe the previous answers lack in providing a comprehensive explanation.

In my scenario, I utilized STL_Viewer which can be found at this link (). This tool assists in creating a threeJS scene and provides the necessary camera and scene components required for this task.

function mouse_click_normalize(clientX,clientY,offset_left,offset_top){
                var mouse = new THREE.Vector2(); // initialize once
                var c = document.getElementById("stl_contEye").childNodes[0];//retrieve canvas element created here
                mouse.x = ( (event.clientX-offset_left) / (c.clientWidth)  * 2) - 1;
                mouse.y = - ( (event.clientY-offset_top) / (c.clientHeight) ) * 2 + 1;
                console.log(`clientX=${clientX},clientY=${clientY},mouse.x=${mouse.x},mouse.y=${mouse.y}\nwidth=${c.clientWidth},height=${c.clientHeight},offset_left=${offset_left},offset_top=${offset_top}`);
                return mouse;
           }

The above function automatically normalizes the x,y coordinates to the clicked space (between -1 and 1), as required by the raycaster for its setFromCamera function.

To account for potential offsets in the screen section being clicked, I designed it this way to ensure it functions correctly regardless of its position in the DOM. Simply replace "stl_contEye" with the name of the div containing the rendered ThreeJS or STLViewer in your case.

function model_clicked(model_id, e, distance, click_type){
                console.log(e);
                
                var pos = new THREE.Vector3(); // initialize once and reuse
                // var vec = new THREE.Vector3(); // initialize once and reuse
                
                var camera = stl_viewer1.camera;
                var scene = stl_viewer1.scene;
                // vec.unproject( camera );
                // vec.sub( camera.position ).normalize();
                // //var distance2 = (distance - camera.position.z) / vec.z;
                // pos.copy( camera.position ).add( vec.multiplyScalar( distance ) );
                var mouse_coords_normalized = mouse_click_normalize(e.clientX,e.clientY,e.srcElement.offsetLeft,e.srcElement.offsetTop);
                var raycaster = new THREE.Raycaster(); // initialize once
                raycaster.setFromCamera( mouse_coords_normalized, camera );
                //console.log(raycaster);
                //console.log(scene);

                var intersects = raycaster.intersectObjects( scene.children, true );
                if(intersects.length > 0){
                    console.log(intersects);
                    console.log(intersects[0].point);
                    pos = intersects[0].point
                }
}

This method allows you to pinpoint the exact location in 3D space where you clicked. The model_clicked function simply returns an event containing the clientX or clientY, which you must obtain yourself if not using STLViewer's event for click detection. Various examples are provided in the previous answers for this.

I hope this guide proves helpful to those navigating this process, whether with or without stl_viewer.

Answer №7

camera.capture = Date.now()

        document.getElementById("viewfinder").addEventListener("mousedown", triggerDown);
        document.getElementById("viewfinder").addEventListener("mouseup", triggerUp);
    
        document.getElementById("viewfinder").addEventListener( 'mousemove', displayImage);
    function triggerDown(event) {
    camera.action = 1;
    
    
    }
    function triggerUp(event) {
     
    camera.action = 0;
       

}
    
    function displayImage(event){
        
            
     if( images.length > 0 &&  keys.length > 0  ){
    
     if( camera.action > 0   ){
    
    
        
    camera.pointer = new THREE.Vector2();
        
    
    var width = document.getElementById("viewfinder").scrollHeight;
    var height =  document.getElementById("viewfinder").scrollWidth;
    var x = (event.clientX / height)  - 1;
        var y = - (event.clientY  / width)  + 1;
     camera.pointer.x = x ;  camera.pointer.y  = y;  
    
    var material = new THREE.MeshBasicMaterial({color: 0xFF9900});
    var geometry = new THREE.SphereGeometry(5, 5, 5);
    var mesh = new THREE.Mesh(geometry, material);
        mesh.position.set(camera.position.x, camera.position.y, camera.position.z);
    
        
       
        mesh.ray = new THREE.Raycaster();
       
       mesh.ray.setFromCamera( camera.pointer, camera);
            
            
        mesh.owner = 'user';
    
        mesh.strength = 100;
        
        images.push(mesh);
        scene.add(mesh);
    camera.lastshot = Date.now();
    camera.capture = Date.now();
    }
    }
    
    }
    function display() { const camControls = new FirstPersonControls(camera);
        
    camControls.update(100);
    
            
     if( images.length > 0 &&  keys.length > 0  ){
    
     if( camera.action > 0 && camera.capture + 25 < Date.now()  ){
     displayImage(event)
    camera.capture = Date.now();
    }} 
    
    
    if(images.length > 1){
    
        for (var i = 0; i < images.length - 1; i++) {
    
            
            
    var current = images[i], position = current.position, direction = current.ray.direction;
    
    
    if(current.owner == "user"){
    
    
    
                
                
    var bulletSpeed = window.document.getElementById("bulletspeed").value;
                current.translateX(bulletSpeed * direction.x);
                current.translateY(bulletSpeed * direction.y);
                current.translateZ(bulletSpeed * direction.z);
    
    }
    
    
        
    
    }}
    
    
    renderer.render( scene, camera ); }

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

Is there a method to track the progress of webpage loading?

I am working on a website built with static HTML pages. My goal is to implement a full-screen loading status complete with a progress bar that indicates the page's load progress, including all images and external assets. Once the page has fully loaded ...

Error encountered in React and Redux: Unable to read properties of undefined (specifically 'region')

Whenever a user clicks on edit, I am fetching data (an object) into a redux state and displaying it in a textarea. Here is the code snippet: const regionData = useSelector((state) => state.myReducer.userDetailList.region); The problem arises when this ...

Is there a way to access the initial element of the array within this variable assignment?

When utilizing the element id 10_food_select: var id = $(sel).attr("id").split("_"); An array is generated as follows: ["10", "food", "select"] The desired outcome is to have id = 10 (or whichever value is in the first element). This can be achieved l ...

I am no longer able to connect to mySQL using node js

I recently upgraded my node version, and now I'm facing issues accessing my database through my application. I have tried various solutions such as: - Changing the route to 127.0.0.1. - Adding my port number 3306. - Including socketPath: '/Applic ...

Converting coordinates of latitude and longitude into JSON format

I'm currently facing some challenges with organizing map waypoints, defined by latitude/longitude pairs, within a JSON array. This is the progress I've made so far. var llData = {}; var waypointData = {}; for (i = 0; i < routeArray.length; ...

Module 'prompt-sync' not found

When attempting to take user input in Node.js using the prompt module, I encountered the following error message: node:internal/modules/cjs/loader:998 throw err; ^ Error: Cannot find module 'prompt-sync' Require stack: - D:\Code\C+ ...

Broaden material-ui component functionality with forwardRef and typescript

To enhance a material-ui component with typescript, I have the javascript code provided in this link. import Button from "@material-ui/core/Button"; const RegularButton = React.forwardRef((props, ref) => { return ( <B ...

The JSONP file can be successfully retrieved through an AJAX request in the console, however, the data does not display when attempting

Currently attempting to send an ajax request utilizing the following code snippet: $.ajax({ url: "http://mywebsite.com/blog/json", dataType: "jsonp", jsonpCallback: "jsonpCallback" }); function jsonpCallback(data) { console.log(data); } E ...

Exploring the power of ElasticSearch alongside Mysql

As I plan the development of my next app, I am faced with the decision between using NoSQL or a Relational Database. This app will be built using ReactJS and ExpressJS. The data structure includes relational elements like videos with tags and users who li ...

`Express.js Controllers: The Key to Context Binding`

I'm currently working on a project in Express.js that involves a UserController class with methods like getAllUsers and findUserById. When using these methods in my User router, I have to bind each method when creating an instance of the UserControlle ...

Navigating Google Oauth - Optimal User Sign in Locations on Frontend and Backend Platforms

What are the distinctions between utilizing Google OAuth versus implementing user sign-ins at the frontend of an application, as opposed to handling them on the backend? For instance, managing user authentication in React to obtain the ID and auth object ...

React Router Redux causing an infinite loop when navigating back

I recently encountered a strange issue with my React + Redux app. I am using React Router in combination with React Router Redux for routing, and I noticed some unexpected behavior. Clicking the back button once takes me from route 0 to -1 successfully. Ho ...

Discovering an item within an array of objects using the find method in Ajax

Within my backend PHP code, I iteratively define the following: foreach ( $data as $res ){ $approved[ ] = [ 'id' => $count, 'title' => "some title" ]; $count++;$title=... ) This eventually leads to the creation ...

Encountering an issue while attempting to utilize the .find() method in Mongoose

Currently, I am in the process of setting up a database using MongoDB and integrating the Mongoose ODM with Node.js. After running the code multiple times without any issues, I encountered an unexpected error when adding the last block to utilize the .find ...

Should we consider packaging the npm dependencies code along with our code as a best practice?

What is the best way to handle npm dependencies in our code bundle? If it's preferable to include the npm dependency code in our bundle, does it make sense to add it as a separate module or package? If not, how can I prevent bundling my dependencie ...

Utilizing Axios recursion to paginate through an API using a cursor-based approach

Is there a way to paginate an API with a cursor using axios? I am looking to repeatedly call this function until response.data.length < 1 and return the complete array containing all items in the collection once the process is complete. Additionally, I ...

Avoid consistently updating information

I am experiencing a strange issue in my project. I have 2 tabs, and in one tab, there are checkboxes and a submit button. The user selects items from the checkboxes, and upon clicking the button, they should see their selections in the other tab. This fu ...

Find the size of the grid using the data attribute

I am currently working on a piece of code that involves fetching a data-attribute known as grid size from the HTML. My objective is to create a conditional statement that checks whether the value of grid size is "large" or "small", and assigns specific x a ...

NextJS 13: Handler Route Request With Invalid JSON Format

Currently utilizing the most recent Next.JS 13 Route Handler to process data submitted by a form. I'm encountering an issue where the new Route Handler isn't working as expected, even though I followed the documentation provided in the link above ...

The cursor seems to be playing a game of hopscotch every time I

In the text box, I've implemented a feature to prevent typing of a forbidden character: #. While this feature is successful in restricting the input of the forbidden character, there's an issue that arises when data is already filled in the text ...