Search code examples
javascriptthree.jstexturesmouseover

Change Color of Textures on MouseOver in Three.js


I've been able to implement a MouseOver Event to change the color of several of my objects in my application, except for the textures created using "disc.png" (these are the red dots in the result window). I want to be able to do the opposite and only change the color of the textures. What part of my code do I need to change in order to make that happen?

var renderer, scene, camera;
var control;
var stats;
var cameraControl;
//var radius = 7.7;
var radius = 15;
var group = new THREE.Object3D();
  
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector3(), INTERSECTED;
  
// Initialize the scene, camera and objects.
function init() {
  
  // To display anything, you need 3 things: (1) Scene, (2) Camera, (3) Renderer
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
  renderer = new THREE.WebGLRenderer();
  renderer.setClearColor(0x000000, 1.0);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMapEnabled = true;
  
  // Mars needs (1) geometry, (2) material, (3) mesh
  var sphereGeometry = new THREE.SphereGeometry(15, 60, 60);
  var sphereMaterial = createMarsMaterial();
  var marsMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
  marsMesh.name = 'mars';
  scene.add(marsMesh);
  
  // position and point the camera to the center of the scene
  camera.position.x = 25;
  camera.position.y = 26;
  camera.position.z = 30;
  camera.lookAt(scene.position);
  
  // add controls
  cameraControl = new THREE.OrbitControls(camera);
  
  // setup the control object for the control gui
  control = new function () {
    this.rotationSpeed = 0.000;
  };
  
  // add extras
  addControlGui(control);
  addStatsObject();
  
  // add the output of the renderer to the html element
  document.body.appendChild(renderer.domElement);
  
  // add a star field
  var starsGeometry = new THREE.Geometry();

  for ( var i = 0; i < 10000; i ++ ) {

    var star = new THREE.Vector3();
    star.x = THREE.Math.randFloatSpread( 2000 );
    star.y = THREE.Math.randFloatSpread( 2000 );
    star.z = THREE.Math.randFloatSpread( 2000 );

    starsGeometry.vertices.push( star )

  }

  var starsMaterial = new THREE.PointsMaterial( { color: 0xF9F9CF } )

  var starField = new THREE.Points( starsGeometry, starsMaterial );

  scene.add( starField );

  // start animating
  render();
  
}
  
function createMarsMaterial() {
  
  // 4096 is the maximum width for maps  
  var marsTexture = THREE.ImageUtils;
  marsTexture.crossOrigin = "";
  
  marsTexture = THREE.ImageUtils.loadTexture("https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/mars.jpg");
  var marsMaterial = new THREE.MeshBasicMaterial();
  marsMaterial.map = marsTexture;
  return marsMaterial;
}
  
function addControlGui(controlObject) {
  
  var gui = new dat.GUI();
  gui.add(controlObject, 'rotationSpeed', -0.01, 0.01);
}
  
function addStatsObject() {
  stats = new Stats();
  stats.setMode(0);
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.left = '0px';
  stats.domElement.style.top = '0px';
  document.body.appendChild(stats.domElement);
}
  
function render() {
  stats.update();
  cameraControl.update();
  scene.getObjectByName('mars').rotation.y += control.rotationSpeed; 
  
  renderer.render(scene, camera);
  requestAnimationFrame(render);
}
  
function handleResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}
  
window.onload = init;
window.addEventListener('resize', handleResize, false);
window.addEventListener( 'mousemove', onMouseMove, false );

  
 
var Landing_Sites = "https://tatornator12.github.io/classes/final-project/Mars_LandingSites2.json";

d3.queue()
    .defer(d3.json, Landing_Sites)
    .await(ready);

function ready(error, json) {
  if (error) throw error;

  scene.add(graticule = wireframe(graticule10(), new THREE.LineBasicMaterial({color: 0xaaaaaa})));
  
  json.features.forEach(function(d) { group.add(createDot(d)) });
  scene.add(group);
  d3.timer(function(t) {
   //group.rotation.y = Math.sin(t / 11000) * Math.PI / 3 - Math.PI / 2;
   // group.rotation.y = t / 10000;
    renderer.render(scene, camera);
  });

}

// Converts a point [longitude, latitude] in degrees to a THREE.Vector3.
function vertex(point) {
  var lambda = point[0] * Math.PI / 180,
      phi = point[1] * Math.PI / 180,
      cosPhi = Math.cos(phi);
  return new THREE.Vector3(
    radius * cosPhi * Math.cos(lambda),
    radius * cosPhi * Math.sin(lambda),
    radius * Math.sin(phi)
  );
}

function createDot(feature) {
  
  var landingSitesGeometry = new THREE.Geometry();
  var position = vertex(feature.geometry.coordinates);
  landingSitesGeometry.vertices.push( position );
  var landingSitesMaterial = new THREE.PointsMaterial({ size: 1.5, color: 0xff0000, map: THREE.ImageUtils.loadTexture("https://raw.githubusercontent.com/mrdoob/three.js/master/examples/textures/sprites/disc.png"), transparent: true });
 var dot = new THREE.Points(landingSitesGeometry, landingSitesMaterial);
  dot.position.set(position.x, position.y, position.z);
  return dot;
}

// Converts a GeoJSON MultiLineString in spherical coordinates to a THREE.LineSegments.
function wireframe(multilinestring, material) {
  var geometry = new THREE.Geometry;
  multilinestring.coordinates.forEach(function(line) {
    d3.pairs(line.map(vertex), function(a, b) {
      geometry.vertices.push(a, b);
    });
  });
  return new THREE.LineSegments(geometry, material);
}

// See https://github.com/d3/d3-geo/issues/95
function graticule10() {
  var epsilon = 1e-6,
      x1 = 180, x0 = -x1, y1 = 80, y0 = -y1, dx = 10, dy = 10,
      X1 = 180, X0 = -X1, Y1 = 90, Y0 = -Y1, DX = 90, DY = 360,
      x = graticuleX(y0, y1, 2.5), y = graticuleY(x0, x1, 2.5),
      X = graticuleX(Y0, Y1, 2.5), Y = graticuleY(X0, X1, 2.5);

  function graticuleX(y0, y1, dy) {
    var y = d3.range(y0, y1 - epsilon, dy).concat(y1);
    return function(x) { return y.map(function(y) { return [x, y]; }); };
  }

  function graticuleY(x0, x1, dx) {
    var x = d3.range(x0, x1 - epsilon, dx).concat(x1);
    return function(y) { return x.map(function(x) { return [x, y]; }); };
  }

  return {
    type: "MultiLineString",
    coordinates: d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X)
        .concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y))
        .concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return Math.abs(x % DX) > epsilon; }).map(x))
        .concat(d3.range(Math.ceil(y0 / dy) * dy, y1 + epsilon, dy).filter(function(y) { return Math.abs(y % DY) > epsilon; }).map(y))
  };
}
  
  function onMouseMove( event ) {

	// calculate mouse position in normalized device coordinates
	// (-1 to +1) for both components
  
  event.preventDefault();

	mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
	mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

   // update the picking ray with the camera and mouse position
	raycaster.setFromCamera( mouse, camera );
  
    
  // calculate objects intersecting the picking ray
	var intersects = raycaster.intersectObjects( scene.children );

  // 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( 0x00ff00 );
    }
  }
  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;
}
  
}
body {
  /* set margin to 0 and overflow to hidden, to go fullscreen */
  margin: 0;
  overflow: hidden;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/three.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/OrbitControls.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/dat.gui.min.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/stats.min.js"></script>


Solution

  • The bounding sphere of the Point is initialized to zero radius. Your mouse will NEVER hit it. I had to manually construct the bounding sphere.

    var renderer, scene, camera;
    var control;
    var stats;
    var cameraControl;
    //var radius = 7.7;
    var radius = 15;
    var group = new THREE.Object3D();
      
    var raycaster = new THREE.Raycaster();
    var mouse = new THREE.Vector3(), INTERSECTED;
      
    // Initialize the scene, camera and objects.
    function init() {
      
      // To display anything, you need 3 things: (1) Scene, (2) Camera, (3) Renderer
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
      renderer = new THREE.WebGLRenderer();
      renderer.setClearColor(0x000000, 1.0);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.shadowMapEnabled = true;
      
      // Mars needs (1) geometry, (2) material, (3) mesh
      var sphereGeometry = new THREE.SphereGeometry(15, 60, 60);
      var sphereMaterial = createMarsMaterial();
      var marsMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
      marsMesh.name = 'mars';
      scene.add(marsMesh);
      
      // position and point the camera to the center of the scene
      camera.position.x = 25;
      camera.position.y = 26;
      camera.position.z = 30;
      camera.lookAt(scene.position);
      
      // add controls
      cameraControl = new THREE.OrbitControls(camera);
      
      // setup the control object for the control gui
      control = new function () {
        this.rotationSpeed = 0.000;
      };
      
      // add extras
      addControlGui(control);
      addStatsObject();
      
      // add the output of the renderer to the html element
      document.body.appendChild(renderer.domElement);
      
      // add a star field
      var starsGeometry = new THREE.Geometry();
    
      for ( var i = 0; i < 10000; i ++ ) {
    
        var star = new THREE.Vector3();
        star.x = THREE.Math.randFloatSpread( 2000 );
        star.y = THREE.Math.randFloatSpread( 2000 );
        star.z = THREE.Math.randFloatSpread( 2000 );
    
        starsGeometry.vertices.push( star )
    
      }
    
      var starsMaterial = new THREE.PointsMaterial( { color: 0xF9F9CF } )
    
      var starField = new THREE.Points( starsGeometry, starsMaterial );
    
      scene.add( starField );
    
      // start animating
      render();
      
    }
      
    function createMarsMaterial() {
      
      // 4096 is the maximum width for maps  
      var marsTexture = THREE.ImageUtils;
      marsTexture.crossOrigin = "";
      
      marsTexture = THREE.ImageUtils.loadTexture("https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/mars.jpg");
      var marsMaterial = new THREE.MeshBasicMaterial();
      marsMaterial.map = marsTexture;
      return marsMaterial;
    }
      
    function addControlGui(controlObject) {
      
      var gui = new dat.GUI();
      gui.add(controlObject, 'rotationSpeed', -0.01, 0.01);
    }
      
    function addStatsObject() {
      stats = new Stats();
      stats.setMode(0);
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.left = '0px';
      stats.domElement.style.top = '0px';
      document.body.appendChild(stats.domElement);
    }
      
    function render() {
      stats.update();
      cameraControl.update();
      scene.getObjectByName('mars').rotation.y += control.rotationSpeed; 
      
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }
      
    function handleResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }
      
    window.onload = init;
    window.addEventListener('resize', handleResize, false);
    window.addEventListener( 'mousemove', onMouseMove, false );
    
      
     
    var Landing_Sites = "https://tatornator12.github.io/classes/final-project/Mars_LandingSites2.json";
    
    d3.queue()
        .defer(d3.json, Landing_Sites)
        .await(ready);
    
    function ready(error, json) {
      if (error) throw error;
    
      scene.add(graticule = wireframe(graticule10(), new THREE.LineBasicMaterial({color: 0xaaaaaa})));
      
      json.features.forEach(function(d) { group.add(createDot(d)) });
      scene.add(group);
      d3.timer(function(t) {
       //group.rotation.y = Math.sin(t / 11000) * Math.PI / 3 - Math.PI / 2;
       // group.rotation.y = t / 10000;
        renderer.render(scene, camera);
      });
      console.clear();
    
    }
    
    // Converts a point [longitude, latitude] in degrees to a THREE.Vector3.
    function vertex(point) {
      var lambda = point[0] * Math.PI / 180,
          phi = point[1] * Math.PI / 180,
          cosPhi = Math.cos(phi);
      return new THREE.Vector3(
        radius * cosPhi * Math.cos(lambda),
        radius * cosPhi * Math.sin(lambda),
        radius * Math.sin(phi)
      );
    }
    
    function createDot(feature) {
      
      var landingSitesGeometry = new THREE.Geometry();
      var position = vertex(feature.geometry.coordinates);
      landingSitesGeometry.vertices.push( position );
      var landingSitesMaterial = new THREE.PointsMaterial({ size: 1.5, color: 0xff0000, map: THREE.ImageUtils.loadTexture("https://raw.githubusercontent.com/mrdoob/three.js/master/examples/textures/sprites/disc.png"), transparent: true });
     var dot = new THREE.Points(landingSitesGeometry, landingSitesMaterial);
      dot.position.set(position.x, position.y, position.z);
      dot.geometry.boundingSphere = new THREE.Sphere();
      dot.geometry.boundingSphere.center.x = position.x;
      dot.geometry.boundingSphere.center.y = position.y;
      dot.geometry.boundingSphere.center.z = position.z;
      dot.geometry.boundingSphere.radius = 1.5;
      return dot;
    }
    
    // Converts a GeoJSON MultiLineString in spherical coordinates to a THREE.LineSegments.
    function wireframe(multilinestring, material) {
      var geometry = new THREE.Geometry;
      multilinestring.coordinates.forEach(function(line) {
        d3.pairs(line.map(vertex), function(a, b) {
          geometry.vertices.push(a, b);
        });
      });
      return new THREE.LineSegments(geometry, material);
    }
    
    // See https://github.com/d3/d3-geo/issues/95
    function graticule10() {
      var epsilon = 1e-6,
          x1 = 180, x0 = -x1, y1 = 80, y0 = -y1, dx = 10, dy = 10,
          X1 = 180, X0 = -X1, Y1 = 90, Y0 = -Y1, DX = 90, DY = 360,
          x = graticuleX(y0, y1, 2.5), y = graticuleY(x0, x1, 2.5),
          X = graticuleX(Y0, Y1, 2.5), Y = graticuleY(X0, X1, 2.5);
    
      function graticuleX(y0, y1, dy) {
        var y = d3.range(y0, y1 - epsilon, dy).concat(y1);
        return function(x) { return y.map(function(y) { return [x, y]; }); };
      }
    
      function graticuleY(x0, x1, dx) {
        var x = d3.range(x0, x1 - epsilon, dx).concat(x1);
        return function(y) { return x.map(function(x) { return [x, y]; }); };
      }
    
      return {
        type: "MultiLineString",
        coordinates: d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X)
            .concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y))
            .concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return Math.abs(x % DX) > epsilon; }).map(x))
            .concat(d3.range(Math.ceil(y0 / dy) * dy, y1 + epsilon, dy).filter(function(y) { return Math.abs(y % DY) > epsilon; }).map(y))
      };
    }
      
      function onMouseMove( event ) {
    
    	// calculate mouse position in normalized device coordinates
    	// (-1 to +1) for both components
      
      event.preventDefault();
    
    	mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    	mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
    
       // update the picking ray with the camera and mouse position
    	raycaster.setFromCamera( mouse, camera );
      
        
      // calculate objects intersecting the picking ray
    	var intersects = raycaster.intersectObjects( scene.children[3].children );
    
      // 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( 0x00ff00 );
        }
      }
      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;
    }
      
    }
    body {
      /* set margin to 0 and overflow to hidden, to go fullscreen */
      margin: 0;
      overflow: hidden;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/topojson.v2.min.js"></script>
    <script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/three.js"></script>
    <script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/OrbitControls.js"></script>
    <script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/dat.gui.min.js"></script>
    <script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/stats.min.js"></script>