three.js 場景編輯器 源碼解析(七)

    這一章講解整個編輯器中最重要的一個文件editor\js\Editor.js,這個文件是真個編輯器的上下文,是所有ui與對象交互的橋樑,相當於mvc模式中的controller。

    Editor.js包含的幾個重要的信息如下:

  1. 整個編輯器的相機DEFAULT_CAMERA
  2. 整個編輯器的場景this.scene
  3.  整個場景中輔助相關對象所在的場景this.sceneHelpers
  4. 編輯器中信息傳遞的介質this.signals
  5. 整個場景的資源信息包括this.object、this.geometries、this.materials、this.textures、this.scripts、this.animations、this.mixer、this.helpers
  6. 編輯器中當前關注的對象this.selected
  7. 編輯器的配置信息(初始化時默認配置)this.config
  8. 編輯器的歷史操作信息this.history
  9. 編輯器存儲相關信息this.storage
  10. 編輯器的ui文本信息this.strings
  11. 編輯器的文件加載器,主要是導入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();
    
    	}
    
    };
    

     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章