site-accueil-insa/assets/map/script_map3d.js
2022-03-10 00:34:02 +01:00

378 lines
No EOL
9.5 KiB
JavaScript

/* Map 3D
* 3D Model : Matthias Onestras
* Code : Ronan Bonnet
*
*/
var rep = "assets/map/";
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';
var container, stats, controls;
var camera, scene, renderer;
var raycaster, mouse;
init();
render();
var height, width;
/**
* Initializes the 3D plan
* Creates and loads every needed things
*/
function init() {
//
// Creates HTML
//
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);
//
// Creates cameras and scene
//
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);
});
//
// Load the 3D model
//
var loader = new GLTFLoader(loadingManager);
loader.load(rep+'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
//
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
//
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 );
//
// EVENTS
//
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
}
/**
* Get all the selectors (buildings identifiers)
* @returns Array with all the selectors
*/
function getSelectors() {
let info = {};
let object = {
"function": 'get_map_selectors',
'info': info,
}
return $.ajax({
url: rep+'ajax.php',
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) {
var selector = intersects[0].object.name.toString().toLowerCase(); // Name of the building we clicked on
// 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: rep+'ajax.php',
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 {
}
}));
}
}
/**
* Get the position where the user clicked (mouse) on a building and process it
*/
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);
}
/**
* Get the position where the user clicked (smartphone) on a building and process it
*/
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);
}
/**
* Process something when the user moved the mouse over a building
* @todo add text over the building
*/
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-resizes the canvas when window size is updated
*/
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); // 0.9 and 0.75 so it looks comfortable on the screen
render();
}
/**
* Render the scene
*/
function render() {
renderer.render(scene, camera);
}
/*
* Remove the loader when the model loaded
*/
function onTransitionEnd( event) {
event.target.remove();
}