diff --git a/.gitignore b/.gitignore index 3968edc..0f13413 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ *.obj vendor/ +.vscode diff --git a/assets/icons/section_z.png b/assets/icons/section_z.png new file mode 100755 index 0000000..0d6700d Binary files /dev/null and b/assets/icons/section_z.png differ diff --git a/config.js b/config.js index 340c3dc..b9d6112 100644 --- a/config.js +++ b/config.js @@ -1,3 +1,5 @@ +const BASE_URI = "/a/scaenae"; + const theater1Popup = `

Teatro San Salvador, Venezia

@@ -30,24 +32,28 @@ export const config = { { id : "salvador", label : "Teatro San Salvador, Venezia", + uri : `${BASE_URI}/scenes/salvador/`, popup: theater1Popup, coords: [45.4363, 12.3352], model: "teatro_san_salvador_20250926.gltf", - pano: "pano/defsky-grass.jpg", + //pano: `${BASE_URI}/assets/pano/defsky-grass.jpg`, + pano: `pano/defsky-grass.jpg`, }, { id : "ssgp", label : "Teatro Santi Giovanni e Paolo, Venezia", + uri : `${BASE_URI}/scenes/ssgp/`, popup: theater2Popup, coords: [45.4401, 12.3408], - model: "SSGP.glb", - pano: "pano/defsky-grass.jpg", + model: `SSGP.glb`, + //pano: `${BASE_URI}/assets/pano/defsky-grass.jpg`, + pano: `pano/defsky-grass.jpg`, } ], map : { - center: [43.570833, 12.140278], - initialZoom : 8, + center: [45.30833, 12.240278], + initialZoom : 12, minZoom : 6, - maxZoom : 16 + maxZoom : 18 } } diff --git a/css/app.css b/css/app.css new file mode 100644 index 0000000..be5ad61 --- /dev/null +++ b/css/app.css @@ -0,0 +1,26 @@ +body { + height: 100vh; +} +.c-hand { + cursor: pointer; +} +input[type="checkbox"] { + cursor: pointer; +} +#clipper-bar .btn { + background-repeat: no-repeat; + background-position: center; + background-size: 80%; +} +#map { + height: 100%; +} +#clipX { + background-image: url('/a/scaenae/assets/icons/section_x.png'); +} +#clipY { + background-image: url('/a/scaenae/assets/icons/section_y.png'); +} +#clipZ { + background-image: url('/a/scaenae/assets/icons/section_z.png'); +} \ No newline at end of file diff --git a/index.html b/index.html index 6c166ea..0e3bc2c 100644 --- a/index.html +++ b/index.html @@ -27,10 +27,10 @@ - + - + @@ -39,6 +39,7 @@ + @@ -46,103 +47,13 @@ - - - -
-
-
- - - - - - - - - -
- Sezionamento - - - -
- - - -
- - - -
Disabilitare le ombre può migliorare le prestazioni
- -
- -
-
- - - +
diff --git a/js/main.js b/js/main.js index f889089..aa13d0c 100644 --- a/js/main.js +++ b/js/main.js @@ -1,5 +1,4 @@ import Map from "./map.js"; -import Scene from "./scene.js"; /* Main js entry for ATON web-app @@ -7,30 +6,10 @@ import Scene from "./scene.js"; // Realize our app let APP = ATON.App.realize(); -APP.UI = {}; - -/** - * - * @param {String} triggerSelector - Usually, the close modal trigger element(s) selector - */ -APP.UI.pauseAudio = function(triggerSelector) { - // What if more than one audio element is playing? - const audio = document.querySelector('audio'); - document.querySelectorAll(triggerSelector).forEach(el => { - el.addEventListener('click', () => audio.pause()); - }); - document.querySelector('.modal').addEventListener('blur', () => { - audio.pause(); - }); -} - - // APP.setup() is required for web-app initialization // You can place here UI setup (HTML), events handling, etc. APP.setup = ()=>{ Map.init('map'); - APP.UI.pauseAudio('[data-bs-dismiss="modal"'); - //APP.UI.showClipper('#clipper'); }; /* If you plan to use an update routine (executed continuously), you can place its logic here. diff --git a/js/map.js b/js/map.js index 05db3cc..1addfcc 100644 --- a/js/map.js +++ b/js/map.js @@ -1,7 +1,6 @@ // Global Leaflet and ATON import {config} from '../config.js'; -import Scene from './scene.js'; import AppState from './state.js'; const Map = {}; @@ -29,7 +28,7 @@ Map.init = function(mapContainerId) { const popup = this.domParser.parseFromString(marker.popup, 'text/html') .querySelector('div'); popup.querySelector('button').onclick = () => { - Scene.openScene(marker); + this.goToScene(marker.uri); map.getContainer().classList.toggle('d-none'); }; @@ -41,4 +40,12 @@ Map.init = function(mapContainerId) { AppState.map = map; } +/** + * + * @param {String} uri The scene's absolute URI (from config) + */ +Map.goToScene = function (uri) { + location.href = uri; +} + export default Map; \ No newline at end of file diff --git a/js/scene.js b/js/scene.js index 5789467..1ad4a5d 100644 --- a/js/scene.js +++ b/js/scene.js @@ -8,6 +8,20 @@ const Scene = {}; Scene.UI = {}; Scene.UI.domParser = new DOMParser; +/** + * + * @param {String} triggerSelector - Usually, the close modal trigger element(s) selector + */ +Scene.UI.pauseAudio = function(triggerSelector) { + // What if more than one audio element is playing? + const audio = document.querySelector('audio'); + document.querySelectorAll(triggerSelector).forEach(el => { + el.addEventListener('click', () => audio.pause()); + }); + document.querySelector('.modal').addEventListener('blur', () => { + audio.pause(); + }); +} /** * Resets the UI state (essentially hides the clipper toolbar if visible...) @@ -32,10 +46,11 @@ Scene.UI.toggleClipper = function(triggerSelector, targetSelector) { () => { toolbar.classList.toggle('d-none'); const aoCurrentState = AppState.ambientOcclusion; - if (!AppState.clipping.enabled) { + + if (!toolbar.classList.contains('d-none')) { AppState.clipping.enabled = true; - if (AppState.clipping.controls) AppState.clipping.controls.enabled = true; + //if (AppState.clipping.controls) AppState.clipping.controls.enabled = true; Scene.toggleAmbientOcclusion(false); @@ -46,7 +61,6 @@ Scene.UI.toggleClipper = function(triggerSelector, targetSelector) { trigger.className += ' border border-2 border-white'; toolbar.addEventListener('click', event => { - console.log('Clipping target:', event.target); if (event.target.id === 'clipX') { // Clip along X... Scene.addClippingPlane('x', -1); @@ -80,7 +94,6 @@ Scene.UI.toggleClipper = function(triggerSelector, targetSelector) { } }); } else { - AppState.clipping.enabled = false; Scene.resetClipping(); let noBorder = trigger.className.replace(/ border.*$/g, ''); trigger.className = noBorder; @@ -155,37 +168,40 @@ Scene.createClippingPlaneMesh = function (boundingSphere) { /** * * @param {THREE.Mesh} planeMesh + * @param {THREE.ArrowHelper} arrowHelper * @param {String} axis */ Scene.dragClipper = function(planeMesh, axis) { const controls = new THREE.DragControls( [planeMesh], - AppState.camera, - AppState.renderer.domElement, + ATON.Nav._camera, + ATON._renderer.domElement, ); const startPosition = new THREE.Vector3(); // Only move along the selected axis (exlude the others) const excludedAxes = ['x', 'y', 'z'].filter(a => a != axis); - controls.addEventListener('dragstart', function (event) { - startPosition.copy(event.object.position); - ATON.Nav.setUserControl(false); - }); + if (AppState.clipping.enabled && AppState.clipping.vector) { + controls.addEventListener('dragstart', function (event) { + startPosition.copy(event.object.position); + ATON.Nav.setUserControl(false); + }); - controls.addEventListener('drag', function(event) { - const point = event.object.position; - Scene.updateClipper(AppState.clipping.vector, point); - for (const a of excludedAxes) { - event.object.position[a] = startPosition[a]; - } - }); + controls.addEventListener('drag', function(event) { + const point = event.object.position; + Scene.updateClipper(AppState.clipping.vector, point); + for (const a of excludedAxes) { + event.object.position[a] = startPosition[a]; + } + }); - controls.addEventListener('dragend', function (event) { - ATON.Nav.setUserControl(true); - }); + controls.addEventListener('dragend', function (event) { + ATON.Nav.setUserControl(true); + }); - AppState.clipping.controls = controls; + AppState.clipping.controls = controls; + } } /** @@ -221,6 +237,7 @@ Scene.addClippingPlane = function(axis, orientation = -1) { * @param {?THREE.Vector3} point - The queried scene point */ Scene.activateClipper = function(vector, axis, point = null) { + ATON.enableClipPlanes(); Scene.updateClipper(vector, point); Scene.dragClipper(AppState.clipping.helper, axis); } @@ -231,22 +248,25 @@ Scene.activateClipper = function(vector, axis, point = null) { * @param {THREE.Vector3} point */ Scene.updateClipper = function(vector, point) { - // Normal of the clipping plane along the Y axis facing down - const normal = new THREE.Vector3(...vector).normalize(); - const plane = AppState.clipping.plane ?? ATON.addClipPlane(normal, point); - // Add a visible plane helper for the clipping plane - const visiblePlane = AppState.clipping.helper ?? Scene.createClippingPlaneMesh(AppState.clipping.boundingSphere); + // Useless guard... + if (vector) { + // Normal of the clipping plane along the axis facing down + const normal = new THREE.Vector3(...vector).normalize(); + const plane = AppState.clipping.plane ?? ATON.addClipPlane(normal, point); + // Add a visible plane helper for the clipping plane + const visiblePlane = AppState.clipping.helper ?? Scene.createClippingPlaneMesh(AppState.clipping.boundingSphere); - if (!AppState.clipping.helper) { - AppState.root.add(visiblePlane); - AppState.clipping.helper = visiblePlane; - } + if (!AppState.clipping.helper) { + AppState.root.add(visiblePlane); + AppState.clipping.helper = visiblePlane; + } - visiblePlane.position.copy(point); - visiblePlane.lookAt(point.clone().add(normal)); + visiblePlane.position.copy(point); + visiblePlane.lookAt(point.clone().add(normal)); - plane.setFromNormalAndCoplanarPoint(normal, point); - AppState.clipping.plane = plane; + plane.setFromNormalAndCoplanarPoint(normal, point); + AppState.clipping.plane = plane; + } } /** @@ -402,7 +422,7 @@ Scene.init = function() { ATON.UI.addBasicEvents(); ATON.UI.init(); // All assets for this app are stored here - ATON.setPathCollection('./assets/'); + ATON.setPathCollection('/a/scaenae/assets/'); // Initial light direction ATON.setMainLightDirection(new THREE.Vector3(0.2,-0.3,-0.7)); ATON.toggleShadows(true); @@ -416,37 +436,21 @@ Scene.init = function() { ATON.Nav.setUserControl(true); } -/** - * @param {String} btnId - The back-to-map button id - */ -Scene.closeScene = function() { - const scene = document.querySelector('#scene'); - scene.classList.toggle('d-none'); - - const canvas = ATON._renderer?.domElement; - Scene.resetClipping(); - AppState.root.clear(); - // Ensure GPU resources are freed... - ATON.renderPause(); - AppState.renderer.dispose(); - ATON._renderer.dispose(); - ATON.Nav._camera = undefined; - - Scene.UI.reset(); - document.querySelector('#map').classList.toggle('d-none'); - AppState.map.invalidateSize(); - AppState.resetSceneState(AppState.map); - // Remove ATON's canvas from the DOM - if (canvas && canvas.parentElement) { - canvas.parentElement.removeChild(canvas); - } -} Scene.resetClipping = function () { - AppState.clipping.controls?.dispose(); - AppState.clipping.controls = null; - ATON.disableClipPlanes(); + + console.warn('Resetting clipping!!'); + AppState.clipping.enabled = false; + ATON.disableClipPlanes(); + AppState.clipping.controls.deactivate(); + // Manually remove event listeners from DragControls!! + AppState.renderer.domElement.removeEventListener( 'pointermove', AppState.clipping.controls.onPointerMove ); + AppState.renderer.domElement.removeEventListener( 'pointerdown', AppState.clipping.controls.onPointerDown ); + AppState.renderer.domElement.removeEventListener( 'pointerup', AppState.clipping.controls.onPointerCancel ); + AppState.renderer.domElement.removeEventListener( 'pointerleave', AppState.clipping.controls.onPointerCancel ) + AppState.clipping.controls = null; + AppState.clipping.helper.removeFromParent(); AppState.root.remove(AppState.clipping.helper); AppState.clipping.helper = null; AppState.clipping.plane = null; @@ -458,27 +462,10 @@ Scene.resetClipping = function () { * @param {Object} marker - The marker object from config */ Scene.openScene = function(marker) { - //let canvas = document.querySelector('canvas'); - let scene = document.querySelector('#scene'); - Scene.init(); - // Button to go back to the map... - const btn = document.querySelector('#back'); - btn.addEventListener('click', () => { - Scene.closeScene('back'); - }); - Scene.UI.toggleClipper('#clipper', '#clipper-bar'); - scene.classList.toggle('d-none'); - ATON.renderResume(); - - if (!AppState.scenes.find(s => s.id === marker.id)) { - const newScene = {id: marker.id, active: false, current: true}; - AppState.scenes.push(newScene); - } - // Load 3D model then let mainNode = ATON.createSceneNode(marker.label); mainNode.load(marker.model); @@ -501,8 +488,6 @@ Scene.openScene = function(marker) { AppState.root = ATON.getRootScene(); // ATON.Node.getBound() returns a THREE.Sphere object AppState.clipping.boundingSphere = mainNode.getBound(); - - console.log(ATON.Nav._camera); } export default Scene; \ No newline at end of file diff --git a/js/state.js b/js/state.js index 1c4e00d..c4e6c0c 100644 --- a/js/state.js +++ b/js/state.js @@ -8,7 +8,6 @@ let AppState = { initialRotation: null, camera: null, renderer: null, - scenes : [], ambientOcclusion : true, shadows : true, lightProbe : false, @@ -19,6 +18,7 @@ let AppState = { controls: null, onDrag: null, helper : null, + arrow : null, boundingSphere: null, listeners: { button: false, @@ -39,7 +39,6 @@ let AppState = { initialRotation: null, camera: null, renderer: null, - scenes : [], ambientOcclusion : true, shadows : true, lightProbe : false, @@ -50,6 +49,7 @@ let AppState = { controls: null, onDrag: null, helper : null, + arrow : null, boundingSphere: null, listeners: { button: false, diff --git a/scenes/salvador/index.html b/scenes/salvador/index.html new file mode 100644 index 0000000..18b0938 --- /dev/null +++ b/scenes/salvador/index.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SCAENE + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ Sezionamento + + + +
+ + + +
+ + + +
Disabilitare le ombre può migliorare le prestazioni
+ +
+ +
+ diff --git a/scenes/salvador/index.js b/scenes/salvador/index.js new file mode 100644 index 0000000..12bb985 --- /dev/null +++ b/scenes/salvador/index.js @@ -0,0 +1,4 @@ +import Scene from "../../js/scene.js"; +import { config } from "../../config.js"; + +Scene.openScene(config.markers.find(m => m.id === 'salvador')); \ No newline at end of file diff --git a/scenes/ssgp/index.html b/scenes/ssgp/index.html new file mode 100644 index 0000000..18b0938 --- /dev/null +++ b/scenes/ssgp/index.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SCAENE + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ Sezionamento + + + +
+ + + +
+ + + +
Disabilitare le ombre può migliorare le prestazioni
+ +
+ +
+ diff --git a/scenes/ssgp/index.js b/scenes/ssgp/index.js new file mode 100644 index 0000000..01809df --- /dev/null +++ b/scenes/ssgp/index.js @@ -0,0 +1,4 @@ +import Scene from "../../js/scene.js"; +import { config } from "../../config.js"; + +Scene.openScene(config.markers.find(m => m.id === 'ssgp')); \ No newline at end of file