本章講解場景編輯器的三維操作窗口editor\js\Viewport.js,這個文件中包含了所有三維物體的展示、操作的相關內容。
包含的內容如下:
- 三維展示的窗口container
- 選中對象的統計窗口Viewport.Info
- 輔助網grid
- 物體的輔助包圍體selectionBox
- 操作物體(移動、縮放、旋轉)的transformControls
- 相機的控制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; };