I would like for the user to be able to hover over the little spheres inside the countries to receive information.
Using a raycaster seems to be the first step here, but it does not trigger upon mouse move. Also, it is not pointing where the mouse is. I believe this has something to do with the camera being rotated.
This is less important because I think I can figure it out, but it’s a bug in the code snippet below so I’m mentioning it:
I also would like for my globe to be rotating constantly, unless the user is controlling it with OrbitControls. Right now, when I try to use OrbitControls (dragging the mouse), the globe just zooms in and out- why is that? Does this have to do with the camera being updated constantly in d3.timer()?
I think this is the part of the code that’s most important.
d3.timer( function ( t ) {
theta += 0.1;
camera.position.x = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
camera.position.y = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
//camera.position.z = 100 * Math.cos( THREE.MathUtils.degToRad( theta ) );
camera.lookAt( scene.position );
camera.updateMatrixWorld();
controls.update();
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( earthPivot.children, false );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
console.log(intersects[0].object.name)
INTERSECTED = intersects[ 0 ].object;
}
} else {
INTERSECTED = null;
}
renderer.render( scene, camera );
} );
Please take a look at this snippet.
<div id='container'> </div>
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://unpkg.com/d3-array@1"></script>
<script src="https://unpkg.com/d3-collection@1"></script>
<script src="https://unpkg.com/d3-dispatch@1"></script>
<script src="https://unpkg.com/d3-request@1"></script>
<script src="https://unpkg.com/d3-timer@1"></script>
<script type='module'>
import * as THREE from "https://unpkg.com/[email protected]/build/three.module.js";
import { OrbitControls } from "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js";
var width = 650;
var height = 650;
var radius = 168,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera( 100, width / height, 1, 1000 ),
renderer = new THREE.WebGLRenderer( { alpha: true } ),
container = document.getElementById( 'container' ),
controls,
raycaster;
const pointer = new THREE.Vector2();
let INTERSECTED;
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', pointerMove );
let theta = 0;
scene.background = new THREE.Color( "rgb(20,20,20)" );
camera.position.set( 0, 0, 300 );
camera.lookAt( 0, 0, 0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( width, height );
container.appendChild( renderer.domElement );
let earthPivot = new THREE.Group();
d3.json( "https://raw.githubusercontent.com/alessiogmonti/alessiogmonti.github.io/master/Pages/Index/dataFranceModified.json", function ( error, topology ) {
if ( error ) throw error;
/// NO NEED TO LOOK AT ////////////////////////////////////////////
var countries = [];
var cones = [];
for ( var i = 0; i < topology.objects.countries.geometries.length; i ++ ) {
var rgb = [];
var newcolor;
for ( var j = 0; j < 3; j ++ ) {
rgb.push( Math.floor( Math.random() * 255 ) );
newcolor = 'rgb(' + rgb.join( ',' ) + ')';
}
var mesh = wireframe( topojson.mesh( topology, topology.objects.countries.geometries[ i ] ), new THREE.LineBasicMaterial( { color: newcolor, linewidth: 5 } ) );
countries.push( mesh );
scene.add( mesh );
mesh.geometry.computeBoundingBox();
var center = new THREE.Vector3();
mesh.geometry.boundingBox.getCenter( center );
mesh.add( earthPivot );
let height = 1;
const geometry = new THREE.SphereGeometry( height, 50, 36 );
const material = new THREE.MeshBasicMaterial( { color: newcolor } );
const cone = new THREE.Mesh( geometry, material );
cone.position.copy( center );
cone.position.setLength( radius + height );
cone.name = topology.objects.countries.geometries[ i ].properties[ 'name' ];
cones.push( cone );
earthPivot.add( cone );
/// NO NEED TO LOOK AT //////////////////////////////////////////////////////////////
}
controls = new OrbitControls( camera, renderer.domElement );
const helper = new THREE.CameraHelper( camera );
scene.add( helper );
const axesHelper = new THREE.AxesHelper( 50 );
scene.add( axesHelper );
d3.timer( function ( t ) {
theta += 0.1;
camera.position.x = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
camera.position.y = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
//camera.position.z = 100 * Math.cos( THREE.MathUtils.degToRad( theta ) );
camera.lookAt( scene.position );
camera.updateMatrixWorld();
controls.update();
// this code was to look at the interaction between pointer and camera
// const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
// const geometry = new THREE.BufferGeometry().setFromPoints( [pointer,camera.position] );
// const line = new THREE.Line( geometry, material );
// scene.add(line)
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( earthPivot.children, false );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
console.log(intersects[0].object.name)
INTERSECTED = intersects[ 0 ].object;
}
} else {
INTERSECTED = null;
}
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 pointerMove( event ){
pointer.x = ( event.clientX / width ) * 2 - 1;
pointer.y = - ( event.clientY / height ) * 2 + 1;
}
// Converts a GeoJSON MultiLineString in spherical coordinates to a THREE.LineSegments.
function wireframe( multilinestring, material ) {
var geometry = new THREE.BufferGeometry();
var pointsArray = new Array();
multilinestring.coordinates.forEach( function ( line ) {
d3.pairs( line.map( vertex ), function ( a, b ) {
pointsArray.push( a, b );
} );
} );
geometry.setFromPoints( pointsArray );
return new THREE.LineSegments( geometry, material );
}
</script>