16

I've put together a WebGL script that displays several meshes using jsonloader and three.js and I now want to add MouseOver and onClick events. The first of these is simply changing the colour of the mesh when the mouse hovers over it:

function render() {
  requestAnimationFrame(render);    
  mesh.rotation.z += 0.090;    
  raycaster.setFromCamera(mouse, camera);    
  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);

}

The render function above allows me to change the colour of any mesh to red by hovering over it. I've tried several if/else variants to try and have this effect only work when the mouse is over a mesh but I can't get it to work- it just stays red. Can anyone suggest how I might amend my script?

Thanks.

gman
  • 100,619
  • 31
  • 269
  • 393
Martin Leman
  • 163
  • 1
  • 1
  • 4
  • Why do you put a space between every row? And why do you use tabs? – Bálint Jul 11 '16 at 19:24
  • That's a bad habit I've gotten in to, sorry. I sometimes copy and paste bits of code over from other files and leave the space in to identify the bit that I've added. – Martin Leman Jul 12 '16 at 11:47

2 Answers2

20

You have to set the color back to the original color on mouse out, that won't happen automatically...

Check this example on http://stemkoski.github.io specifically the update method:

Here a fiddle with a demo updated to the latest three.js master:

// create a Ray with origin at the mouse position
//   and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects( scene.children );

// INTERSECTED = the object in the scene currently closest to the camera 
//      and intersected by the Ray projected from the mouse position    

// if there is one (or more) intersections
if ( intersects.length > 0 )
{
    // if the closest object intersected is not the currently stored intersection object
    if ( intersects[ 0 ].object != INTERSECTED )
    {
        // restore previous intersection object (if it exists) to its original color
        if ( INTERSECTED )
            INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
        // store reference to closest object as current intersection object
        INTERSECTED = intersects[ 0 ].object;
        // store color of closest object (for later restoration)
        INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
        // set a new color for closest object
        INTERSECTED.material.color.setHex( 0xffff00 );
    }
}
else // there are no intersections
{
    // restore previous intersection object (if it exists) to its original color
    if ( INTERSECTED )
        INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
    // remove previous intersection object reference
    //     by setting current intersection object to "nothing"
    INTERSECTED = null;
}
Wilt
  • 41,477
  • 12
  • 152
  • 203
  • 1
    Great solution, it works quite perfectly. Just need to take care about the right adjustment of the mouse coordinates used, and the possibly you will have to ignore some objects you hit (e.g. using the 'name' attribute). – Trantor Apr 06 '17 at 10:43
  • @Displee Did you try this code in r85? I am almost sure it will work, if not then the changes needed will be minimal. Did you try? If yes, what issues did you run into? – Wilt Aug 22 '17 at 07:27
  • @Displee I added a fiddle where the code is updated to r85. As I expected the code is hardly different from the page I linked to. – Wilt Aug 22 '17 at 15:57
6

As the code in the answer above is outdated (I tried it)... you can take a look at threex.domevents library. It did the trick in my case.

threex.domevents is a three.js extension which provide dom events inside your 3d scene. Always in a effort to stay close to usual pratices, the events name are the same as in DOM. The semantic is the same too, which makes it all very easy to learn. Currently, the available events are click, dblclick, mouseup, mousedown, mouseover and mouse out.

Here's an example that uses it:

http://bl.ocks.org/fabiovalse/2e8ae04bfce21af400e6

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480