import * as THREE from 'three'; import { Config } from './Config.js'; import { Loader } from './Loader.js'; import { History as _History } from './History.js'; import { Strings } from './Strings.js'; import { Storage as _Storage } from './Storage.js'; import { Selector } from './Viewport.Selector.js'; var _DEFAULT_CAMERA = new THREE.PerspectiveCamera(50, 1, 0.01, 1000); _DEFAULT_CAMERA.name = 'Camera'; _DEFAULT_CAMERA.position.set(0, 5, 10); _DEFAULT_CAMERA.lookAt(new THREE.Vector3()); function Editor() { const Signal = signals.Signal; // eslint-disable-line no-undef this.signals = { // script editScript: new Signal(), // player startPlayer: new Signal(), stopPlayer: new Signal(), // vr toggleVR: new Signal(), exitedVR: new Signal(), // notifications editorCleared: new Signal(), savingStarted: new Signal(), savingFinished: new Signal(), transformModeChanged: new Signal(), snapChanged: new Signal(), spaceChanged: new Signal(), rendererCreated: new Signal(), rendererUpdated: new Signal(), rendererCSSCreated: new Signal(), rendererCSSUpdated: new Signal(), sceneBackgroundChanged: new Signal(), sceneEnvironmentChanged: new Signal(), sceneFogChanged: new Signal(), sceneFogSettingsChanged: new Signal(), sceneGraphChanged: new Signal(), sceneRendered: new Signal(), cameraChanged: new Signal(), cameraResetted: new Signal(), geometryChanged: new Signal(), objectSelected: new Signal(), objectFocused: new Signal(), objectAdded: new Signal(), objectChanged: new Signal(), objectRemoved: new Signal(), cameraAdded: new Signal(), cameraRemoved: new Signal(), helperAdded: new Signal(), helperRemoved: new Signal(), materialAdded: new Signal(), materialChanged: new Signal(), materialRemoved: new Signal(), scriptAdded: new Signal(), scriptChanged: new Signal(), scriptRemoved: new Signal(), windowResize: new Signal(), showGridChanged: new Signal(), showHelpersChanged: new Signal(), refreshSidebarObject3D: new Signal(), historyChanged: new Signal(), viewportCameraChanged: new Signal(), intersectionsDetected: new Signal(), render: new Signal(), }; this.config = new Config(); this.history = new _History(this); this.storage = new _Storage(); this.strings = new Strings(this.config); console.log("language=", this.config.getKey("language")); this.selector = new Selector(this); this.loader = new Loader(this); this.camera = _DEFAULT_CAMERA.clone(); this.scene = new THREE.Scene(); this.scene.name = 'Scene'; this.sceneHelpers = new THREE.Scene(); this.object = {}; this.geometries = {}; this.materials = {}; this.textures = {}; this.scripts = {}; this.materialsRefCounter = new Map(); // tracks how often is a material used by a 3D object this.mixer = new THREE.AnimationMixer(this.scene); this.selected = null; this.helpers = {}; this.cameras = {}; this.labellist = []; this.monitoringlist = []; this.viewportCamera = this.camera; this.addCamera(this.camera); } Editor.prototype = { addBuilding: function (building) { this.removeBuilding() this._building = building this.scene.add(building) this.signals.objectAdded.dispatch(building); this.signals.sceneGraphChanged.dispatch(); }, removeBuilding: function () { if (this._building) { let building = this._building this.scene.remove(this._building) this._building = null // this.signals.objectRemoved.dispatch(building) // this.signals.sceneGraphChanged.dispatch(); } }, setScene: function (scene) { this.scene.uuid = scene.uuid; this.scene.name = scene.name; this.scene.background = scene.background; this.scene.environment = scene.environment; this.scene.fog = scene.fog; this.scene.userData = JSON.parse(JSON.stringify(scene.userData)); // avoid render per object this.signals.sceneGraphChanged.active = false; while (scene.children.length > 0) { this.addObject(scene.children[0]); } this.signals.sceneGraphChanged.active = true; this.signals.sceneGraphChanged.dispatch(); }, // addObject: function (object, parent, index) { var scope = this; object.traverse(function (child) { if (child.geometry !== undefined) scope.addGeometry(child.geometry); if (child.material !== undefined) scope.addMaterial(child.material); scope.addCamera(child); scope.addHelper(child); }); if (parent === undefined) { this.scene.add(object); } else { parent.children.splice(index, 0, object); object.parent = parent; } this.signals.objectAdded.dispatch(object); this.signals.sceneGraphChanged.dispatch(); }, addLable: function (object, parent, index) { // debugger // this.labellist.push(object); }, moveObject: function (object, parent, before) { if (parent === undefined) { parent = this.scene; } parent.add(object); // sort children array if (before !== undefined) { var index = parent.children.indexOf(before); parent.children.splice(index, 0, object); parent.children.pop(); } this.signals.sceneGraphChanged.dispatch(); }, nameObject: function (object, name) { object.name = name; this.signals.sceneGraphChanged.dispatch(); }, removeObject: function (object) { if (object.parent === null) return; // avoid deleting the camera or scene var scope = this; object.traverse(function (child) { scope.removeCamera(child); scope.removeHelper(child); if (child.material !== undefined) scope.removeMaterial(child.material); }); object.parent.remove(object); this.signals.objectRemoved.dispatch(object); this.signals.sceneGraphChanged.dispatch(); }, addGeometry: function (geometry) { this.geometries[geometry.uuid] = geometry; }, setGeometryName: function (geometry, name) { geometry.name = name; this.signals.sceneGraphChanged.dispatch(); }, addMaterial: function (material) { if (Array.isArray(material)) { for (var i = 0, l = material.length; i < l; i++) { this.addMaterialToRefCounter(material[i]); } } else { this.addMaterialToRefCounter(material); } this.signals.materialAdded.dispatch(); }, addMaterialToRefCounter: function (material) { var materialsRefCounter = this.materialsRefCounter; var count = materialsRefCounter.get(material); if (count === undefined) { materialsRefCounter.set(material, 1); this.materials[material.uuid] = material; } else { count++; materialsRefCounter.set(material, count); } }, removeMaterial: function (material) { if (Array.isArray(material)) { for (var i = 0, l = material.length; i < l; i++) { this.removeMaterialFromRefCounter(material[i]); } } else { this.removeMaterialFromRefCounter(material); } this.signals.materialRemoved.dispatch(); }, removeMaterialFromRefCounter: function (material) { var materialsRefCounter = this.materialsRefCounter; var count = materialsRefCounter.get(material); count--; if (count === 0) { materialsRefCounter.delete(material); delete this.materials[material.uuid]; } else { materialsRefCounter.set(material, count); } }, getMaterialById: function (id) { var material; var materials = Object.values(this.materials); for (var i = 0; i < materials.length; i++) { if (materials[i].id === id) { material = materials[i]; break; } } return material; }, setMaterialName: function (material, name) { material.name = name; this.signals.sceneGraphChanged.dispatch(); }, addTexture: function (texture) { this.textures[texture.uuid] = texture; }, // addCamera: function (camera) { if (camera.isCamera) { this.cameras[camera.uuid] = camera; this.signals.cameraAdded.dispatch(camera); } }, removeCamera: function (camera) { if (this.cameras[camera.uuid] !== undefined) { delete this.cameras[camera.uuid]; this.signals.cameraRemoved.dispatch(camera); } }, // addHelper: function () { var geometry = new THREE.SphereGeometry(2, 4, 2); var material = new THREE.MeshBasicMaterial({ color: 0xff0000, visible: false }); return function (object, helper) { if (helper === undefined) { if (object.isCamera) { helper = new THREE.CameraHelper(object); } else if (object.isPointLight) { helper = new THREE.PointLightHelper(object, 1); } else if (object.isDirectionalLight) { helper = new THREE.DirectionalLightHelper(object, 1); } else if (object.isSpotLight) { helper = new THREE.SpotLightHelper(object); } else if (object.isHemisphereLight) { helper = new THREE.HemisphereLightHelper(object, 1); } else if (object.isSkinnedMesh) { helper = new THREE.SkeletonHelper(object.skeleton.bones[0]); } else if (object.isBone === true && object.parent?.isBone !== true) { helper = new THREE.SkeletonHelper(object); } else { // no helper for this object type return; } const picker = new THREE.Mesh(geometry, material); picker.name = 'picker'; picker.userData.object = object; helper.add(picker); } this.sceneHelpers.add(helper); this.helpers[object.id] = helper; this.signals.helperAdded.dispatch(helper); }; }(), removeHelper: function (object) { if (this.helpers[object.id] !== undefined) { var helper = this.helpers[object.id]; helper.parent.remove(helper); delete this.helpers[object.id]; this.signals.helperRemoved.dispatch(helper); } }, // addScript: function (object, script) { if (this.scripts[object.uuid] === undefined) { this.scripts[object.uuid] = []; } this.scripts[object.uuid].push(script); this.signals.scriptAdded.dispatch(script); }, removeScript: function (object, script) { if (this.scripts[object.uuid] === undefined) return; var index = this.scripts[object.uuid].indexOf(script); if (index !== - 1) { this.scripts[object.uuid].splice(index, 1); } this.signals.scriptRemoved.dispatch(script); }, getObjectMaterial: function (object, slot) { var material = object.material; if (Array.isArray(material) && slot !== undefined) { material = material[slot]; } return material; }, setObjectMaterial: function (object, slot, newMaterial) { if (Array.isArray(object.material) && slot !== undefined) { object.material[slot] = newMaterial; } else { object.material = newMaterial; } }, setViewportCamera: function (uuid) { this.viewportCamera = this.cameras[uuid]; this.signals.viewportCameraChanged.dispatch(); }, // select: function (object) { this.selector.select(object); }, selectById: function (id) { if (id === this.camera.id) { this.select(this.camera); return; } this.select(this.scene.getObjectById(id)); }, selectByUuid: function (uuid) { var scope = this; this.scene.traverse(function (child) { if (child.uuid === uuid) { scope.select(child); } }); }, deselect: function () { this.selector.deselect(); }, focus: function (object) { if (object !== undefined) { this.signals.objectFocused.dispatch(object); } }, focusById: function (id) { this.focus(this.scene.getObjectById(id)); }, clear: function () { this.history.clear(); this.storage.clear(); this.camera.copy(_DEFAULT_CAMERA); this.signals.cameraResetted.dispatch(); this.scene.name = 'Scene'; this.scene.userData = {}; this.scene.background = null; this.scene.environment = null; this.scene.fog = null; var objects = this.scene.children; while (objects.length > 0) { this.removeObject(objects[0]); } this.geometries = {}; this.materials = {}; this.textures = {}; this.scripts = {}; this.materialsRefCounter.clear(); this.animations = {}; this.mixer.stopAllAction(); this.deselect(); this.signals.editorCleared.dispatch(); this.labellist = []; this.monitoringlist = []; }, // fromJSON: async function (json) { var loader = new THREE.ObjectLoader(); var camera = await loader.parseAsync(json.camera); this.camera.copy(camera); this.signals.cameraResetted.dispatch(); this.history.fromJSON(json.history); this.scripts = json.scripts; this.setScene(await loader.parseAsync(json.scene)); }, toJSON: function () { // scripts clean up var scene = this.scene; var scripts = this.scripts; // debugger for (var key in scripts) { var script = scripts[key]; if (script.length === 0 || scene.getObjectByProperty('uuid', key) === undefined) { delete scripts[key]; } } // return { metadata: {}, project: { shadows: this.config.getKey('project/renderer/shadows'), shadowType: this.config.getKey('project/renderer/shadowType'), vr: this.config.getKey('project/vr'), physicallyCorrectLights: this.config.getKey('project/renderer/physicallyCorrectLights'), toneMapping: this.config.getKey('project/renderer/toneMapping'), toneMappingExposure: this.config.getKey('project/renderer/toneMappingExposure') }, camera: this.camera.toJSON(), scene: this.scene.toJSON(), scripts: this.scripts, history: this.history.toJSON(), labels: this.labellist, }; }, objectByUuid: function (uuid) { return this.scene.getObjectByProperty('uuid', uuid, true); }, execute: function (cmd, optionalName) { this.history.execute(cmd, optionalName); }, undo: function () { this.history.undo(); }, redo: function () { this.history.redo(); } }; export { Editor };