import * as THREE from 'https://cdn.skypack.dev/three@0.136.0/build/three.module.js'; import { OrbitControls } from 'https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js'; export function init( data ) { /* eslint-disable-line no-unused-vars */ const { canvas, inputElement } = data; const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } ); const fov = 75; const aspect = 2; // the canvas default const near = 0.1; const far = 100; const camera = new THREE.PerspectiveCamera( fov, aspect, near, far ); camera.position.z = 4; const controls = new OrbitControls( camera, inputElement ); controls.target.set( 0, 0, 0 ); controls.update(); const scene = new THREE.Scene(); { const color = 0xFFFFFF; const intensity = 1; const light = new THREE.DirectionalLight( color, intensity ); light.position.set( - 1, 2, 4 ); scene.add( light ); } const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth ); function makeInstance( geometry, color, x ) { const material = new THREE.MeshPhongMaterial( { color, } ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube ); cube.position.x = x; return cube; } const cubes = [ makeInstance( geometry, 0x44aa88, 0 ), makeInstance( geometry, 0x8844aa, - 2 ), makeInstance( geometry, 0xaa8844, 2 ), ]; class PickHelper { constructor() { this.raycaster = new THREE.Raycaster(); this.pickedObject = null; this.pickedObjectSavedColor = 0; } pick( normalizedPosition, scene, camera, time ) { // restore the color if there is a picked object if ( this.pickedObject ) { this.pickedObject.material.emissive.setHex( this.pickedObjectSavedColor ); this.pickedObject = undefined; } // cast a ray through the frustum this.raycaster.setFromCamera( normalizedPosition, camera ); // get the list of objects the ray intersected const intersectedObjects = this.raycaster.intersectObjects( scene.children ); if ( intersectedObjects.length ) { // pick the first object. It's the closest one this.pickedObject = intersectedObjects[ 0 ].object; // save its color this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex(); // set its emissive color to flashing red/yellow this.pickedObject.material.emissive.setHex( ( time * 8 ) % 2 > 1 ? 0xFFFF00 : 0xFF0000 ); } } } const pickPosition = { x: - 2, y: - 2 }; const pickHelper = new PickHelper(); clearPickPosition(); function resizeRendererToDisplaySize( renderer ) { const canvas = renderer.domElement; const width = inputElement.clientWidth; const height = inputElement.clientHeight; const needResize = canvas.width !== width || canvas.height !== height; if ( needResize ) { renderer.setSize( width, height, false ); } return needResize; } function render( time ) { time *= 0.001; if ( resizeRendererToDisplaySize( renderer ) ) { camera.aspect = inputElement.clientWidth / inputElement.clientHeight; camera.updateProjectionMatrix(); } cubes.forEach( ( cube, ndx ) => { const speed = 1 + ndx * .1; const rot = time * speed; cube.rotation.x = rot; cube.rotation.y = rot; } ); pickHelper.pick( pickPosition, scene, camera, time ); renderer.render( scene, camera ); requestAnimationFrame( render ); } requestAnimationFrame( render ); function getCanvasRelativePosition( event ) { const rect = inputElement.getBoundingClientRect(); return { x: event.clientX - rect.left, y: event.clientY - rect.top, }; } function setPickPosition( event ) { const pos = getCanvasRelativePosition( event ); pickPosition.x = ( pos.x / inputElement.clientWidth ) * 2 - 1; pickPosition.y = ( pos.y / inputElement.clientHeight ) * - 2 + 1; // note we flip Y } function clearPickPosition() { // unlike the mouse which always has a position // if the user stops touching the screen we want // to stop picking. For now we just pick a value // unlikely to pick something pickPosition.x = - 100000; pickPosition.y = - 100000; } inputElement.addEventListener( 'mousemove', setPickPosition ); inputElement.addEventListener( 'mouseout', clearPickPosition ); inputElement.addEventListener( 'mouseleave', clearPickPosition ); inputElement.addEventListener( 'touchstart', ( event ) => { // prevent the window from scrolling event.preventDefault(); setPickPosition( event.touches[ 0 ] ); }, { passive: false } ); inputElement.addEventListener( 'touchmove', ( event ) => { setPickPosition( event.touches[ 0 ] ); } ); inputElement.addEventListener( 'touchend', clearPickPosition ); }