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

本章講解場景編輯器的三維操作窗口editor\js\Viewport.js,這個文件中包含了所有三維物體的展示、操作的相關內容。

包含的內容如下:

  1. 三維展示的窗口container
  2. 選中對象的統計窗口Viewport.Info
  3. 輔助網grid
  4. 物體的輔助包圍體selectionBox
  5. 操作物體(移動、縮放、旋轉)的transformControls
  6. 相機的控制EditorControls
    //編輯器的三維操作窗口(所有三維渲染相關的都在這裏)
    var Viewport = function ( editor ) {
    	//編輯器的所有信號
    	var signals = editor.signals;
    
    	//編輯器三維容器\設置div的id\設置div的樣式
    	var container = new UI.Panel();
    	container.setId( 'viewport' );
    	container.setPosition( 'absolute' );
    	//添加模型的統計信息面板
    	container.add( new Viewport.Info( editor ) );
    
    	//創建一個渲染器
    	var renderer = null;
    	//獲取相機、場景
    	var camera = editor.camera;
    	var scene = editor.scene;
    	//獲取輔助信息場景
    	var sceneHelpers = editor.sceneHelpers;
    
    	//場景中存儲的所有對象
    	var objects = [];
    
    	// helpers
    	//場景中的輔助網格
    	var grid = new THREE.GridHelper( 30, 30, 0x444444, 0x888888 );
    	sceneHelpers.add( grid );
    
    	//設置網格的顏色
    	var array = grid.geometry.attributes.color.array;
    	for ( var i = 0; i < array.length; i += 60 ) {
    		for ( var j = 0; j < 12; j ++ ) {
    			array[ i + j ] = 0.26;
    		}
    	}
    
    	//
    
    	var box = new THREE.Box3();
    
    	//選擇物體的包圍盒
    	var selectionBox = new THREE.BoxHelper();
    	selectionBox.material.depthTest = false;	//禁用深度測試
    	selectionBox.material.transparent = true;	//包圍盒頭透明
    	selectionBox.visible = false;				//不可見
    	sceneHelpers.add( selectionBox );			//添加到輔助場景當中
    
    	//
    	var objectPositionOnDown = null;
    	var objectRotationOnDown = null;
    	var objectScaleOnDown = null;
    
    	//旋轉、平移、縮放的控制按鈕
    	var transformControls = new THREE.TransformControls( camera, container.dom );
    
    	//當變換控制改變時會引起包圍盒變化、側邊欄改變
    	transformControls.addEventListener( 'change', function () {
    		//控制中的對象
    		var object = transformControls.object;
    		//對象不爲空
    		if ( object !== undefined ) {
    			//關聯選中的對象
    			selectionBox.setFromObject( object );
    
    			//更新對象(相機、燈)
    			if ( editor.helpers[ object.id ] !== undefined ) {
    				//更新輔助信息
    				editor.helpers[ object.id ].update();
    			}
    
    			//更新側邊欄
    			signals.refreshSidebarObject3D.dispatch( object );
    		}
    
    		//渲染
    		render();
    	} );
    
    	//當“變換控制”鼠標按下時,獲取對象的變換參量
    	transformControls.addEventListener( 'mouseDown', function () {
    		//獲取對象
    		var object = transformControls.object;
    		//獲取對象的位置、旋轉、縮放
    		objectPositionOnDown = object.position.clone();
    		objectRotationOnDown = object.rotation.clone();
    		objectScaleOnDown = object.scale.clone();
    		//禁用場景中相機控制
    		controls.enabled = false;
    	} );
    
    	//當“變換控制”鼠標擡起時,獲取對象的變換參量
    	transformControls.addEventListener( 'mouseUp', function () {
    		//獲取對象
    		var object = transformControls.object;
    		//對象存在
    		if ( object !== undefined ) {
    			//獲取當前的操作模式
    			switch ( transformControls.getMode() ) {
    				//平移
    				case 'translate':
    					//鼠標按下的位置和擡起的位置不相同
    					if ( ! objectPositionOnDown.equals( object.position ) ) {
    						//創建位置改變命令
    						editor.execute( new SetPositionCommand( object, object.position, objectPositionOnDown ) );
    					}
    					break;
    				//旋轉
    				case 'rotate':
    					//鼠標擡起時旋轉了
    					if ( ! objectRotationOnDown.equals( object.rotation ) ) {
    						//創建旋轉命令
    						editor.execute( new SetRotationCommand( object, object.rotation, objectRotationOnDown ) );
    					}
    
    					break;
    				//縮放
    				case 'scale':
    					//經過縮放了
    					if ( ! objectScaleOnDown.equals( object.scale ) ) {
    						//創建縮放命令
    						editor.execute( new SetScaleCommand( object, object.scale, objectScaleOnDown ) );
    					}
    					break;
    			}
    
    		}
    		//啓用場景中相機控制(轉向、拉遠拉近) EditorControls
    		controls.enabled = true;
    
    	} );
    	//將控制部件加入到場景當中
    	sceneHelpers.add( transformControls );
    
    	// object picking
    	//拾取對象的射線、鼠標位置
    	var raycaster = new THREE.Raycaster();
    	var mouse = new THREE.Vector2();
    
    	// events
    	//射線檢測
    	function getIntersects( point, objects ) {
    
    		mouse.set( ( point.x * 2 ) - 1, - ( point.y * 2 ) + 1 );
    		//射線檢測
    		raycaster.setFromCamera( mouse, camera );
    		//返回拾取的對象信息
    		return raycaster.intersectObjects( objects );
    
    	}
    
    	//鼠標按下、擡起、雙擊位置
    	var onDownPosition = new THREE.Vector2();
    	var onUpPosition = new THREE.Vector2();
    	var onDoubleClickPosition = new THREE.Vector2();
    
    	//獲取鼠標的位置
    	function getMousePosition( dom, x, y ) {
    
    		var rect = dom.getBoundingClientRect();
    		return [ ( x - rect.left ) / rect.width, ( y - rect.top ) / rect.height ];
    
    	}
    
    	//處理鼠標點擊事件
    	function handleClick() {
    		//如果鼠標點擊位置與鼠標擡起位置不變
    		if ( onDownPosition.distanceTo( onUpPosition ) === 0 ) {
    			//獲取射線檢測對象
    			var intersects = getIntersects( onUpPosition, objects );
    
    			if ( intersects.length > 0 ) {
    
    				var object = intersects[ 0 ].object;
    				//對象有自定義數據(燈的輔助對象)
    				if ( object.userData.object !== undefined ) {
    
    					// helper
    					//選擇對象(燈的輔助對象)
    					editor.select( object.userData.object );
    
    				} else {
    					//選擇對象
    					editor.select( object );
    				}
    
    			} else {
    				//不選擇對象
    				editor.select( null );
    			}
    			//刷新
    			render();
    		}
    
    	}
    
    	//鼠標按下
    	function onMouseDown( event ) {
    		//阻止觸發dom默認事件
    		event.preventDefault();
    		//獲取鼠標位置
    		var array = getMousePosition( container.dom, event.clientX, event.clientY );
    		onDownPosition.fromArray( array );
    		//添加鼠標擡起的監聽
    		document.addEventListener( 'mouseup', onMouseUp, false );
    
    	}
    	//鼠標擡起
    	function onMouseUp( event ) {
    		//獲取鼠標位置
    		var array = getMousePosition( container.dom, event.clientX, event.clientY );
    		onUpPosition.fromArray( array );
    		//處理鼠標點擊
    		handleClick();
    		//移除鼠標擡起事件
    		document.removeEventListener( 'mouseup', onMouseUp, false );
    	}
    
    	function onTouchStart( event ) {
    
    		var touch = event.changedTouches[ 0 ];
    
    		var array = getMousePosition( container.dom, touch.clientX, touch.clientY );
    		onDownPosition.fromArray( array );
    
    		document.addEventListener( 'touchend', onTouchEnd, false );
    
    	}
    
    	function onTouchEnd( event ) {
    
    		var touch = event.changedTouches[ 0 ];
    
    		var array = getMousePosition( container.dom, touch.clientX, touch.clientY );
    		onUpPosition.fromArray( array );
    
    		handleClick();
    
    		document.removeEventListener( 'touchend', onTouchEnd, false );
    
    	}
    
    	//雙擊鼠標
    	function onDoubleClick( event ) {
    		//獲取鼠標的位置
    		var array = getMousePosition( container.dom, event.clientX, event.clientY );
    		//雙擊位置
    		onDoubleClickPosition.fromArray( array );
    		//射線檢測
    		var intersects = getIntersects( onDoubleClickPosition, objects );
    		if ( intersects.length > 0 ) {
    			//
    			var intersect = intersects[ 0 ];
    			//物體添加焦點
    			signals.objectFocused.dispatch( intersect.object );
    		}
    
    	}
    
    	//註冊鼠標按下、觸摸屏、雙擊事件
    	container.dom.addEventListener( 'mousedown', onMouseDown, false );
    	container.dom.addEventListener( 'touchstart', onTouchStart, false );
    	container.dom.addEventListener( 'dblclick', onDoubleClick, false );
    
    	// controls need to be added *after* main logic,
    	// otherwise controls.enabled doesn't work.
    	// 相機的控制類
    	var controls = new THREE.EditorControls( camera, container.dom );
    	controls.addEventListener( 'change', function () {
    		//相機觀察的目標改變了,導致相機的位置、方向改變了,會派發消息,重新渲染
    		signals.cameraChanged.dispatch( camera );
    	} );
    
    	// signals
    	// 關閉程序時清除編輯器
    	signals.editorCleared.add( function () {
    		//重新設置相機控制的中心
    		controls.center.set( 0, 0, 0 );
    		render();
    	} );
    
    	//轉換模式改變了,應該是平移、旋轉、縮放模式改變了
    	signals.transformModeChanged.add( function ( mode ) {
    		//設置轉換模式
    		transformControls.setMode( mode );
    	} );
    
    	//??
    	signals.snapChanged.add( function ( dist ) {
    
    		transformControls.setTranslationSnap( dist );
    
    	} );
    
    	//空間改變(世界空間、本地空間)
    	signals.spaceChanged.add( function ( space ) {
    		//設置空間
    		transformControls.setSpace( space );
    	} );
    
    	//渲染方式改變了(webglrender、cssrender、softrender、raytrackrender)
    	signals.rendererChanged.add( function ( newRenderer ) {
    
    		if ( renderer !== null ) {
    			//移除渲染控件
    			container.dom.removeChild( renderer.domElement );
    
    		}
    		//設置新的渲染方式
    		renderer = newRenderer;
    		//禁用自動清除
    		renderer.autoClear = false;
    		//手動更新場景,不在自動更新scene.updateMatrixWorld()
    		renderer.autoUpdateScene = false;
    		//gamma矯正
    		renderer.gammaOutput = true;
    		//屏幕密度
    		renderer.setPixelRatio( window.devicePixelRatio );
    		//渲染視口大小
    		renderer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
    		//添加dom節點
    		container.dom.appendChild( renderer.domElement );
    		//渲染
    		render();
    	} );
    
    	//場景圖改變了(場景樹)
    	signals.sceneGraphChanged.add( function () {
    		//渲染
    		render();
    	} );
    
    	//相機改變了
    	signals.cameraChanged.add( function () {
    		//渲染
    		render();
    
    	} );
    
    	//爲對象信號添加處理函數(爲什麼要在viewport中處理,因爲viewport是一個窗口,包含了所有的可視化的東西)
    	signals.objectSelected.add( function ( object ) {
    		//首先隱藏原來對象的包圍盒
    		selectionBox.visible = false;
    		//剔除原來關聯的對象
    		transformControls.detach();
    		//現在的對象不是null、不是scene、不是camera
    		if ( object !== null && object !== scene && object !== camera ) {
    			//將輔助盒子關聯到新選擇的對象當中
    			box.setFromObject( object );
    			//如果盒子不是空的
    			if ( box.isEmpty() === false ) {
    				//顯示物體的盒子,並可見
    				selectionBox.setFromObject( object );
    				selectionBox.visible = true;
    			}
    			//關聯對象
    			transformControls.attach( object );
    		}
    		//渲染
    		render();
    	} );
    
    	//對象焦點改變
    	signals.objectFocused.add( function ( object ) {
    		controls.focus( object );
    	} );
    
    	//幾何數據改變(半徑等)
    	signals.geometryChanged.add( function ( object ) {
    		if ( object !== undefined ) {
    			//選擇對象的包圍盒
    			selectionBox.setFromObject( object );
    		}
    		render();
    	} );
    
    	//場景中添加對象後會觸發事件處理
    	signals.objectAdded.add( function ( object ) {
    		//遍歷所有的對象
    		object.traverse( function ( child ) {
    			//存儲對象
    			objects.push( child );
    		} );
    	} );
    
    	//對象改變
    	signals.objectChanged.add( function ( object ) {
    
    		if ( editor.selected === object ) {	//當前選擇的對象
    			//添加包圍盒
    			selectionBox.setFromObject( object );
    
    		}
    		//如果對象是透視相機
    		if ( object.isPerspectiveCamera ) {
    			//更新投影矩陣
    			object.updateProjectionMatrix();
    		}
    
    		//輔助信息
    		if ( editor.helpers[ object.id ] !== undefined ) {
    			//更新對象的輔助對象
    			editor.helpers[ object.id ].update();
    
    		}
    		//重新渲染
    		render();
    
    	} );
    
    	//刪除對象
    	signals.objectRemoved.add( function ( object ) {
    
    		if ( object === transformControls.object ) {	//當前對象正在被操作
    			transformControls.detach();		//解除平移、旋轉、縮放
    		}
    
    		//遍歷對象、刪除對象
    		object.traverse( function ( child ) {
    			objects.splice( objects.indexOf( child ), 1 );
    		} );
    
    	} );
    
    	//添加對象的輔助對象時會觸發這個事件處理
    	signals.helperAdded.add( function ( object ) {
    		//將對象的拾取對象也添加到objects中(例如燈的輔助虛擬拾取對象)
    		objects.push( object.getObjectByName( 'picker' ) );
    	} );
    
    	//移除輔助信息
    	signals.helperRemoved.add( function ( object ) {
    		//移除輔助模型,如果有虛擬的拾取模型也一併移除
    		objects.splice( objects.indexOf( object.getObjectByName( 'picker' ) ), 1 );
    
    	} );
    
    	//材質改變
    	signals.materialChanged.add( function ( material ) {
    
    		render();
    
    	} );
    
    	// fog
    	//場景的背景改變
    	signals.sceneBackgroundChanged.add( function ( backgroundColor ) {
    		//設置背景顏色
    		scene.background.setHex( backgroundColor );
    
    		render();
    
    	} );
    
    	//當前霧的類型
    	var currentFogType = null;
    	//場景中的霧改變
    	signals.sceneFogChanged.add( function ( fogType, fogColor, fogNear, fogFar, fogDensity ) {
    
    		if ( currentFogType !== fogType ) {		//設置霧的類型
    			switch ( fogType ) {
    				case 'None':
    					scene.fog = null;
    					break;
    				case 'Fog':
    					scene.fog = new THREE.Fog();
    					break;
    				case 'FogExp2':
    					scene.fog = new THREE.FogExp2();
    					break;
    			}
    			currentFogType = fogType;
    		}
    
    		if ( scene.fog.isFog ) {	//普通霧
    			//設置霧的顏色、霧的近、遠距離
    			scene.fog.color.setHex( fogColor );
    			scene.fog.near = fogNear;
    			scene.fog.far = fogFar;
    
    		} else if ( scene.fog.isFogExp2 ) {	//高級霧
    			//設置霧的顏色、衰減
    			scene.fog.color.setHex( fogColor );
    			scene.fog.density = fogDensity;
    
    		}
    
    		render();
    
    	} );
    
    	//
    	//窗口大小改變響應
    	signals.windowResize.add( function () {
    
    		// TODO: Move this out?
    		//更新默認相機(DEFAULT_CAMERA 和 camera是同一個相機)
    		editor.DEFAULT_CAMERA.aspect = container.dom.offsetWidth / container.dom.offsetHeight;
    		editor.DEFAULT_CAMERA.updateProjectionMatrix();
    		//更新相機
    		camera.aspect = container.dom.offsetWidth / container.dom.offsetHeight;
    		camera.updateProjectionMatrix();
    
    		//視口大小改變
    		renderer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
    
    		render();
    
    	} );
    
    	//網格的顯示和隱藏
    	signals.showGridChanged.add( function ( showGrid ) {
    		grid.visible = showGrid;
    		render();
    	} );
    
    	// animations
    
    	var prevTime = performance.now();
    	//動畫
    	function animate( time ) {
    		//動畫循環
    		requestAnimationFrame( animate );
    		//動畫
    		var mixer = editor.mixer;
    		//動畫狀態時正在被使用時才實時更新
    		if ( mixer.stats.actions.inUse > 0 ) {
    			//更新、渲染(非實時渲染)
    			mixer.update( ( time - prevTime ) / 1000 );
    			render();
    		}
    		prevTime = time;
    	}
    
    	//開啓動畫
    	requestAnimationFrame( animate );
    
    	//
    	//重新渲染
    	function render() {
    		//更新輔助場景矩陣
    		sceneHelpers.updateMatrixWorld();
    		//更新場景矩陣
    		scene.updateMatrixWorld();
    		//渲染場景
    		renderer.render( scene, camera );
    		//
    		if ( renderer instanceof THREE.RaytracingRenderer === false ) {
    			//渲染輔助信息
    			renderer.render( sceneHelpers, camera );
    		}
    	}
    	//返回一個容器
    	return container;
    };
    

     

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