這一章講解整個編輯器中最重要的一個文件editor\js\Editor.js,這個文件是真個編輯器的上下文,是所有ui與對象交互的橋樑,相當於mvc模式中的controller。
Editor.js包含的幾個重要的信息如下:
- 整個編輯器的相機DEFAULT_CAMERA
- 整個編輯器的場景this.scene
- 整個場景中輔助相關對象所在的場景this.sceneHelpers
- 編輯器中信息傳遞的介質this.signals
- 整個場景的資源信息包括this.object、this.geometries、this.materials、this.textures、this.scripts、this.animations、this.mixer、this.helpers
- 編輯器中當前關注的對象this.selected
- 編輯器的配置信息(初始化時默認配置)this.config
- 編輯器的歷史操作信息this.history
- 編輯器存儲相關信息this.storage
- 編輯器的ui文本信息this.strings
- 編輯器的文件加載器,主要是導入json、對象等this.loader
//編輯器的上下文(是各個子類的橋樑,各個子模塊通信的橋樑) var Editor = function () { this.DEFAULT_CAMERA = new THREE.PerspectiveCamera( 50, 1, 0.01, 1000 ); this.DEFAULT_CAMERA.name = 'Camera'; this.DEFAULT_CAMERA.position.set( 0, 5, 10 ); this.DEFAULT_CAMERA.lookAt( new THREE.Vector3() ); //信號庫 var Signal = signals.Signal; //各種信號(信號中心,都是通過editor中定義的全局變量來在各個組件中共享的) this.signals = { // script // 腳本監聽 editScript: new Signal(), // player // 播放器開始、停止監聽 startPlayer: new Signal(), stopPlayer: new Signal(), // actions //顯示模型監聽? showModal: new Signal(), // notifications //清空編輯器監聽? editorCleared: new Signal(), //開始保存監聽、保存完成監聽 savingStarted: new Signal(), savingFinished: new Signal(), //主題改變監聽 themeChanged: new Signal(), //變換改變監聽(平移、旋轉、縮放) transformModeChanged: new Signal(), snapChanged: new Signal(), //選取監聽? spaceChanged: new Signal(), //? rendererChanged: new Signal(), //? //場景背景改變監聽、霧監聽、場景圖改變監聽 sceneBackgroundChanged: new Signal(), sceneFogChanged: new Signal(), sceneGraphChanged: new Signal(), //相機改變監聽 cameraChanged: new Signal(), //幾何數據改變監聽 geometryChanged: new Signal(), //選擇對象、設置焦點對象監聽 objectSelected: new Signal(), objectFocused: new Signal(), //對象的添加、改變、移除監聽 objectAdded: new Signal(), objectChanged: new Signal(), objectRemoved: new Signal(), //添加、移除輔助信息監聽 helperAdded: new Signal(), helperRemoved: new Signal(), //材質改變監聽 materialChanged: new Signal(), //添加、改變、移除腳本監聽 scriptAdded: new Signal(), scriptChanged: new Signal(), scriptRemoved: new Signal(), //窗口改變大小監聽 windowResize: new Signal(), // 網格監聽、刷新邊欄監聽、歷史改變監聽 showGridChanged: new Signal(), refreshSidebarObject3D: new Signal(), historyChanged: new Signal() }; //配置類(配置的信息保存在瀏覽器的localstring中) this.config = new Config(); //歷史類 this.history = new History( this ); //存儲類,封裝了瀏覽器數據庫的操作 this.storage = new Storage(); //編輯器ui中的所有文本信息 this.strings = new Strings( this.config ); //文件加載器,可以加載模型、發佈的json文件、壓縮文件 this.loader = new Loader( this ); //克隆相機 this.camera = this.DEFAULT_CAMERA.clone(); //創建場景 this.scene = new THREE.Scene(); this.scene.name = 'Scene'; this.scene.background = new THREE.Color( 0xaaaaaa ); //創建輔助場景展示的模型 this.sceneHelpers = new THREE.Scene(); //場景中所有的對象、幾何體、材質、紋理、腳本 this.object = {}; this.geometries = {}; this.materials = {}; this.textures = {}; this.scripts = {}; //所有的動畫 this.animations = {}; //創建一個混合器,所有的動畫骨骼名稱對應的mesh都會在整個場景中查找 this.mixer = new THREE.AnimationMixer( this.scene ); //選擇的對象、所有的輔助的東西 this.selected = null; this.helpers = {}; }; Editor.prototype = { setTheme: function ( value ) { //設置主題div document.getElementById( 'theme' ).href = value; //派發消息 this.signals.themeChanged.dispatch( value ); }, // //設置場景 setScene: function ( scene ) { //場景名稱、id this.scene.uuid = scene.uuid; this.scene.name = scene.name; //場景的背景、霧 if ( scene.background !== null ) this.scene.background = scene.background.clone(); if ( scene.fog !== null ) this.scene.fog = scene.fog.clone(); //場景的數據 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 ) { var scope = this; //遍歷對象 object.traverse( function ( child ) { //存儲所有的幾何數據、材質數據 if ( child.geometry !== undefined ) scope.addGeometry( child.geometry ); if ( child.material !== undefined ) scope.addMaterial( child.material ); //添加輔助信息 scope.addHelper( child ); } ); //對象添加到場景中 this.scene.add( object ); //派發消息 this.signals.objectAdded.dispatch( object ); this.signals.sceneGraphChanged.dispatch(); }, //移動對象(主要是材質場景樹,將一個模型變爲另一個模型的子節點) 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.removeHelper( child ); } ); //移除該對象 object.parent.remove( object ); //對象移除事件、場景圖改變事件 this.signals.objectRemoved.dispatch( object ); this.signals.sceneGraphChanged.dispatch(); }, //添加幾何數據 addGeometry: function ( geometry ) { //所有的幾何數據都存儲在這裏了(幾何數據id--幾何數據) this.geometries[ geometry.uuid ] = geometry; }, //設置幾何數據名稱 setGeometryName: function ( geometry, name ) { geometry.name = name; this.signals.sceneGraphChanged.dispatch(); }, //添加材質 addMaterial: function ( material ) { this.materials[ material.uuid ] = material; }, //修改材質名稱 setMaterialName: function ( material, name ) { material.name = name; this.signals.sceneGraphChanged.dispatch(); }, //添加紋理 addTexture: function ( texture ) { //場景中所有的紋理都存在這裏了(紋理id---紋理) this.textures[ texture.uuid ] = texture; }, //添加動畫 addAnimation: function ( object, animations ) { //動畫 if ( animations.length > 0 ) { //場景中所有的動畫都存儲在這裏了(對象id---對象動畫) this.animations[ object.uuid ] = animations; } }, // //添加輔助信息 addHelper: function () { //小圓球作爲輔助模型(小球幾何數據全局只有一個,選中那個物體就把輔助小球顯示在那個物體上) var geometry = new THREE.SphereBufferGeometry( 2, 4, 2 ); var material = new THREE.MeshBasicMaterial( { color: 0xff0000, visible: false } ); // return function ( object ) { var helper; if ( object.isCamera ) { //選中相機 helper = new THREE.CameraHelper( object, 1 ); } 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, 1 ); } else if ( object.isHemisphereLight ) { //選中半球光 helper = new THREE.HemisphereLightHelper( object, 1 ); } else if ( object.isSkinnedMesh ) { //選中骨骼模型 helper = new THREE.SkeletonHelper( object ); //添加輔助骨架 } else { //其他的對象沒有輔助信息 // no helper for this object type return; } //創建一個模型對象,將該對象添加到輔助模型上 var 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 ) ) { //按照材質索引獲取材質 material = material[ slot ]; } return material; }, //對象賦予新的材質,對象可能有多個材質,可以對每一個材質單獨設置 setObjectMaterial: function ( object, slot, newMaterial ) { //材質數組 if ( Array.isArray( object.material ) ) { object.material[ slot ] = newMaterial; } else { //唯一材質 object.material = newMaterial; } }, // //選擇對象時調用 select: function ( object ) { //如果選擇的對象是自己就返回 if ( this.selected === object ) return; // var uuid = null; //對象存在就設置uuid if ( object !== null ) { uuid = object.uuid; } //設置爲選擇的對象 this.selected = object; //config應該是一個配置文件,可能是關聯了localstring this.config.setKey( 'selected', uuid ); //派發對象選中的信息 this.signals.objectSelected.dispatch( object ); }, //通過id選擇對象 selectById: function ( id ) { //如果選擇的是相機 if ( id === this.camera.id ) { this.select( this.camera ); return; } this.select( this.scene.getObjectById( id, true ) ); }, //通過id選擇對象 selectByUuid: function ( uuid ) { var scope = this; this.scene.traverse( function ( child ) { if ( child.uuid === uuid ) { scope.select( child ); } } ); }, //撤銷選擇對象 deselect: function () { this.select( null ); }, //聚焦對象 focus: function ( object ) { this.signals.objectFocused.dispatch( object ); }, //通過id聚焦對象 focusById: function ( id ) { this.focus( this.scene.getObjectById( id, true ) ); }, clear: function () { //清除歷史、清除存儲 this.history.clear(); this.storage.clear(); //相機默認、北京默認、霧默認 this.camera.copy( this.DEFAULT_CAMERA ); this.scene.background.setHex( 0xaaaaaa ); 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.animations = {}; this.mixer.stopAllAction(); //選擇置空 this.deselect(); //清除所有的編輯項 this.signals.editorCleared.dispatch(); }, // //反序列化 fromJSON: function ( json ) { //創建對象解析器 var loader = new THREE.ObjectLoader(); // backwards // 場景未定義,創建一個場景,然後返回 if ( json.scene === undefined ) { // this.setScene( loader.parse( json ) ); return; } //解析相機 var camera = loader.parse( json.camera ); //設置相機參數 this.camera.copy( camera ); this.camera.aspect = this.DEFAULT_CAMERA.aspect; this.camera.updateProjectionMatrix(); //解析歷史、腳本 this.history.fromJSON( json.history ); this.scripts = json.scripts; //解析場景 this.setScene( loader.parse( json.scene ) ); }, toJSON: function () { // scripts clean up //獲取場景、腳本 var scene = this.scene; var scripts = this.scripts; //遍歷場景中所有腳本的key-value(對象名稱--腳本) 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' ), //工程的陰影設置 vr: this.config.getKey( 'project/vr' ) //是否vr }, camera: this.camera.toJSON(), //相機的信息 scene: this.scene.toJSON(), //場景的信息 scripts: this.scripts, //腳本的信息 history: this.history.toJSON() //歷史信息 }; }, objectByUuid: function ( uuid ) { //根據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(); } };