2
votes

I'm a complete Three.js novice and new to javascript in general. I'm trying to test out some mouse event interactions. I tried many things, but couldn't get any interaction happening, so I just grabbed some Javascript off of the following page, adjusting it so that it's a 'MouseDown', rather than a 'MouseOver' event and generally simplifying it so that there is no rotations etc:-

https://threejs.org/examples/#webgl_interactive_cubes

However, the resulting page has an issue that I don't understand. Whenever the page is refreshed the cube is automatically coloured red, but this is only supposed to happen when one clicks on it. If I reduce the size of the cube, then this resolves the issue.

I did a console.log(INTERSECTIONS) and found that on refresh the value was '5', but every time I clicked on the cube after that, the value is '1'. I would have expected the value to be '0' on refresh since I've not actually clicked on anything yet? Knowing this, I could easily add an additional 'if' statement that goes something like:

if (intersects.length > 4)
      {
     INTERSECTED.material.emissive.setHex( 0x111111 );
      }

And this resolves the issue somewhat, but I'm still wondering why this issue occurs in the first place?

How would I simplify this code further so that I just get a base template from which I can create future mouse click event type pages? For example, If I wanted the cube to rotate on click, this doesn't seem to work at the moment?

The code is as follows:

<script src="js/three.js"></script>
<script>

  var camera, scene, raycaster, renderer;
  var mouse = new THREE.Vector2(), INTERSECTED;
  var radius = 100;
  init();
  animate();

  function init() {

    container = document.createElement( 'div' );
    document.body.appendChild( container );

    var info = document.createElement( 'div' );
    info.style.position = 'absolute';
    info.style.top = '10px';
    info.style.width = '100%';
    info.style.textAlign = 'center';


    camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
    scene = new THREE.Scene();

    scene.add( new THREE.AmbientLight( 0xffffff, 0.2 ) );

    var light = new THREE.DirectionalLight( 0xffffff, 2 );

    light.position.set( 30, 10, 1 ).normalize();
    scene.add( light );

    var cubeGeometry = new THREE.BoxGeometry(20,20,20);
    var cubeMaterial = new THREE.MeshLambertMaterial({color: 0x999999, wireframe: false});  
    var object = new THREE.Mesh(cubeGeometry, cubeMaterial);

      object.position.x = 0;  
      object.position.y = 0;  
      object.position.z = 0;

      scene.add( object );



    raycaster = new THREE.Raycaster();
    renderer = new THREE.WebGLRenderer();
    renderer.setClearColor( 0xf0f0f0 );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.sortObjects = false;
    container.appendChild(renderer.domElement);


    document.addEventListener( 'mousedown', onDocumentMouseDown, false );   
    window.addEventListener( 'resize', onWindowResize, false );

  }
  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
  }
  function onDocumentMouseDown( event ) {
    event.preventDefault();
    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
  }

  function animate() {
    requestAnimationFrame( animate );
    render();
  }

  camera.position.x = -30;  
  camera.position.y = 30;  
  camera.position.z = 30;  
  camera.lookAt( scene.position );
  camera.updateMatrixWorld();

  function render() 
  {
    // find intersections
    raycaster.setFromCamera( mouse, camera );
    var intersects = raycaster.intersectObjects( scene.children );
    if ( intersects.length > 0 ) {
      if ( INTERSECTED != intersects[ 0 ].object ) {
        if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
        INTERSECTED = intersects[ 0 ].object;
        INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
        INTERSECTED.material.emissive.setHex( 0xff0000 );
         console.log(intersects.length);
      }
    } else {
      if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
      INTERSECTED = null;
    }
    renderer.render( scene, camera );

  }
</script>
1

1 Answers

1
votes

When you declare var mouse = new THREE.Vector2(), it means that you create a vector with values {x:0, y:0} (which means that your mouse positioned in the center of the screen). Then you initialize the scene with init(), setting there the cube, and then you start rendering with animate().

Now, your mouse in the center of the scneen and on every render (in your render() function) you check for intersection. It means that while your mouse in the middle of the screen you'll have positive result of intersection. When you click somewhere out of the cube, you set a new point of your mouse, thus intersection result is negative.

As you want to interact with mouse clicking, then it's better to move the block of code which checks intersection from render() to onDocumentMouseDown(event).

// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
  if ( INTERSECTED != intersects[ 0 ].object ) {
    if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
    INTERSECTED = intersects[ 0 ].object;
    INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
    INTERSECTED.material.emissive.setHex( 0xff0000 );
     console.log(intersects.length);
  }
} else {
  if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
  INTERSECTED = null;
}

So you'll check for intersection only when you click the scene.

jsfiddle example