diff --git a/js/scene.js b/js/scene.js index 86da6b2..6050150 100644 --- a/js/scene.js +++ b/js/scene.js @@ -2,17 +2,13 @@ import AppState from "./state.js"; import { config } from "../config.js"; -import UI from "./ui.js"; - -const Scene = {}; - -Scene.UI = UI; +import { toggleAmbientOcclusion } from "./utils/environment.js"; /** * @todo Experimental... * @param {THREE.Object3D} object - A THREE Object3D instance */ -Scene.showEdges = function(object) { +function showEdges (object) { const edgeMaterial = new THREE.LineBasicMaterial( { color: 0x000000 } ); object.traverse(function(child) { @@ -27,169 +23,7 @@ Scene.showEdges = function(object) { }); } -/** - * Calculate bounding box for the scene root object - * and return it along with its center and size (bad?). - * It only uses meshes to prevent "empty" nodes in the scene - * from being included in the calculation. - * @todo Use ATON.Node.getBound()? [bounding sphere] - */ -Scene.getRootBoundingBox = function() { - const meshes = []; - AppState.root.traverse(obj => { - if (obj.isMesh) meshes.push(obj); - }); - - if (meshes.length === 0) return null; - - const bbox = new THREE.Box3().setFromObject(meshes[0]); - for (let i = 1; i < meshes.length; i++) { - bbox.union(new THREE.Box3().setFromObject(meshes[i])); - } - - const center = bbox.getCenter(new THREE.Vector3()); - const size = bbox.getSize(new THREE.Vector3()); - - return { bbox, center, size }; -} - -/** - * - * @param {THREE.Sphere} boundingSphere - The bounding sphere for the main node - * @returns {THREE.Mesh} - */ -Scene.createClippingPlaneMesh = function (boundingSphere) { - const planeSize = boundingSphere.radius * 1.5; - const mesh = new THREE.Mesh( - new THREE.PlaneGeometry(planeSize, planeSize), - new THREE.MeshBasicMaterial({ color: 0xffff00, opacity: 0.05, side: THREE.DoubleSide, transparent: true }) - ); - - return mesh; -} - -/** - * - * @param {THREE.Mesh} planeMesh - * @param {THREE.ArrowHelper} arrowHelper - * @param {String} axis - */ -Scene.dragClipper = function(planeMesh, axis) { - const controls = new THREE.DragControls( - [planeMesh], - 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); - - 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('dragend', function (event) { - ATON.Nav.setUserControl(true); - }); - - AppState.clipping.controls = controls; - } -} - -/** - * @param {String} axis - The axis along which the plane's normal should be directed, - * one of 'x', 'y', 'z' - * @param {Number} orientation - Positive (1) or negative (-1) orientation on the axis - */ -Scene.addClippingPlane = function(axis, orientation = -1) { - axis = axis.toLowerCase(); - const bound = AppState.clipping.boundingSphere; - - if (!bound) return; - - const vector = [ - axis === 'x' ? orientation : 0, - axis === 'y' ? orientation : 0, - axis === 'z' ? orientation : 0, - ]; - - AppState.clipping.vector = vector; - - // First, add a default clipping plane - // at a default point (calculated...) - const defaultPoint = bound.center.clone(); - Scene.activateClipper(vector, axis, defaultPoint); -} - -/** - * @todo WIP! - * Activate clipping plane - * @param {Number[]} vector - The vector array to direct the plane - * @param {String} axis - The x,y,z axis - * @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); -} - -/** - * - * @param {THREE.Vector3} vector - * @param {THREE.Vector3} point - */ -Scene.updateClipper = function(vector, point) { - // 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; - } - - visiblePlane.position.copy(point); - visiblePlane.lookAt(point.clone().add(normal)); - - plane.setFromNormalAndCoplanarPoint(normal, point); - AppState.clipping.plane = plane; - } -} - -/** - * - * @param {THREE.Vector3} vector - An object with x,y,z coordinates - */ -Scene.changeLightDirection = function(vector) { - ATON.setMainLightDirection(vector); -} - -/** - * - * @param {Boolean} isEnabled - */ -Scene.toggleAmbientOcclusion = function(isEnabled) { - ATON.FX.togglePass(ATON.FX.PASS_AO, isEnabled); - console.log('Ambient occlusion', isEnabled ? 'ON' : 'OFF'); -} - -Scene.init = function() { +function init () { ATON.realize(); ATON.UI.addBasicEvents(); ATON.UI.init(); @@ -200,49 +34,22 @@ Scene.init = function() { ATON.toggleShadows(true); ATON.setExposure(config.scene.initialExposure); // Open settings side panel when clicking on settings btn - Scene.UI.toggleSettingsPanel('settings'); - Scene.UI.toggleContentMenu('menu'); AppState.camera = ATON.Nav._camera; AppState.renderer = ATON._renderer; ATON.Nav.setUserControl(true); } -/** - * Reset clipping state after disabling from UI - * @todo DragControls errors!! - */ -Scene.resetClipping = function () { - 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; - AppState.clipping.vector = null; - // Ensure nav controls are reactivated! - ATON.Nav.setUserControl(true); -} /** * @param {Object} marker - The marker object from config * @param {Object[]} nodes - The flat list of nodes for this scene */ -Scene.openScene = function(marker, nodes) { - Scene.init(); - // TODO: move to init logic - Scene.UI.toggleClipperBar('#clipper', '#clipper-bar'); - +export function openScene (marker, nodes) { + init(); // Filter nodes with models first nodes = nodes.filter(n => n.model); // Load 3D models and create nodes - Scene.loadNodes(nodes); + loadNodes(nodes); ATON.setMainPanorama(marker.pano); // TODO: hardcoded... @@ -250,7 +57,7 @@ Scene.openScene = function(marker, nodes) { ATON.setAutoLP(config.scene.autoLP); AppState.lightProbe = config.scene.autoLP; - Scene.toggleAmbientOcclusion(true); + toggleAmbientOcclusion(true); AppState.ambientOcclusion = true; AppState.root = ATON.getRootScene(); } @@ -259,7 +66,7 @@ Scene.openScene = function(marker, nodes) { * * @param {Object[]} nodes */ -Scene.loadNodes = function (nodes) { +function loadNodes(nodes) { nodes.forEach(n => { let node = ATON.createSceneNode(n.label); node.load(n.model); @@ -289,5 +96,3 @@ Scene.loadNodes = function (nodes) { AppState.nodes.push({id: n.label, active: true}); }); } - -export default Scene; \ No newline at end of file diff --git a/js/ui.js b/js/ui.js index 4bf909f..a9dd07e 100644 --- a/js/ui.js +++ b/js/ui.js @@ -1,14 +1,14 @@ -import { config } from "../config.js"; -import Scene from "./scene.js"; import AppState from "./state.js"; +import { changeLightDirection, toggleAmbientOcclusion } from "./utils/environment.js"; +import { resetClipping, addClippingPlane } from "./utils/clipping.js"; + /** - * @namespace UI + * @module UI */ -const UI = {}; -UI.domParser = new DOMParser; +const domParser = new DOMParser; -UI.contentMenuTabs = ` +const contentMenuTabs = `