Compare commits

..

5 Commits

Author SHA1 Message Date
d37e72390d Fix config for salvador 2026-04-17 18:12:23 +02:00
7a6e084a97 Disable shadows for clipping plane 2026-04-17 18:12:08 +02:00
337bcce0bb Better config defaults 2026-04-16 15:23:16 +02:00
12ae63d332 Draft ontology menu 2026-04-08 11:39:57 +02:00
556086117b Theater at top level in menu 2026-04-08 10:59:58 +02:00
7 changed files with 198 additions and 143 deletions

View File

@@ -18,6 +18,9 @@ export const config = {
scene : { scene : {
initialExposure: 0.7, initialExposure: 0.7,
autoLP: false, autoLP: false,
shadows: false,
initLightDir: [0.2,-0.3,-0.7],
initRotation: [0, 1.5, 0],
}, },
menu : { menu : {
//audioBtn1 //audioBtn1
@@ -29,13 +32,11 @@ export const config = {
uri : `${BASE_URI}/scenes/salvador/`, uri : `${BASE_URI}/scenes/salvador/`,
popup: theater1Popup, popup: theater1Popup,
coords: [45.4363, 12.3352], coords: [45.4363, 12.3352],
nodes: [ nodes: {
{
label: 'Teatro', label: 'Teatro',
model: "teatro_san_salvador_20250926.gltf", model: "teatro_san_salvador_20250926.gltf",
isMain: true, isMain: true,
}, },
],
pano: `pano/gradient.jpg`, pano: `pano/gradient.jpg`,
}, },
{ {
@@ -44,9 +45,10 @@ export const config = {
uri : `${BASE_URI}/scenes/ssgp/`, uri : `${BASE_URI}/scenes/ssgp/`,
popup: theater2Popup, popup: theater2Popup,
coords: [45.4401, 12.3408], coords: [45.4401, 12.3408],
nodes: [ nodes: {
{
label: 'Teatro', label: 'Teatro',
model: 'models/ssgp/Teatro_SSGP_Full_ConSottrazioni.glb',
opacity: 0.0,
children: [ children: [
/* /*
{ {
@@ -60,8 +62,6 @@ export const config = {
isMain: true, isMain: true,
opacity: 0.2, opacity: 0.2,
}, },
]
},
{ {
label: 'Sala / Auditorium', label: 'Sala / Auditorium',
children: [ children: [
@@ -157,7 +157,8 @@ export const config = {
}, },
] ]
} }
], ]
},
pano: `pano/gradient.jpg`, pano: `pano/gradient.jpg`,
} }
], ],

View File

@@ -2,6 +2,33 @@
* @module Ontology * @module Ontology
*/ */
/**
* @todo Temporarily returns domains and ontology labels only
* Traverse an ontology from its JSON description
* @param {String} jsonPath The path (URI) of the ontology JSON file
* @returns {Object}
*/
export async function traverseOntology(jsonPath) {
const ontology = await loadOntology(jsonPath);
const domains = [];
for (const k of Object.keys(ontology)) {
if (k === 'domains') {
for (const domainKey of Object.keys(ontology[k])) {
domains.push({
label: domainKey,
child: ontology[k][domainKey][0].label,
});
}
}
}
return {
ontology: ontology.ontology,
domains
};
}
/** /**
* Load an ontology from its JSON description * Load an ontology from its JSON description
* @param {String} jsonPath The path (URI) of the ontology JSON file * @param {String} jsonPath The path (URI) of the ontology JSON file

View File

@@ -34,13 +34,14 @@ function init () {
// All assets for this app are stored here // All assets for this app are stored here
ATON.setPathCollection('/a/scaenae/assets/'); ATON.setPathCollection('/a/scaenae/assets/');
// Initial light direction // Initial light direction
ATON.setMainLightDirection(new THREE.Vector3(0.2,-0.3,-0.7)); ATON.setMainLightDirection(new THREE.Vector3(...config.scene.initLightDir));
ATON.toggleShadows(true); ATON.toggleShadows(config.scene.shadows);
ATON.setExposure(config.scene.initialExposure); ATON.setExposure(config.scene.initialExposure);
// Open settings side panel when clicking on settings btn // Open settings side panel when clicking on settings btn
AppState.camera = ATON.Nav._camera; AppState.camera = ATON.Nav._camera;
AppState.renderer = ATON._renderer; AppState.renderer = ATON._renderer;
AppState.shadows = config.scene.shadows;
ATON.Nav.setUserControl(true); ATON.Nav.setUserControl(true);
} }
@@ -57,7 +58,7 @@ export function openScene (marker, nodes) {
ATON.setMainPanorama(marker.pano); ATON.setMainPanorama(marker.pano);
// TODO: hardcoded... // TODO: hardcoded...
AppState.initialRotation = new THREE.Vector3(0, 1.5, 0); AppState.initialRotation = new THREE.Vector3(...config.scene.initRotation);
ATON.setAutoLP(config.scene.autoLP); ATON.setAutoLP(config.scene.autoLP);
AppState.lightProbe = config.scene.autoLP; AppState.lightProbe = config.scene.autoLP;
@@ -74,10 +75,10 @@ function loadNodes(nodes) {
nodes.forEach(n => { nodes.forEach(n => {
let node = ATON.createSceneNode(n.label); let node = ATON.createSceneNode(n.label);
node.load(n.model); node.load(n.model);
node.setRotation(0, 1.5, 0); node.setRotation(...config.scene.initRotation);
// Apply any transparency before attaching to scene // Apply any transparency before attaching to scene
if (n.opacity) { if (n.opacity !== undefined && n.opacity !== null) {
node.setMaterial(new THREE.MeshPhongMaterial({ node.setMaterial(new THREE.MeshPhongMaterial({
transparent: true, transparent: true,
opacity: n.opacity, opacity: n.opacity,

View File

@@ -1,6 +1,7 @@
import AppState from "./state.js"; import AppState from "./state.js";
import { changeLightDirection, toggleAmbientOcclusion } from "./utils/environment.js"; import { changeLightDirection, toggleAmbientOcclusion } from "./utils/environment.js";
import { resetClipping, addClippingPlane } from "./utils/clipping.js"; import { resetClipping, addClippingPlane } from "./utils/clipping.js";
import { traverseOntology } from "./ontology.js";
/** /**
* @module UI * @module UI
@@ -17,7 +18,7 @@ const contentMenuTabs = `
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="media-tab" data-bs-toggle="tab" data-bs-target="#media" type="button" role="tab" aria-controls="media" aria-selected="true"> <button class="nav-link" id="media-tab" data-bs-toggle="tab" data-bs-target="#content" type="button" role="tab" aria-controls="media" aria-selected="true">
<i class="bi bi-diagram-3 me-1"></i> Contenuti <i class="bi bi-diagram-3 me-1"></i> Contenuti
</button> </button>
</li> </li>
@@ -25,8 +26,8 @@ const contentMenuTabs = `
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content ps-4 ms-3 overflow-y-auto"> <div class="tab-content ps-4 ms-3 overflow-y-auto">
<div class="tab-pane active p-3 ms-3" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div> <div class="tab-pane active p-3 ms-2" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div>
<div class="tab-pane p-3" id="media" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div> <div class="tab-pane pt-3" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div>
</div> </div>
`; `;
@@ -219,11 +220,12 @@ function createLightSlider(direction, label, range, step) {
/** /**
* Right-side main menu panel * Right-side main menu panel
* @param {String} triggerId - The menu button id * @param {String} triggerId - The menu button id
* @param {String} ontologyJsonPath
*/ */
function toggleContentMenu(triggerId) { function toggleContentMenu(triggerId, ontologyJsonPath) {
const btn = document.querySelector(`#${triggerId}`); const btn = document.querySelector(`#${triggerId}`);
btn.addEventListener('click', () => { btn.addEventListener('click', async () => {
ATON.UI.setSidePanelRight(); ATON.UI.setSidePanelRight();
ATON.UI.showSidePanel({header: 'Menu'}); ATON.UI.showSidePanel({header: 'Menu'});
// Append tabs, then tab panes // Append tabs, then tab panes
@@ -231,11 +233,12 @@ function toggleContentMenu(triggerId) {
ATON.UI.elSidePanel.appendChild(tabs.querySelector('#content-tabs')); ATON.UI.elSidePanel.appendChild(tabs.querySelector('#content-tabs'));
ATON.UI.elSidePanel.appendChild(tabs.querySelector('.tab-content')); ATON.UI.elSidePanel.appendChild(tabs.querySelector('.tab-content'));
buildLayersMenu(AppState.normalizedNodes, ATON.UI.elSidePanel.querySelector('#layer')); buildLayersMenu(AppState.normalizedNodes, ATON.UI.elSidePanel.querySelector('#layer'));
buildOntologyMenu(await traverseOntology(ontologyJsonPath), ATON.UI.elSidePanel.querySelector('#content'));
}); });
} }
/** /**
* @todo Don't rebuild it every time the side panel is shown... * @todo Don't rebuild it every time the side panel is shown...
* @param {Array} nodes The scenes nodes (IDs and status) * @param {Array} nodes The normalized scene nodes (IDs and status)
* @param {HTMLElement} sidePanel ATON's side panel element * @param {HTMLElement} sidePanel ATON's side panel element
*/ */
function buildLayersMenu(nodes, sidePanel) { function buildLayersMenu(nodes, sidePanel) {
@@ -253,13 +256,13 @@ function buildLayersMenu(nodes, sidePanel) {
const label = document.createElement('label'); const label = document.createElement('label');
label.className = "form-check-label"; label.className = "form-check-label";
label.textContent = node.label; label.textContent = node.id;
menuItem.appendChild(label); menuItem.appendChild(label);
sidePanel.appendChild(menuItem); sidePanel.appendChild(menuItem);
// Will this ever work?? // Will this ever work??
menuItem.addEventListener('change', event => toggleNode(node.label, event.target.checked)); menuItem.addEventListener('change', event => toggleNode(node.id, event.target.checked));
} }
/** /**
@@ -269,15 +272,42 @@ function buildLayersMenu(nodes, sidePanel) {
*/ */
const toggleNode = (id, status) => { const toggleNode = (id, status) => {
ATON.getSceneNode(id).toggle(status); ATON.getSceneNode(id).toggle(status);
AppState.normalizedNodes.find(n => n.label === id).active = status; AppState.normalizedNodes.find(n => n.id === id).active = status;
} }
} }
/** /**
* Initialize required components for scene UI * @see traverseOntology
* @param {Object} ontology The traversed ontology object (temp)
* @param {HTMLElement} sidePanel ATON's side panel element
*/ */
export function initUI() { function buildOntologyMenu(ontology, sidePanel) {
const list = document.createElement('ul');
list.className = 'list-group';
const mainNode = document.createElement('li');
mainNode.className = 'list-group-item';
mainNode.textContent = ontology.ontology;
const domainList = document.createElement('ul');
domainList.className = 'list-group';
for(let domain of ontology.domains) {
const domainItem = document.createElement('li');
domainItem.textContent = domain.label;
domainItem.className = 'list-group-item';
domainList.appendChild(domainItem);
}
mainNode.appendChild(domainList);
list.appendChild(mainNode);
sidePanel.appendChild(list);
}
/**
* Initialize required components for scene UI
* @param {String} ontologyJsonPath
*/
export async function initUI(ontologyJsonPath) {
toggleSettingsPanel('settings'); toggleSettingsPanel('settings');
toggleContentMenu('menu'); toggleContentMenu('menu', ontologyJsonPath);
toggleClipperBar('#clipper', '#clipper-bar'); toggleClipperBar('#clipper', '#clipper-bar');
} }

View File

@@ -18,6 +18,9 @@ function createClippingPlaneMesh (boundingSphere) {
new THREE.MeshBasicMaterial({ color: 0xffff00, opacity: 0.05, side: THREE.DoubleSide, transparent: true }) new THREE.MeshBasicMaterial({ color: 0xffff00, opacity: 0.05, side: THREE.DoubleSide, transparent: true })
); );
mesh.castShadow = false;
mesh.receiveShadow = false;
return mesh; return mesh;
} }

View File

@@ -62,10 +62,7 @@ function traverse(node, flatList, depth = 1) {
**/ **/
export function normalizeNodes (nodes) { export function normalizeNodes (nodes) {
let flatList = []; let flatList = [];
traverse(nodes, flatList);
for (let node of nodes) {
traverse(node, flatList);
}
return flatList; return flatList;
} }

View File

@@ -3,14 +3,10 @@ import { config } from "../../config.js";
import AppState from "../../js/state.js"; import AppState from "../../js/state.js";
import { normalizeNodes } from "../../js/utils/nodeUtils.js"; import { normalizeNodes } from "../../js/utils/nodeUtils.js";
import { initUI } from "../../js/ui.js"; import { initUI } from "../../js/ui.js";
import { loadOntology } from "../../js/ontology.js";
AppState.currentScene = 'ssgp'; AppState.currentScene = 'ssgp';
const marker = config.markers.find(m => m.id === 'ssgp'); const marker = config.markers.find(m => m.id === 'ssgp');
AppState.normalizedNodes = normalizeNodes(marker.nodes); AppState.normalizedNodes = normalizeNodes(marker.nodes);
openScene(marker, AppState.normalizedNodes); openScene(marker, AppState.normalizedNodes);
initUI(); initUI(location.pathname + '/ontology.json');
// DEBUG
console.debug(await loadOntology('ontology.json'));