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({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); }