123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- 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 './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(),
- // xr
- enterXR: new Signal(),
- offerXR: new Signal(),
- leaveXR: 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(),
- rendererDetectKTX2Support: 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(),
- showHelpersChanged: new Signal(),
- refreshSidebarObject3D: new Signal(),
- refreshSidebarEnvironment: new Signal(),
- historyChanged: new Signal(),
- viewportCameraChanged: new Signal(),
- viewportShadingChanged: new Signal(),
- intersectionsDetected: new Signal(),
- pathTracerUpdated: new Signal(),
- };
- this.config = new Config();
- this.history = new _History( this );
- this.selector = new Selector( this );
- this.storage = new _Storage();
- this.strings = new Strings( this.config );
- 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.sceneHelpers.add( new THREE.HemisphereLight( 0xffffff, 0x888888, 2 ) );
- 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.viewportCamera = this.camera;
- this.viewportShading = 'default';
- this.addCamera( this.camera );
- }
- Editor.prototype = {
- 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.backgroundBlurriness = scene.backgroundBlurriness;
- this.scene.backgroundIntensity = scene.backgroundIntensity;
- 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();
- },
- 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 && 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 );
- helper.dispose();
- 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();
- },
- setViewportShading: function ( value ) {
- this.viewportShading = value;
- this.signals.viewportShadingChanged.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;
- this.signals.sceneGraphChanged.active = false;
- while ( objects.length > 0 ) {
- this.removeObject( objects[ 0 ] );
- }
- this.signals.sceneGraphChanged.active = true;
- this.geometries = {};
- this.materials = {};
- this.textures = {};
- this.scripts = {};
- this.materialsRefCounter.clear();
- this.animations = {};
- this.mixer.stopAllAction();
- this.deselect();
- this.signals.editorCleared.dispatch();
- },
- //
- fromJSON: async function ( json ) {
- var loader = new THREE.ObjectLoader();
- var camera = await loader.parseAsync( json.camera );
- const existingUuid = this.camera.uuid;
- const incomingUuid = camera.uuid;
- // copy all properties, including uuid
- this.camera.copy( camera );
- this.camera.uuid = incomingUuid;
- delete this.cameras[ existingUuid ]; // remove old entry [existingUuid, this.camera]
- this.cameras[ incomingUuid ] = this.camera; // add new entry [incomingUuid, this.camera]
- this.signals.cameraResetted.dispatch();
- this.history.fromJSON( json.history );
- this.scripts = json.scripts;
- this.setScene( await loader.parseAsync( json.scene ) );
- if ( json.environment === 'ModelViewer' ) {
- this.signals.sceneEnvironmentChanged.dispatch( json.environment );
- this.signals.refreshSidebarEnvironment.dispatch();
- }
- },
- toJSON: function () {
- // scripts clean up
- var scene = this.scene;
- var scripts = this.scripts;
- for ( var key in scripts ) {
- var script = scripts[ key ];
- if ( script.length === 0 || scene.getObjectByProperty( 'uuid', key ) === undefined ) {
- delete scripts[ key ];
- }
- }
- // honor modelviewer environment
- let environment = null;
- if ( this.scene.environment !== null && this.scene.environment.isRenderTargetTexture === true ) {
- environment = 'ModelViewer';
- }
- //
- return {
- metadata: {},
- project: {
- shadows: this.config.getKey( 'project/renderer/shadows' ),
- shadowType: this.config.getKey( 'project/renderer/shadowType' ),
- toneMapping: this.config.getKey( 'project/renderer/toneMapping' ),
- toneMappingExposure: this.config.getKey( 'project/renderer/toneMappingExposure' )
- },
- camera: this.viewportCamera.toJSON(),
- scene: this.scene.toJSON(),
- scripts: this.scripts,
- history: this.history.toJSON(),
- environment: environment
- };
- },
- 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();
- },
- utils: {
- save: save,
- saveArrayBuffer: saveArrayBuffer,
- saveString: saveString,
- formatNumber: formatNumber
- }
- };
- const link = document.createElement( 'a' );
- function save( blob, filename ) {
- if ( link.href ) {
- URL.revokeObjectURL( link.href );
- }
- link.href = URL.createObjectURL( blob );
- link.download = filename || 'data.json';
- link.dispatchEvent( new MouseEvent( 'click' ) );
- }
- function saveArrayBuffer( buffer, filename ) {
- save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
- }
- function saveString( text, filename ) {
- save( new Blob( [ text ], { type: 'text/plain' } ), filename );
- }
- function formatNumber( number ) {
- return new Intl.NumberFormat( 'en-us', { useGrouping: true } ).format( number );
- }
- export { Editor };
|