Search code examples
three.jscallbackmouseobject-detectionraycasting

Using THREE.Raycaster - I get always the id 8?


I would like to create a dashboard on which a certain number of "THREE.Meshes" appear. When I click on one of them with the mouse, I want to call a function that recognizes what I clicked on.

My problem: I always get the ID:8 back from the "callFromTitleWithId" function. It doesn´t matter which one I selected with the mouseclick.

Maybe someone can help, I could not find the error :(

Here is the complete Example:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>TEST</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <style>
            body { margin: 0;
            background: black; }
            canvas { width: 100%; height: 100% }
        </style>
    </head>

    <body>
        <!-- Bibs -->
        <script src="../extlib/webthree/build/three.js"></script>
        <script>
            document.addEventListener('mousedown', onDocumentMouseDown, false);

            var arrayTiles = [];

            //[SCREEN]
            var SCREEN_WIDTH = window.innerWidth - 5;
            var SCREEN_HEIGHT = window.innerHeight - 5;

            screenOrientationValue=0;

            //RayCaster für Objekte
            var raycaster2 = new THREE.Raycaster(new THREE.Vector3(0,0,35),new THREE.Vector3(0,0,1));
            var mouse2 = new THREE.Vector2();

            var renderer = new THREE.WebGLRenderer();
            renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
            document.body.appendChild( renderer.domElement );

            var sceneWorld = new THREE.Scene();
            sceneWorld.add( new THREE.AxesHelper(50) );
            createTiles(sceneWorld);

            var camera= new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
            camera.position.set(0,0,35);

            var light2 = new THREE.AmbientLight( 0x20202A, 20, 100 );
            light2.position.set( 30, -10, 30 );
            sceneWorld.add( light2 );


            var animate = function () {

                requestAnimationFrame( animate );
                renderer.render( sceneWorld, camera);
                sceneUpdate();
            };

            animate();

            function sceneUpdate()
            {
                //Update            
            }

            function callFromTitleWithId(titleId)
            {
                console.log("ID:"+titleId);
            }

            function onDocumentMouseDown(event)
            {
                event.preventDefault();



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

                    raycaster2.setFromCamera(mouse2, camera);

                    var intersects2 = raycaster2.intersectObjects(arrayTiles,true);

                    if (intersects2.length > 0) {
                          console.log('Maus: X:'+mouse2.x+' Y:'+mouse2.y);
                          console.log("intersects:" + intersects2[0].object);
                          console.log("distance:" + intersects2[0].distance);
                          console.log("face:" + intersects2[0].face);
                          console.log("faceIndex:" + intersects2[0].faceIndex);
                          console.log("faceIndex y:" + intersects2[0].point.y);
                          console.log("faceIndex x:" + intersects2[0].point.x);

                          intersects2[0].object.callback();                       
                    }
            }

            function createTiles(sceneWorld)
            {
                var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
                var materialSide = new THREE.MeshBasicMaterial( {color: 0xffff00} );
                var materialsTiles = [materialSide, // Left side
                materialSide, // Right side
                materialSide, // Top side   ---> THIS IS THE FRONT
                materialSide, // Bottom side --> THIS IS THE BACK
                material, // Front side
                materialSide  // Back side
                ];

                var maxTileSizeWidth=11;
                var maxTileSizeHeight=8;

                var distanceBetween=2;
                var distanceBorderLeft=-1;

                var countInColumns=2;
                var countInRow = 4;

                var startCoordinatesX = ((((countInColumns * maxTileSizeWidth)+(countInColumns-6.5)*distanceBetween)/2))*-1;
                var startCoordinatesY = 20;

                var actualCoordinatesX = startCoordinatesX;
                var actualCoordinatesY = startCoordinatesY;

                //id for test
                var idNumberForTest=0;

                let r;
                for(r=1; r<=countInRow; r++ )
                {
                    let i;
                    for(i=1; i<=countInColumns; i++ )
                    {
                        var geometry = new THREE.BoxGeometry(maxTileSizeWidth, maxTileSizeHeight, 1 );
                        var tileX = new THREE.Mesh( geometry, materialsTiles );
                        tileX.position.set(actualCoordinatesX,actualCoordinatesY,0);

                        //Change actualCoordinates X
                        actualCoordinatesX = actualCoordinatesX + maxTileSizeWidth + distanceBetween;

                        idNumberForTest=idNumberForTest + 1;
                        tileX.callback = function(){callFromTitleWithId(idNumberForTest+'')};
                        arrayTiles.push(tileX);

                        sceneWorld.add( tileX );
                    }

                    actualCoordinatesY = actualCoordinatesY - (maxTileSizeHeight+1);
                    actualCoordinatesX = startCoordinatesX;
                }
            }
        </script>
    </body>
</html>

`


Solution

  • The problem is that idNumberForTest is equal for all objects. The callback functions always refer to the same variable. Instead of using a callback function, you can store the ID for the clicked mesh like so:

    tileX.userData.id = idNumberForTest;
    

    In this way, the correct ID is assigned to the respective object. Check out the following live example to see this approach in action.

    document.addEventListener('mousedown', onDocumentMouseDown, false);
    
    var arrayTiles = [];
    
    //[SCREEN]
    var SCREEN_WIDTH = window.innerWidth - 5;
    var SCREEN_HEIGHT = window.innerHeight - 5;
    
    screenOrientationValue=0;
    
    //RayCaster für Objekte
    var raycaster2 = new THREE.Raycaster(new THREE.Vector3(0,0,35),new THREE.Vector3(0,0,1));
    var mouse2 = new THREE.Vector2();
    
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
    document.body.appendChild( renderer.domElement );
    
    var sceneWorld = new THREE.Scene();
    sceneWorld.add( new THREE.AxesHelper(50) );
    createTiles(sceneWorld);
    
    var camera= new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
    camera.position.set(0,0,35);
    
    var light2 = new THREE.AmbientLight( 0x20202A, 20, 100 );
    light2.position.set( 30, -10, 30 );
    sceneWorld.add( light2 );
    
    
    var animate = function () {
    
    	requestAnimationFrame( animate );
    	renderer.render( sceneWorld, camera);
    	sceneUpdate();
    };
    
    animate();
    
    function sceneUpdate()
    {
    	//Update            
    }
    
    function callFromTitleWithId(titleId)
    {
    	console.log("ID:"+titleId);
    }
    
    function onDocumentMouseDown(event)
    {
    	event.preventDefault();
    
    	mouse2.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
    	mouse2.y =  - (event.clientY / renderer.domElement.clientHeight) * 2 + 1;
    
    	raycaster2.setFromCamera(mouse2, camera);
    	
    	console.log( arrayTiles );
    
    	var intersects2 = raycaster2.intersectObjects(arrayTiles,true);
    
    	if (intersects2.length > 0) {
    		console.log('Maus: X:'+mouse2.x+' Y:'+mouse2.y);
    		console.log("intersects:" + intersects2[0].object);
    		console.log("distance:" + intersects2[0].distance);
    		console.log("face:" + intersects2[0].face);
    		console.log("faceIndex:" + intersects2[0].faceIndex);
    		console.log("faceIndex y:" + intersects2[0].point.y);
    		console.log("faceIndex x:" + intersects2[0].point.x);
    
    		console.log("ID:", intersects2[0].object.userData.id);                
    	}
    }
    
    function createTiles(sceneWorld)
    {
    	var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
    	var materialSide = new THREE.MeshBasicMaterial( {color: 0xffff00} );
    	var materialsTiles = [materialSide, // Left side
    												materialSide, // Right side
    												materialSide, // Top side   ---> THIS IS THE FRONT
    												materialSide, // Bottom side --> THIS IS THE BACK
    												material, // Front side
    												materialSide  // Back side
    											 ];
    
    	var maxTileSizeWidth=11;
    	var maxTileSizeHeight=8;
    
    	var distanceBetween=2;
    	var distanceBorderLeft=-1;
    
    	var countInColumns=2;
    	var countInRow = 4;
    
    	var startCoordinatesX = ((((countInColumns * maxTileSizeWidth)+(countInColumns-6.5)*distanceBetween)/2))*-1;
    	var startCoordinatesY = 20;
    
    	var actualCoordinatesX = startCoordinatesX;
    	var actualCoordinatesY = startCoordinatesY;
    
    	//id for test
    	var idNumberForTest=0;
    
    	let r;
    	for(r=1; r<=countInRow; r++ )
    	{
    		let i;
    		for(i=1; i<=countInColumns; i++ )
    		{
    			var geometry = new THREE.BoxGeometry(maxTileSizeWidth, maxTileSizeHeight, 1 );
    			var tileX = new THREE.Mesh( geometry, materialsTiles );
    			tileX.position.set(actualCoordinatesX,actualCoordinatesY,0);
    
    			//Change actualCoordinates X
    			actualCoordinatesX = actualCoordinatesX + maxTileSizeWidth + distanceBetween;
    
    			idNumberForTest=idNumberForTest + 1;
    			arrayTiles.push(tileX);
    			tileX.userData.id = idNumberForTest;
    
    			sceneWorld.add( tileX );
    		}
    
    		actualCoordinatesY = actualCoordinatesY - (maxTileSizeHeight+1);
    		actualCoordinatesX = startCoordinatesX;
    	}
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>