import * as THREE from "three"; import OrbitControls from "~/common/three/orbital.js"; import GlobePoints from "~/common/three/points.json"; import CountryPoints from "~/common/three/countries.json"; const convertFlatCoordsToSphereCoords = ({ globe, x, y }) => { let latitude = ((x - globe.width) / globe.width) * -180; let longitude = ((y - globe.height) / globe.height) * -90; latitude = (latitude * Math.PI) / 180; longitude = (longitude * Math.PI) / 180; const radius = Math.cos(longitude) * globe.radius; return { x: Math.cos(latitude) * radius, y: Math.sin(longitude) * globe.radius, z: Math.sin(latitude) * radius, }; }; const convertLatLonToSphereCoords = ({ globe, lon, lat }) => { let latitude = (-lat * Math.PI) / 180; let longitude = (lon * Math.PI) / 180; const radius = Math.cos(longitude) * globe.radius; return { x: Math.cos(latitude) * radius, y: Math.sin(longitude) * globe.radius, z: Math.sin(latitude) * radius, }; }; const getViewportData = (width, height) => { const viewSize = height; const aspectRatio = width / height; return { viewSize: viewSize, aspectRatio: aspectRatio, left: (-aspectRatio * viewSize) / 2, right: (aspectRatio * viewSize) / 2, top: viewSize / 2, bottom: viewSize / -2, near: -2048, far: 2048, }; }; export default class GLComponent { constructor(props) { this.state = { mountedNodes: [], width: props.width, height: props.height, scene: undefined, renderer: undefined, container: undefined, camera: undefined, controls: undefined, measurements: undefined, ...props, }; } setState(newProps) { this.state = { ...this.state, ...newProps }; } unmount() { this.state.mountedNodes = null; this.state.width = null; this.state.height = null; this.state.scene = null; this.state.renderer = null; this.state.container = null; this.state.camera = null; this.state.controls = null; this.state.measurements = null; this.render = () => { console.log("Error: If this is getting called, that is bad."); }; } async mount() { this.state.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, }); this.state.renderer.shadowMap.enabled = true; this.state.renderer.shadowMap.type = THREE.PCFSoftShadowMap; this.state.renderer.physicallyBasedShading = true; this.state.renderer.setClearColor(0x000000, 0); this.state.renderer.setPixelRatio(window.devicePixelRatio); this.state.renderer.setSize(this.state.width, this.state.height); this.state.scene = new THREE.Scene(); this.state.container.appendChild(this.state.renderer.domElement); const viewport = getViewportData(this.state.width, this.state.height); this.state.camera = new THREE.OrthographicCamera( viewport.left, viewport.right, viewport.top, viewport.bottom, viewport.near, viewport.far ); this.state.camera.position.x = 0.2; this.state.camera.position.y = 0.25; this.state.camera.position.z = 0.2; this.state.controls = new OrbitControls( this.state.camera, this.state.renderer.domElement ); const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x888888, 0.9); this.state.scene.add(hemisphereLight); const ambientLight = new THREE.AmbientLight(0x888888, 0.7); this.state.scene.add(ambientLight); } firstRender() { const mergedGeometry = new THREE.Geometry(); const pointGeometry = new THREE.SphereGeometry(2, 1, 1); const pointMaterial = new THREE.MeshBasicMaterial({ color: "#0047FF", }); let { points } = GlobePoints; for (let point of points) { const { x, y, z } = convertFlatCoordsToSphereCoords({ x: point.x, y: point.y, globe: { radius: 188, width: 4098 / 2, height: 1968 / 2 }, }); pointGeometry.translate(x, y, z); mergedGeometry.merge(pointGeometry); pointGeometry.translate(-x, -y, -z); } const globeShape = new THREE.Mesh(mergedGeometry, pointMaterial); this.state.mountedNodes.push(globeShape); this.state.scene.add(globeShape); if (this.state.countries) { const mergedCountryGeometry = new THREE.Geometry(); const pointCountryGeometry = new THREE.SphereGeometry(4, 7, 7); const countryMaterial = new THREE.MeshBasicMaterial({ color: "black", }); points = CountryPoints.points; for (let point of points) { const { x, y, z } = convertLatLonToSphereCoords({ lat: point.lon, lon: point.lat, globe: { radius: 188, width: 4098 / 2, height: 1968 / 2 }, }); pointCountryGeometry.translate(x, y, z); mergedCountryGeometry.merge(pointCountryGeometry); pointCountryGeometry.translate(-x, -y, -z); } const countryShape = new THREE.Mesh( mergedCountryGeometry, countryMaterial ); this.state.mountedNodes.push(countryShape); this.state.scene.add(countryShape); } } axis = new THREE.Vector3(0, 1, 0).normalize(); render() { this.state.mountedNodes.forEach((n) => { const object = this.state.scene.getObjectById(n.id); object.rotateOnAxis(this.axis, Math.PI * 0.001); }); this.state.renderer.render(this.state.scene, this.state.camera); } }