Compare commits
4 Commits
becb0865b9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| cabfe687e2 | |||
| 26733a4b84 | |||
| 380999ff4b | |||
| d0d24c0e6c |
@@ -49,6 +49,7 @@ export const config = {
|
|||||||
label: 'Teatro',
|
label: 'Teatro',
|
||||||
model: 'models/ssgp/Teatro_SSGP_Full_ConSottrazioni.glb',
|
model: 'models/ssgp/Teatro_SSGP_Full_ConSottrazioni.glb',
|
||||||
opacity: 0.0,
|
opacity: 0.0,
|
||||||
|
isInvisible: true,
|
||||||
children: [
|
children: [
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// Global ATON
|
// Global ATON
|
||||||
import { Controller } from "@hotwired/stimulus"
|
import { Controller } from "@hotwired/stimulus"
|
||||||
import AppState from "../state.js";
|
import AppState from "../state.js";
|
||||||
|
import { traverseOntology } from "../ontology.js";
|
||||||
|
|
||||||
const html = String.raw;
|
const html = String.raw;
|
||||||
const domParser = new DOMParser;
|
const domParser = new DOMParser;
|
||||||
|
// TODO: hard-coded, but follows a convention...
|
||||||
|
const ontologyJsonPath = location.pathname + 'ontology.json';
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ['trigger', 'layers', 'ontology'];
|
static targets = ['trigger', 'layers', 'ontology'];
|
||||||
@@ -15,11 +18,12 @@ export default class extends Controller {
|
|||||||
* Open settings panel
|
* Open settings panel
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
toggleMenu(event) {
|
async toggleMenu() {
|
||||||
ATON.UI.setSidePanelRight();
|
ATON.UI.setSidePanelRight();
|
||||||
ATON.UI.showSidePanel({header: 'Menu'});
|
ATON.UI.showSidePanel({header: 'Menu'});
|
||||||
this.#buildMenuPanel(ATON.UI.elSidePanel);
|
this.#buildMenuPanel(ATON.UI.elSidePanel);
|
||||||
this.#buildLayersMenu(AppState.normalizedNodes, this.layersTarget);
|
this.#buildLayersMenu(AppState.normalizedNodes, this.layersTarget);
|
||||||
|
this.#buildOntologyMenu(await traverseOntology(ontologyJsonPath), this.ontologyTarget);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
@@ -75,4 +79,31 @@ export default class extends Controller {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Temporary implementation to show domains only
|
||||||
|
* @todo Don't rebuild it every time, use caching, return a container
|
||||||
|
* @param {Object} ontology The traversed ontology object (temp)
|
||||||
|
* @param {HTMLElement} tab Tab content element
|
||||||
|
*/
|
||||||
|
#buildOntologyMenu(ontology, tab) {
|
||||||
|
console.debug(ontology);
|
||||||
|
|
||||||
|
const mainNode = tab.querySelector('#ontology-list');
|
||||||
|
mainNode.textContent = ontology.ontology;
|
||||||
|
|
||||||
|
let domainList = html`
|
||||||
|
<ul class="list-group mt-2" id="domains-list"></ul>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Very fragile and ugly!!
|
||||||
|
mainNode.innerHTML += domainList;
|
||||||
|
domainList = tab.querySelector('#domains-list');
|
||||||
|
|
||||||
|
for(let domain of ontology.domains) {
|
||||||
|
const domainItem = html`
|
||||||
|
<li class="list-group-item">${domain.label}</li>
|
||||||
|
`;
|
||||||
|
domainList.innerHTML += domainItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Global ATON
|
// Global ATON
|
||||||
import { Controller } from "@hotwired/stimulus"
|
import { Controller } from "@hotwired/stimulus"
|
||||||
import AppState from "../state.js";
|
import AppState from "../state.js";
|
||||||
import { createLightSlider } from "../utils/environment.js";
|
import { createExposureSlider, createLightSlider } from "../utils/environment.js";
|
||||||
|
|
||||||
const html = String.raw;
|
const html = String.raw;
|
||||||
const panelHeader = html`
|
const panelHeader = html`
|
||||||
@@ -39,12 +39,15 @@ export default class extends Controller {
|
|||||||
#buildSettingsPanel(panel) {
|
#buildSettingsPanel(panel) {
|
||||||
const fragment = this.#cloneTemplate('tmpl-settings');
|
const fragment = this.#cloneTemplate('tmpl-settings');
|
||||||
let sliderContainer = fragment.querySelector('[data-sliders-container]');
|
let sliderContainer = fragment.querySelector('[data-sliders-container]');
|
||||||
|
let exposureContainer = fragment.querySelector('[data-slider-exposure-container]');
|
||||||
|
|
||||||
['x', 'y', 'z'].forEach((axis, i) => {
|
['x', 'y', 'z'].forEach((axis, i) => {
|
||||||
const label = ['Asse X', 'Asse Y', 'Asse Z'][i];
|
const label = ['Asse X', 'Asse Y', 'Asse Z'][i];
|
||||||
sliderContainer.appendChild(createLightSlider(axis, label, [-2, 2], 0.1));
|
sliderContainer.appendChild(createLightSlider(axis, label, [-2, 2], 0.1));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
exposureContainer.appendChild(createExposureSlider('Valore', [0, 5]));
|
||||||
|
|
||||||
panel.appendChild(fragment);
|
panel.appendChild(fragment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ function init () {
|
|||||||
AppState.camera = ATON.Nav._camera;
|
AppState.camera = ATON.Nav._camera;
|
||||||
AppState.renderer = ATON._renderer;
|
AppState.renderer = ATON._renderer;
|
||||||
AppState.shadows = config.scene.shadows;
|
AppState.shadows = config.scene.shadows;
|
||||||
|
AppState.lightDirection = ATON.getMainLightDirection();
|
||||||
|
AppState.exposure = config.scene.initialExposure;
|
||||||
|
|
||||||
ATON.Nav.setUserControl(true);
|
ATON.Nav.setUserControl(true);
|
||||||
}
|
}
|
||||||
@@ -86,6 +88,9 @@ function loadNodes(nodes) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable a node for picking (shadows, light probe etc.)
|
||||||
|
if (n.isInvisible) node.hide();
|
||||||
|
|
||||||
node.attachToRoot();
|
node.attachToRoot();
|
||||||
|
|
||||||
if (n.isMain) {
|
if (n.isMain) {
|
||||||
@@ -94,7 +99,7 @@ function loadNodes(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: n.isInvisible ? false: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!AppState.clipping.boundingSphere) {
|
if (!AppState.clipping.boundingSphere) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ let AppState = {
|
|||||||
// {id: String, active: Boolean}
|
// {id: String, active: Boolean}
|
||||||
nodes: [],
|
nodes: [],
|
||||||
/**
|
/**
|
||||||
* @property {NormalizedSceneNode[]} normalizedNodes
|
* @type {NormalizedSceneNode[]} normalizedNodes
|
||||||
*/
|
*/
|
||||||
normalizedNodes: [],
|
normalizedNodes: [],
|
||||||
mainNodeId: null,
|
mainNodeId: null,
|
||||||
@@ -28,6 +28,11 @@ let AppState = {
|
|||||||
sceneHasAudio: false,
|
sceneHasAudio: false,
|
||||||
layersMenuBuilt: false,
|
layersMenuBuilt: false,
|
||||||
initialRotation: null,
|
initialRotation: null,
|
||||||
|
lightDirection: [],
|
||||||
|
/**
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
exposure: null,
|
||||||
camera: null,
|
camera: null,
|
||||||
renderer: null,
|
renderer: null,
|
||||||
ambientOcclusion : true,
|
ambientOcclusion : true,
|
||||||
|
|||||||
49
js/ui.js
49
js/ui.js
@@ -1,49 +0,0 @@
|
|||||||
import AppState from "./state.js";
|
|
||||||
import { traverseOntology } from "./ontology.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module UI
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {String} triggerSelector - Usually, the close modal trigger element(s) selector
|
|
||||||
*/
|
|
||||||
export function pauseAudio(triggerSelector) {
|
|
||||||
// What if more than one audio element is playing?
|
|
||||||
const audio = document.querySelector('audio');
|
|
||||||
|
|
||||||
if (audio) {
|
|
||||||
document.querySelectorAll(triggerSelector).forEach(el => {
|
|
||||||
el.addEventListener('click', () => audio.pause());
|
|
||||||
});
|
|
||||||
document.querySelector('.modal').addEventListener('blur', () => {
|
|
||||||
audio.pause();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @see traverseOntology
|
|
||||||
* @param {Object} ontology The traversed ontology object (temp)
|
|
||||||
* @param {HTMLElement} sidePanel ATON's side panel element
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
// Global ATON and THREE
|
// Global ATON and THREE
|
||||||
|
|
||||||
|
import AppState from "../state.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module Environment
|
* @module Environment
|
||||||
*/
|
*/
|
||||||
@@ -21,23 +23,28 @@ export function toggleAmbientOcclusion(isEnabled) {
|
|||||||
console.log('Ambient occlusion', isEnabled ? 'ON' : 'OFF');
|
console.log('Ambient occlusion', isEnabled ? 'ON' : 'OFF');
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
* Slider to change light direction, based on ATON.UI
|
||||||
* @param {String} direction - The axis direction, one of 'x','y','z'
|
* @param {String} direction - The axis direction, one of 'x','y','z'
|
||||||
* @param {String} label - The slider label
|
* @param {String} label - The slider label
|
||||||
* @param {Number[]} range - The slider's range
|
* @param {Array<Number>} range - The slider's range
|
||||||
* @param {Number} step - The slider's step
|
* @param {Number} step - The slider's step
|
||||||
*/
|
*/
|
||||||
export function createLightSlider(direction, label, range, step) {
|
export function createLightSlider(direction, label, range, step) {
|
||||||
const currentVal = ATON.getMainLightDirection()[direction];
|
const currentVal = AppState.lightDirection[direction];
|
||||||
|
|
||||||
|
console.debug(currentVal);
|
||||||
|
|
||||||
const lightSlider = ATON.UI.createSlider({
|
const lightSlider = ATON.UI.createSlider({
|
||||||
range,
|
range,
|
||||||
label,
|
label,
|
||||||
value: Number.parseFloat(currentVal).toPrecision(1),
|
value: Number.parseFloat(currentVal).toPrecision(2),
|
||||||
oninput: val => {
|
oninput: val => {
|
||||||
const lightDir = ATON.getMainLightDirection();
|
const lightDir = AppState.lightDirection;
|
||||||
|
|
||||||
// Keep existing direction values for the other axes
|
// Keep existing direction values for the other axes
|
||||||
lightDir[direction] = Number.parseFloat(val);
|
lightDir[direction] = Number.parseFloat(val);
|
||||||
changeLightDirection(lightDir);
|
changeLightDirection(lightDir);
|
||||||
|
AppState.lightDirection = lightDir;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,4 +52,30 @@ export function createLightSlider(direction, label, range, step) {
|
|||||||
lightSlider.querySelector('input').step = step;
|
lightSlider.querySelector('input').step = step;
|
||||||
|
|
||||||
return lightSlider;
|
return lightSlider;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Slider to change the env exposure level, based on ATON.UI
|
||||||
|
* @param {String} label - The slider label
|
||||||
|
* @param {Array<Number>} range - The slider's range
|
||||||
|
* @param {Number} step - The slider's step
|
||||||
|
*/
|
||||||
|
export function createExposureSlider(label, range, step = 0.05) {
|
||||||
|
const currentVal = AppState.exposure;
|
||||||
|
|
||||||
|
const exposureSlider = ATON.UI.createSlider({
|
||||||
|
range,
|
||||||
|
label,
|
||||||
|
value: Number.parseFloat(currentVal).toPrecision(1),
|
||||||
|
oninput: val => {
|
||||||
|
ATON.setExposure(val);
|
||||||
|
AppState.exposure = val;
|
||||||
|
|
||||||
|
console.debug('Current exposure:', ATON.getExposure());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exposureSlider.classList.add('ms-4');
|
||||||
|
exposureSlider.querySelector('input').step = step;
|
||||||
|
|
||||||
|
return exposureSlider;
|
||||||
}
|
}
|
||||||
@@ -99,9 +99,16 @@
|
|||||||
<template id="tmpl-settings">
|
<template id="tmpl-settings">
|
||||||
<div data-controller="settings">
|
<div data-controller="settings">
|
||||||
<h2 class="fs-5 ms-2 mb-3 mt-3">
|
<h2 class="fs-5 ms-2 mb-3 mt-3">
|
||||||
<i class="bi bi-lightbulb me-1"></i> Direzione luce
|
<i class="bi bi-lightbulb me-1"></i> Illuminazione
|
||||||
</h2>
|
</h2>
|
||||||
|
<h3 class="fs-6 ms-4 mb-3 mt-3">
|
||||||
|
Direzione (x, y, z)
|
||||||
|
</h3>
|
||||||
<div data-sliders-container></div>
|
<div data-sliders-container></div>
|
||||||
|
<h3 class="fs-6 ms-4 mb-3 mt-3">
|
||||||
|
Intensità
|
||||||
|
</h3>
|
||||||
|
<div data-slider-exposure-container></div>
|
||||||
<h2 class="fs-5 ms-2 mb-3 mt-3">
|
<h2 class="fs-5 ms-2 mb-3 mt-3">
|
||||||
<i class="bi bi-brightness-high me-1"></i> Ambiente
|
<i class="bi bi-brightness-high me-1"></i> Ambiente
|
||||||
</h2>
|
</h2>
|
||||||
@@ -127,7 +134,7 @@
|
|||||||
</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="#content" type="button" role="tab" aria-controls="media" aria-selected="true">
|
<button class="nav-link" id="content-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>
|
||||||
@@ -135,7 +142,12 @@
|
|||||||
<!-- 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-2" data-menu-target="layers" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div>
|
<div class="tab-pane active p-3 ms-2" data-menu-target="layers" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div>
|
||||||
<div class="tab-pane pt-3" data-menu-target="ontology" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div>
|
<div class="tab-pane pt-3" data-menu-target="ontology" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0">
|
||||||
|
<!-- Temporary -->
|
||||||
|
<ul class="list-group me-4 ms-0">
|
||||||
|
<li class="list-group-item pt-2 pb-2" id="ontology-list"> </li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user