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;
    };
    

     

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值