Structured layers menu (WIP)
This commit is contained in:
84
config.js
84
config.js
@@ -44,15 +44,13 @@ 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: [
|
||||||
groups: [
|
|
||||||
{
|
{
|
||||||
label: 'Teatro',
|
label: 'Teatro',
|
||||||
layers: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Struttura complessiva',
|
label: 'Struttura complessiva',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Full_ConSottrazioni.glb',
|
model: 'models/ssgp/Teatro_SSGP_Full_ConSottrazioni.glb',
|
||||||
//isMain: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Involucro',
|
label: 'Involucro',
|
||||||
@@ -64,30 +62,35 @@ export const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Sala / Auditorium',
|
label: 'Sala / Auditorium',
|
||||||
layers: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Peplano / Platea',
|
label: 'Peplano / Platea',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Platea_peplano.glb',
|
model: 'models/ssgp/Teatro_SSGP_Platea_peplano.glb',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Ordine 1',
|
label: 'Ordini di palchi',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Ordine1.glb',
|
children: [
|
||||||
},
|
{
|
||||||
{
|
label: 'Ordine 1',
|
||||||
label: 'Ordine 2',
|
model: 'models/ssgp/Teatro_SSGP_Ordine1.glb',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Ordine2.glb',
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Ordine 2',
|
||||||
label: 'Ordine 3',
|
model: 'models/ssgp/Teatro_SSGP_Ordine2.glb',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Ordine3.glb',
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Ordine 3',
|
||||||
label: 'Ordine 4',
|
model: 'models/ssgp/Teatro_SSGP_Ordine3.glb',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Ordine4.glb',
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Ordine 4',
|
||||||
label: 'Ordine 5',
|
model: 'models/ssgp/Teatro_SSGP_Ordine4.glb',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Ordine5.glb',
|
},
|
||||||
|
{
|
||||||
|
label: 'Ordine 5',
|
||||||
|
model: 'models/ssgp/Teatro_SSGP_Ordine5.glb',
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Parapetto',
|
label: 'Parapetto',
|
||||||
@@ -101,7 +104,7 @@ export const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Scena',
|
label: 'Scena',
|
||||||
layers: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Palcoscenico',
|
label: 'Palcoscenico',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Palcoscenico.glb',
|
model: 'models/ssgp/Teatro_SSGP_Palcoscenico.glb',
|
||||||
@@ -122,36 +125,37 @@ export const config = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Spazi tecnici',
|
label: 'Spazi tecnici',
|
||||||
layers: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Spazio tecnico superiore',
|
label: 'Spazio tecnico superiore',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Layer_Spazio_tecnico_sup_soffitta.glb',
|
model: 'models/ssgp/Teatro_SSGP_Layer_Spazio_tecnico_sup_soffitta.glb',
|
||||||
},
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Graticcia',
|
label: 'Graticcia',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Graticcia.glb',
|
model: 'models/ssgp/Teatro_SSGP_Graticcia.glb',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Ballatoio',
|
label: 'Ballatoio',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Ballatoio.glb',
|
model: 'models/ssgp/Teatro_SSGP_Ballatoio.glb',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Spazio tecnico inferiore',
|
label: 'Spazio tecnico inferiore',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Spazio_tecnico_inf.glb',
|
model: 'models/ssgp/Teatro_SSGP_Spazio_tecnico_inf.glb',
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Orchestra',
|
label: 'Orchestra',
|
||||||
layers: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Fossa orchestra',
|
label: 'Fossa orchestra',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Fossa_orchestra.glb',
|
model: 'models/ssgp/Teatro_SSGP_Fossa_orchestra.glb',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
|
||||||
pano: `pano/gradient.jpg`,
|
pano: `pano/gradient.jpg`,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
18
js/scene.js
18
js/scene.js
@@ -232,18 +232,21 @@ Scene.resetClipping = function () {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {Object} marker - The marker object from config
|
* @param {Object} marker - The marker object from config
|
||||||
|
* @param {Object[]} nodes - The flat list of nodes for this scene
|
||||||
*/
|
*/
|
||||||
Scene.openScene = function(marker) {
|
Scene.openScene = function(marker, nodes) {
|
||||||
Scene.init();
|
Scene.init();
|
||||||
|
// TODO: move to init logic
|
||||||
Scene.UI.toggleClipperBar('#clipper', '#clipper-bar');
|
Scene.UI.toggleClipperBar('#clipper', '#clipper-bar');
|
||||||
|
|
||||||
|
// Filter nodes with models first
|
||||||
|
nodes = nodes.filter(n => n.model);
|
||||||
// Load 3D models and create nodes
|
// Load 3D models and create nodes
|
||||||
Scene.loadNodes(marker.nodes);
|
Scene.loadNodes(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(0, 1.5, 0);
|
||||||
//Scene.showEdges(mainNode);
|
|
||||||
|
|
||||||
ATON.setAutoLP(config.scene.autoLP);
|
ATON.setAutoLP(config.scene.autoLP);
|
||||||
AppState.lightProbe = config.scene.autoLP;
|
AppState.lightProbe = config.scene.autoLP;
|
||||||
@@ -254,7 +257,7 @@ Scene.openScene = function(marker) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Array} nodes
|
* @param {Object[]} nodes
|
||||||
*/
|
*/
|
||||||
Scene.loadNodes = function (nodes) {
|
Scene.loadNodes = function (nodes) {
|
||||||
nodes.forEach(n => {
|
nodes.forEach(n => {
|
||||||
@@ -264,7 +267,11 @@ Scene.loadNodes = function (nodes) {
|
|||||||
|
|
||||||
// Apply any transparency before attaching to scene
|
// Apply any transparency before attaching to scene
|
||||||
if (n.opacity) {
|
if (n.opacity) {
|
||||||
node.setMaterial(new THREE.MeshPhongMaterial({transparent: true, opacity: n.opacity, color: '#fff'}));
|
node.setMaterial(new THREE.MeshPhongMaterial({
|
||||||
|
transparent: true,
|
||||||
|
opacity: n.opacity,
|
||||||
|
color: '#fff'
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
node.attachToRoot();
|
node.attachToRoot();
|
||||||
@@ -275,7 +282,6 @@ Scene.loadNodes = function (nodes) {
|
|||||||
AppState.clipping.boundingSphere = node.getBound();
|
AppState.clipping.boundingSphere = node.getBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AppState.nodes.push({id: n.label, active: true});
|
AppState.nodes.push({id: n.label, active: true});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ let AppState = {
|
|||||||
root: null,
|
root: null,
|
||||||
// {id: String, active: Boolean}
|
// {id: String, active: Boolean}
|
||||||
nodes: [],
|
nodes: [],
|
||||||
|
normalizedNodes: [],
|
||||||
mainNodeId: null,
|
mainNodeId: null,
|
||||||
currentScene: null,
|
currentScene: null,
|
||||||
sceneHasAudio: false,
|
sceneHasAudio: false,
|
||||||
|
|||||||
37
js/ui.js
37
js/ui.js
@@ -9,7 +9,7 @@ const UI = {};
|
|||||||
UI.domParser = new DOMParser;
|
UI.domParser = new DOMParser;
|
||||||
|
|
||||||
UI.contentMenuTabs = `
|
UI.contentMenuTabs = `
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul class="nav nav-pills" id="content-tabs" role="tablist">
|
<ul class="nav nav-pills" id="content-tabs" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link active" id="layer-tab" data-bs-toggle="tab" data-bs-target="#layer" type="button" role="tab" aria-controls="layer" aria-selected="false">
|
<button class="nav-link active" id="layer-tab" data-bs-toggle="tab" data-bs-target="#layer" type="button" role="tab" aria-controls="layer" aria-selected="false">
|
||||||
@@ -24,8 +24,8 @@ UI.contentMenuTabs = `
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!-- Tab panes -->
|
<!-- Tab panes -->
|
||||||
<div class="tab-content">
|
<div class="tab-content ps-4 ms-3" style="overflow: auto">
|
||||||
<div class="tab-pane active p-3" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div>
|
<div class="tab-pane active p-3 ms-3" 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 p-3" id="media" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -230,7 +230,7 @@ UI.toggleContentMenu = function(triggerId) {
|
|||||||
const tabs = this.domParser.parseFromString(UI.contentMenuTabs, 'text/html');
|
const tabs = this.domParser.parseFromString(UI.contentMenuTabs, 'text/html');
|
||||||
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'));
|
||||||
this.buildLayersMenu(AppState.nodes, ATON.UI.elSidePanel.querySelector('#layer'));
|
this.buildLayersMenu(AppState.normalizedNodes, ATON.UI.elSidePanel.querySelector('#layer'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -240,19 +240,26 @@ UI.toggleContentMenu = function(triggerId) {
|
|||||||
*/
|
*/
|
||||||
UI.buildLayersMenu = function(nodes, sidePanel) {
|
UI.buildLayersMenu = function(nodes, sidePanel) {
|
||||||
for(let node of nodes) {
|
for(let node of nodes) {
|
||||||
const checkboxStr = `
|
const menuItem = document.createElement('div');
|
||||||
<div class="form-check form-switch ms-4 mt-2">
|
menuItem.className = `form-check form-switch ms-${node.depth} ps-${node.depth} mt-2`;
|
||||||
<input class="form-check-input" type="checkbox" ${node.active ? 'checked' : ''} role="switch" title="Mostra / nascondi layer">
|
const checkbox = document.createElement('input');
|
||||||
<label class="form-check-label" for="aoSwitch">${node.id}</label>
|
checkbox.type = 'checkbox';
|
||||||
</div>
|
checkbox.className = "form-check-input";
|
||||||
`;
|
checkbox.checked = node.active;
|
||||||
|
checkbox.role = 'switch';
|
||||||
|
checkbox.title = "Mostra / nascondi layer";
|
||||||
|
|
||||||
let element = this.domParser.parseFromString(checkboxStr, 'text/html');
|
menuItem.appendChild(checkbox);
|
||||||
element = element.querySelector('div.form-check');
|
|
||||||
|
|
||||||
sidePanel.appendChild(element);
|
const label = document.createElement('label');
|
||||||
|
label.className = "form-check-label";
|
||||||
|
label.textContent = node.label;
|
||||||
|
|
||||||
|
menuItem.appendChild(label);
|
||||||
|
|
||||||
|
sidePanel.appendChild(menuItem);
|
||||||
// Will this ever work??
|
// Will this ever work??
|
||||||
element.addEventListener('change', event => toggleNode(node.id, event.target.checked));
|
menuItem.addEventListener('change', event => toggleNode(node.label, event.target.checked));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,7 +269,7 @@ UI.buildLayersMenu = function(nodes, sidePanel) {
|
|||||||
*/
|
*/
|
||||||
const toggleNode = (id, status) => {
|
const toggleNode = (id, status) => {
|
||||||
ATON.getSceneNode(id).toggle(status);
|
ATON.getSceneNode(id).toggle(status);
|
||||||
AppState.nodes.find(n => n.id === id).active = status;
|
AppState.normalizedNodes.find(n => n.label === id).active = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
js/utils/nodeUtils.js
Normal file
42
js/utils/nodeUtils.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} node
|
||||||
|
* @param {Object[]} flatList
|
||||||
|
* @param {Number} depth
|
||||||
|
*/
|
||||||
|
function traverse(node, flatList, depth = 1) {
|
||||||
|
const normNode = {label: node.label};
|
||||||
|
if (node.model) {
|
||||||
|
normNode.model = node.model;
|
||||||
|
}
|
||||||
|
flatList.push({
|
||||||
|
...normNode,
|
||||||
|
depth
|
||||||
|
});
|
||||||
|
if (node.children) {
|
||||||
|
depth++;
|
||||||
|
for(let child of node.children) {
|
||||||
|
traverse(child, flatList, depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a flat list of nodes from
|
||||||
|
* the nested structure in config
|
||||||
|
* @param {Array} nodes
|
||||||
|
* @returns {Object[]} A flat list of nodes
|
||||||
|
**/
|
||||||
|
export function normalizeNodes (nodes) {
|
||||||
|
let flatNodes = [];
|
||||||
|
|
||||||
|
for (let node of nodes) {
|
||||||
|
traverse(node, flatNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return flatNodes;
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import Scene from "../../js/scene.js";
|
import Scene from "../../js/scene.js";
|
||||||
import { config } from "../../config.js";
|
import { config } from "../../config.js";
|
||||||
import AppState from "../../js/state.js";
|
import AppState from "../../js/state.js";
|
||||||
|
import { normalizeNodes } from "../../js/utils/nodeUtils.js";
|
||||||
|
|
||||||
AppState.currentScene = 'salvador';
|
AppState.currentScene = 'salvador';
|
||||||
AppState.sceneHasAudio = true;
|
AppState.sceneHasAudio = true;
|
||||||
|
const marker = config.markers.find(m => m.id === 'salvador');
|
||||||
|
AppState.normalizedNodes = normalizeNodes(marker.nodes);
|
||||||
|
|
||||||
|
AppState.normalizedNodes.forEach(node => node.active = true);
|
||||||
|
|
||||||
|
Scene.openScene(marker, AppState.normalizedNodes);
|
||||||
|
|
||||||
Scene.openScene(config.markers.find(m => m.id === 'salvador'));
|
|
||||||
Scene.UI.pauseAudio('[data-bs-dismiss="modal"]');
|
Scene.UI.pauseAudio('[data-bs-dismiss="modal"]');
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import Scene from "../../js/scene.js";
|
import Scene from "../../js/scene.js";
|
||||||
import { config } from "../../config.js";
|
import { config } from "../../config.js";
|
||||||
import AppState from "../../js/state.js";
|
import AppState from "../../js/state.js";
|
||||||
|
import { normalizeNodes } from "../../js/utils/nodeUtils.js";
|
||||||
|
|
||||||
AppState.currentScene = 'ssgp';
|
AppState.currentScene = 'ssgp';
|
||||||
Scene.openScene(config.markers.find(m => m.id === 'ssgp'));
|
const marker = config.markers.find(m => m.id === 'ssgp');
|
||||||
|
AppState.normalizedNodes = normalizeNodes(marker.nodes);
|
||||||
|
|
||||||
|
AppState.normalizedNodes.forEach(node => node.active = true);
|
||||||
|
|
||||||
|
Scene.openScene(marker, AppState.normalizedNodes);
|
||||||
|
|||||||
Reference in New Issue
Block a user