UNPKG

aframe-inspector

Version:
310 lines (265 loc) 8.05 kB
import { createRoot } from 'react-dom/client'; import Events from './lib/Events'; import { Viewport } from './lib/viewport'; import { AssetsLoader } from './lib/assetsLoader'; import { Shortcuts } from './lib/shortcuts'; import Main from './components/Main'; import { initCameras } from './lib/cameras'; import { createEntity } from './lib/entity'; import { Config } from './lib/config'; import { GLTFExporter } from 'three/addons/exporters/GLTFExporter'; import './style/index.styl'; function Inspector(configOverrides) { this.assetsLoader = new AssetsLoader(); this.config = new Config(configOverrides); this.exporters = { gltf: new GLTFExporter() }; this.history = require('./lib/history'); this.isFirstOpen = true; this.modules = {}; this.opened = false; // Wait for stuff. const doInit = () => { if (!AFRAME.scenes.length) { setTimeout(() => { doInit(); }, 100); return; } this.sceneEl = AFRAME.scenes[0]; if (this.sceneEl.hasLoaded) { this.init(); return; } this.sceneEl.addEventListener('loaded', this.init.bind(this), { once: true }); }; doInit(); } Inspector.prototype = { init: function () { // Wait for camera. if (!this.sceneEl.camera) { this.sceneEl.addEventListener( 'camera-set-active', () => { this.init(); }, { once: true } ); return; } this.container = document.querySelector('.a-canvas'); initCameras(this); this.initUI(); }, initUI: function () { Shortcuts.init(this); this.initEvents(); this.selected = null; // Init React. const div = document.createElement('div'); div.id = 'aframeInspector'; div.setAttribute('data-aframe-inspector', 'app'); document.body.appendChild(div); const root = createRoot(div); root.render(<Main />); this.scene = this.sceneEl.object3D; this.helpers = {}; this.sceneHelpers = new THREE.Scene(); this.sceneHelpers.userData.source = 'INSPECTOR'; this.sceneHelpers.visible = true; this.inspectorActive = false; this.viewport = new Viewport(this); this.sceneEl.object3D.traverse((node) => { this.addHelper(node); }); this.scene.add(this.sceneHelpers); this.open(); }, removeObject: function (object) { // Remove just the helper as the object will be deleted by A-Frame this.removeHelpers(object); Events.emit('objectremove', object); }, addHelper: function (object) { let helper; if (object instanceof THREE.Camera) { this.cameraHelper = helper = new THREE.CameraHelper(object); } else if (object instanceof THREE.PointLight) { helper = new THREE.PointLightHelper(object, 1); } else if (object instanceof THREE.DirectionalLight) { helper = new THREE.DirectionalLightHelper(object, 1); } else if (object instanceof THREE.SpotLight) { helper = new THREE.SpotLightHelper(object, 1); } else if (object instanceof THREE.HemisphereLight) { helper = new THREE.HemisphereLightHelper(object, 1); } else if (object instanceof THREE.SkinnedMesh) { helper = new THREE.SkeletonHelper(object); } else { // no helper for this object type return; } helper.visible = false; this.sceneHelpers.add(helper); this.helpers[object.uuid] = helper; // SkeletonHelper doesn't have an update method if (helper.update) { helper.update(); } }, removeHelpers: function (object) { object.traverse((node) => { const helper = this.helpers[node.uuid]; if (helper) { this.sceneHelpers.remove(helper); helper.dispose(); delete this.helpers[node.uuid]; Events.emit('helperremove', this.helpers[node.uuid]); } }); }, selectEntity: function (entity, emit) { this.selectedEntity = entity; if (entity) { this.select(entity.object3D); } else { this.select(null); } if (emit === undefined) { Events.emit('entityselect', entity); } // Update helper visibilities. for (const id in this.helpers) { this.helpers[id].visible = false; } if (entity === this.sceneEl) { return; } if (entity) { entity.object3D.traverse((node) => { if (this.helpers[node.uuid]) { this.helpers[node.uuid].visible = true; } }); } }, initEvents: function () { // Remove inspector component to properly unregister keydown listener when the inspector is loaded via a script tag, // otherwise the listener will be registered twice and we can't toggle the inspector from viewer mode with the shortcut. this.sceneEl.removeAttribute('inspector'); window.addEventListener('keydown', (evt) => { // Alt + Ctrl + i: Shorcut to toggle the inspector const shortcutPressed = evt.keyCode === 73 && ((evt.ctrlKey && evt.altKey) || evt.getModifierState('AltGraph')); if (shortcutPressed) { this.toggle(); } }); Events.on('entityselect', (entity) => { this.selectEntity(entity, false); }); Events.on('inspectortoggle', (active) => { this.inspectorActive = active; this.sceneHelpers.visible = this.inspectorActive; }); Events.on('entitycreate', (definition) => { createEntity(definition, (entity) => { this.selectEntity(entity); }); }); document.addEventListener('child-detached', (event) => { const entity = event.detail.el; AFRAME.INSPECTOR.removeObject(entity.object3D); }); }, selectById: function (id) { if (id === this.camera.id) { this.select(this.camera); return; } const object = this.scene.getObjectById(id); if (object) { this.select(object); } }, /** * Change to select object. */ select: function (object3D) { if (this.selected === object3D) { return; } this.selected = object3D; Events.emit('objectselect', object3D); }, deselect: function () { this.select(null); }, /** * Toggle the editor */ toggle: function () { if (this.opened) { this.close(); } else { this.open(); } }, /** * Open the editor UI */ open: function (focusEl) { this.opened = true; Events.emit('inspectortoggle', true); if (this.sceneEl.hasAttribute('embedded')) { // Remove embedded styles, but keep track of it. this.sceneEl.removeAttribute('embedded'); this.sceneEl.setAttribute('aframe-inspector-removed-embedded'); } document.body.classList.add('aframe-inspector-opened'); this.sceneEl.resize(); this.sceneEl.pause(); this.sceneEl.exitVR(); Shortcuts.enable(); // Trick scene to run the cursor tick. this.sceneEl.isPlaying = true; this.cursor.play(); if ( !focusEl && this.isFirstOpen && AFRAME.utils.getUrlParameter('inspector') ) { // Focus entity with URL parameter on first open. focusEl = document.getElementById( AFRAME.utils.getUrlParameter('inspector') ); } if (focusEl) { this.selectEntity(focusEl); Events.emit('objectfocus', focusEl.object3D); } this.isFirstOpen = false; }, /** * Closes the editor and gives the control back to the scene * @return {[type]} [description] */ close: function () { this.opened = false; Events.emit('inspectortoggle', false); // Untrick scene when we enabled this to run the cursor tick. this.sceneEl.isPlaying = false; this.sceneEl.play(); this.cursor.pause(); if (this.sceneEl.hasAttribute('aframe-inspector-removed-embedded')) { this.sceneEl.setAttribute('embedded', ''); this.sceneEl.removeAttribute('aframe-inspector-removed-embedded'); } document.body.classList.remove('aframe-inspector-opened'); this.sceneEl.resize(); Shortcuts.disable(); } }; AFRAME.INSPECTOR = new Inspector(window.AFRAME_INSPECTOR_CONFIG);