/* Map 3D * 3D Model : Matthias Onestras * Code : Ronan Bonnet * */ import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.119.1/build/three.module.js'; import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.119.1/examples/jsm/controls/OrbitControls.js'; import { GLTFLoader } from 'https://cdn.jsdelivr.net/npm/three@0.119.1/examples/jsm/loaders/GLTFLoader.js'; import { RGBELoader } from 'https://cdn.jsdelivr.net/npm/three@0.119.1/examples/jsm/loaders/RGBELoader.js'; var container, stats, controls; var camera, scene, renderer; var raycaster, mouse; init(); render(); var height, width; function init() { container = document.createElement('div'); container.id = 'map3d'; height = document.querySelector('#maps').clientHeight; width = document.querySelector('#maps').clientWidth; var svg = document.querySelector('#maps #map'); document.querySelector('#maps').insertBefore(container, svg); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.75, 20000); camera.position.set(500,1500,500); scene = new THREE.Scene(); // LIGHTS let sol = new THREE.AmbientLight(0x404040, 1.0); scene.add(sol); var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 ); hemiLight.position.set( 0, 20, 0 ); scene.add( hemiLight ); var dirLight = new THREE.DirectionalLight( 0xffffff ); dirLight.position.set( - 3, 10, - 10 ); scene.add( dirLight ); //scene.background = new THREE.Color( 0xff0000 ); raycaster = new THREE.Raycaster(); mouse = new THREE.Vector2() // Loading screen const loadingManager = new THREE.LoadingManager( () => { const loadingScreen = document.getElementById('loading-screen'); loadingScreen.classList.add('fade-out'); loadingScreen.addEventListener('transitionend', onTransitionEnd); }); var loader = new GLTFLoader(loadingManager); loader.load('assets/images/map3D.glb', function(gltf) { var object = gltf.scene; gltf.scene.scale.set( 2, 2, 2 ); gltf.scene.position.x = 0; //Position (x = right+ left-) gltf.scene.position.y = 0; //Position (y = up+, down-) gltf.scene.position.z = 0; scene.add(gltf.scene); render(); }); renderer = new THREE.WebGLRenderer({ antialias: true, }); renderer.setClearColor( 0x000000 ); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, window.innerHeight * 0.75); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1; renderer.outputEncoding = THREE.sRGBEncoding; container.appendChild(renderer.domElement); var pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); controls = new OrbitControls(camera, renderer.domElement); controls.addEventListener('change', render); // use if there is no animation loop controls.minDistance = 0; controls.maxDistance = 3500; controls.enablePan = true; controls.target.set(-110, 300, 0); controls.maxPolarAngle = Math.PI/2.05; controls.update(); // Load Light var ambientLight = new THREE.AmbientLight( 0xcccccc ); scene.add( ambientLight ); var directionalLight = new THREE.DirectionalLight( 0xffffff ); directionalLight.position.set( 0, 1, 1 ).normalize(); scene.add( directionalLight ); window.addEventListener('resize', onWindowResize, false); renderer.domElement.addEventListener('click', onClick, false); // Mouse //renderer.domElement.addEventListener('mousemove', onMouseOver,false); renderer.domElement.addEventListener('touchend', onTouchEnd, false); // Smartphone } // Return all the selectors in the database function getSelectors() { let info = {}; let object = { "function": 'get_map_selectors', 'info': info, } return $.ajax({ url: 'admin/read', data: object, method: 'get', success: function(data){ } }); } /* * Show a screen page if the building on which was clicked is in the database * Display the name of the building and the description of the building */ function handleClickOnBuilding(x,y) { mouse.x = x; mouse.y = y; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects(scene.children, true); // If we clicked on a building if (intersects.length > 0) { // console.log(intersects); //console.log( intersects[0].object.name.toString()); var selector = intersects[0].object.name.toString().toLowerCase(); // Wait for getSelectors() to be done // If we do not wait, everything will be executed before checking what is inside the database $.when(getSelectors().done(function(data) { if (data.map(x => x.selector).includes(selector)){ // Use the same thing as the one for the 2D map $.alert({ title: 'Chargement...', content: function () { let self = this; let object = { "function": 'get_map_info', 'selector': selector, }; return $.ajax({ url: 'ajax/read', data: object, method: 'get' }).done(function (data) { if (data.length > 0) { self.setTitle(data[0]['title']); self.setContent(data[0]['description']); } else { self.setTitle('Erreur'); self.setContent('Une erreur est survenue') return; } }).fail(function(){ self.setContent('Something went wrong.'); }); }, }); } else { } })); } } // // When the user clicks on a building with a mouse // function onClick() { event.preventDefault(); height = document.querySelector('#maps').clientHeight; width = document.querySelector('#maps').clientWidth; const rect = renderer.domElement.getBoundingClientRect(); mouse.x = ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1; mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1; handleClickOnBuilding(mouse.x, mouse.y); } // // When the user clicks on a building on a smartphone // function onTouchEnd() { var clientX, clientY; clientX = event.changedTouches[0].clientX; clientY = event.changedTouches[0].clientY; const rect = renderer.domElement.getBoundingClientRect(); mouse.x = ( ( clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1; mouse.y = - ( ( clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1; handleClickOnBuilding(mouse.x, mouse.y); } function onMouseOver() { event.preventDefault(); height = document.querySelector('#maps').clientHeight; width = document.querySelector('#maps').clientWidth; const rect = renderer.domElement.getBoundingClientRect(); mouse.x = ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1; mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1; createTextOverBuilding(mouse.x,mouse.y) } function createTextOverBuilding(x,y) { mouse.x = x; mouse.y = y; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects(scene.children, true); // If we clicked on a building if (intersects.length > 0) { console.log(intersects); } } function makeLabelCanvas(baseWidth, size, name) { const borderSize = 2; const ctx = document.createElement('canvas').getContext('2d'); const font = `${size}px bold sans-serif`; ctx.font = font; // measure how long the name will be const textWidth = ctx.measureText(name).width; const doubleBorderSize = borderSize * 2; const width = baseWidth + doubleBorderSize; const height = size + doubleBorderSize; ctx.canvas.width = width; ctx.canvas.height = height; // need to set font again after resizing canvas ctx.font = font; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; ctx.fillStyle = 'blue'; ctx.fillRect(0, 0, width, height); // scale to fit but don't stretch const scaleFactor = Math.min(1, baseWidth / textWidth); ctx.translate(width / 2, height / 2); ctx.scale(scaleFactor, 1); ctx.fillStyle = 'white'; ctx.fillText(name, 0, 0); const labelBaseScale = 0.01; const label = new THREE.Sprite(labelMaterial); scene.add(label); label.position.y = head.position.y + headRadius + size * labelBaseScale; label.scale.x = canvas.width * labelBaseScale; label.scale.y = canvas.height * labelBaseScale; return ctx.canvas; } // // Auto-resize the canvas // function onWindowResize() { height = document.querySelector('#main-content .inner').clientHeight; width = document.querySelector('#main-content .inner').clientWidth; camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(width * 0.9, window.innerHeight * 0.75); render(); } function render() { renderer.render(scene, camera); } /* * When the model finished loading */ function onTransitionEnd( event) { event.target.remove(); }