/** * @author mschuetz / http://mschuetz.at * * adapted from THREE.OrbitControls by * * @author qiao / https://github.com/qiao * @author mrdoob / http://mrdoob.com * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author erich666 / http://erichaines.com * * * */ import * as THREE from "../../libs/three.js/build/three.module.js"; import {Utils} from "../utils.js"; import cameraLight from "../custom/utils/cameraLight.js"; import Common from "../custom/utils/Common.js"; import math from "../custom/utils/math.js"; let Buttons = Potree.defines.Buttons export class FirstPersonControls extends THREE.EventDispatcher { constructor (viewer, viewport) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.scene = viewer.scene; this.rotationSpeed = 200; this.moveSpeed = 10; this.setCurrentViewport({hoverViewport:viewport, force:true}) //this.currentViewport = viewport this.keys = { FORWARD: ['W'.charCodeAt(0), 38], BACKWARD: ['S'.charCodeAt(0), 40], LEFT: ['A'.charCodeAt(0), 37], RIGHT: ['D'.charCodeAt(0), 39], UP: ['Q'.charCodeAt(0)], DOWN: ['E'.charCodeAt(0)], //SHIFT : [16], ALT : [18], Rotate_LEFT : ['L'.charCodeAt(0)], Rotate_RIGHT : ['J'.charCodeAt(0)], Rotate_UP : ['K'.charCodeAt(0)], Rotate_DOWN : ['I'.charCodeAt(0)], }; this.fadeFactor = 20; this.yawDelta = 0; this.pitchDelta = 0; this.translationDelta = new THREE.Vector3(0, 0, 0); this.translationWorldDelta = new THREE.Vector3(0, 0, 0); this.tweens = []; this.dollyStart = new THREE.Vector2 this.dollyEnd = new THREE.Vector2 //this.enableChangePos = true this.viewer.addEventListener('camera_changed',(e)=>{ if(this.viewer.name == 'mapViewer' || e.changeInfo && e.changeInfo.positionChanged && !viewer.mainViewport.view.isFlying() ){ this.setFPCMoveSpeed(e.viewport) } }) let drag = (e) => { if(!this.enabled)return let viewport = e.dragViewport; if(!viewport)return let camera = viewport.camera let mode if(e.isTouch){ if(e.touches.length == 1){ mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan' }else if(e.touches.length == 2){ mode = Potree.settings.displayMode == 'showPanos' ? 'scale' : 'pan-scale' }else{ mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'pan' : 'scale' } }else{ //mode = e.buttons === Buttons.LEFT && (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan' mode = e.buttons === Buttons.LEFT && camera.type != 'OrthographicCamera' ? 'rotate' : 'pan' } //console.log('mode ', mode ) let moveSpeed = this.currentViewport.getMoveSpeed(); if (e.drag.startHandled === undefined) {///??????? e.drag.startHandled = true; this.dispatchEvent({type: 'start'}); } if (mode.includes('rotate')) {//旋转 //来自panoramaControl updateRotation if(!this.pointerDragStart){ return this.pointerDragStart = e.pointer.clone() } let view = this.scene.view; if(this.rotateStartInfo.rotAroundPoint){//定点旋转: 以当前intersect的点为target旋转,不改点在屏幕中的位置 let distance = camera.position.distanceTo(this.rotateStartInfo.rotCenter/* this.intersectStart.location */) //不按下ctrl的话 if(this.rotateStartInfo.rotCenter2d.z>1)distance *= -1 //在背面 //按照orbitControl的方式旋转: let rotationSpeed = 2; this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed; this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed; /* //旋转方向和偏移量尽量和在漫游点处旋转方式的一样 this.yawDelta += e.drag.pointerDelta.x / 500 * viewport.resolution.x this.pitchDelta -= e.drag.pointerDelta.y / 500 * viewport.resolution.y */ //先更新一下相机: this.update() view.applyToCamera(camera) //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。//感觉貌似也可以用project和unproject后的差值的方式,还不用判断z背面 let newPointerDir = viewer.inputHandler.getMouseDirection(this.rotateStartInfo.rotCenter2d).direction.clone().multiplyScalar(distance) let pivot = new THREE.Vector3().addVectors(camera.position, newPointerDir) //新的3d点 let moveVec = new THREE.Vector3().subVectors(pivot, this.rotateStartInfo.rotCenter) this.translationWorldDelta.copy(moveVec.negate()) //立即更新下,防止因update和此drag频率不同而打滑。 this.update() view.applyToCamera(camera) }else{ let _matrixWorld = camera.matrixWorld camera.matrixWorld = new THREE.Matrix4;//unproject 前先把相机置于原点 var e1 = new THREE.Vector3(this.pointerDragStart.x,this.pointerDragStart.y,-1).unproject(camera) , t = new THREE.Vector3(e.pointer.x,e.pointer.y,-1).unproject(camera) , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z) , n = Math.sqrt(t.x * t.x + t.z * t.z) , o = Math.atan2(e1.y, i) , a = Math.atan2(t.y, n); this.pitchDelta += o - a //上下旋转 e1.y = 0, t.y = 0; var s = Math.acos(e1.dot(t) / e1.length() / t.length()); if(!isNaN(s)){ var yawDelta = s //左右旋转 this.pointerDragStart.x > e.pointer.x && (yawDelta *= -1) this.yawDelta += yawDelta } //console.log('rotate:', this.pitchDelta, e.pointer.toArray(), this.pointerDragStart.toArray()) this.pointerDragStart.copy(e.pointer) camera.matrixWorld = _matrixWorld ; } } if (mode.includes('pan')) {//平移 if(!this.canMovePos(viewport)){ return } if(camera.type == "OrthographicCamera"){ //console.log(e.drag.pointerDelta, e.pointer, e.drag.end) let moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera )//最近一次移动向量 let pointclouds; let Alignment = window.viewer.modules.Alignment let MergeEditor = window.viewer.modules.MergeEditor let handleState = Alignment.handleState //右键平移视图、左键操作点云 let a = e.buttons === Buttons.LEFT && viewport.alignment && handleState && viewport.alignment[handleState] if(Potree.settings.editType == 'pano'){ let PanoEditor = window.viewer.modules.PanoEditor if(a && PanoEditor.selectedPano){ if(/* !PanoEditor.selectedGroup || */!PanoEditor.checkIfAllLinked({group:PanoEditor.selectedGroup}) ){ if(handleState == 'translate' && ( e.drag.intersectStart.pointclouds && Common.getMixedSet(PanoEditor.selectedClouds, e.drag.intersectStart.pointclouds).length || PanoEditor.selectedPano.hovered)//平移时 拖拽到点云上 或 circle。(其中点云只需要intersect的点云中包含选择的点云中之一即可) || handleState == 'rotate' ) //旋转模式不需要intersect { pointclouds = PanoEditor.selectedClouds } }else{ PanoEditor.dispatchEvent('needToDisConnect') console.warn('选中的漫游点连通了整个数据集,不允许移动') } } if(!pointclouds && e.buttons === Buttons.LEFT && viewport.rotateSide){//侧视图 (有时候会卡顿,是mousemove执行延迟了,一般发生在突然加载很多点云时) //console.log('rotateSide', -e.drag.pointerDelta.x ) return PanoEditor.rotateSideCamera(-e.drag.pointerDelta.x) } }else if(Potree.settings.editType == 'merge'){ if(e.buttons === Buttons.LEFT && viewport.rotateSide){ return MergeEditor.rotateSideCamera(-e.drag.pointerDelta.x) } }else{ /* if(Alignment.selectedClouds && Alignment.selectedClouds.length){//多个点云 pointclouds = a && e.drag.intersectStart.pointclouds && Common.getMixedSet(Alignment.selectedClouds, e.drag.intersectStart.pointclouds).length && Alignment.selectedClouds }else{ */ //pointclouds = a && e.drag.intersectStart.pointcloud && [e.drag.intersectStart.pointcloud] //} if(a && e.drag.intersectStart.pointcloud){ pointclouds = [e.drag.intersectStart.pointcloud] if(e.drag.intersectStart.pointcloud.dataset_id == Potree.settings.originDatasetId){ let p = e.drag.intersectStart.pointclouds.find(p=>p.dataset_id != Potree.settings.originDatasetId) if(p) pointclouds = [p] } } } if(pointclouds){ if(handleState == 'translate' && viewport.alignment.translateVec){//只能沿某个方向移动 moveVec.projectOnVector(viewport.alignment.translateVec) } this.dispatchEvent({ type : "transformPointcloud", intersect: e.intersect.orthoIntersect, intersectStart: e.drag.intersectStart.orthoIntersect, moveVec, pointclouds, camera }) }else{ this.translationWorldDelta.add(moveVec.negate()) } }else{ //perspectiveCamera: if(e.drag.intersectStart){//如果拖拽着点云 let ifInit = e.drag.z == void 0 let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置 e.drag.z = pointerStartPos2d.z //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变(如果拖拽过程中没有缩放,这个z其实不变) if(ifInit){//拖拽开始 e.drag.projectionMatrixInverse = camera.projectionMatrixInverse.clone() //防止吸附到最近点上(因为鼠标所在位置并非识别到的点云点的位置,需要得到鼠标所在位置的3d坐标。) let pointerStartPos2dReal = new THREE.Vector3(this.pointerDragStart.x,this.pointerDragStart.y, e.drag.z); e.drag.translateStartPos = pointerStartPos2dReal.clone().unproject(camera); //console.log('开始拖拽', e.pointer.clone()) } //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。 var _projectionMatrixInverse = camera.projectionMatrixInverse; camera.projectionMatrixInverse = e.drag.projectionMatrixInverse; let newPos2d = new THREE.Vector3(e.pointer.x,e.pointer.y, e.drag.z ); let newPos3d = newPos2d.clone().unproject(camera); let moveVec = newPos3d.clone().sub( e.drag.translateStartPos /* e.drag.intersectStart.location */ );//移动相机,保持鼠标下的位置永远不变,所以用鼠标下的新位置减去鼠标下的原始位置 camera.projectionMatrixInverse = _projectionMatrixInverse this.translationWorldDelta.copy(moveVec.negate()) //这里没法用add,原因未知,打开console时会跳动 //console.log('pan 1', this.translationWorldDelta.clone()) //四指松开剩三指时会偏移一下,暂不知道哪里的问题,或许跟开头防止点云吸附有关? }else{ //如果鼠标没有找到和点云的交点,就假设移动整个模型(也可以去扩大范围寻找最近点云) /* let center = viewer.scene.pointclouds[0].position; let radius = camera.position.distanceTo(center); let ratio = radius * Math.tan(THREE.Math.degToRad(camera.fov)/2) / 1000 */ /* let speed = this.currentViewport.getMoveSpeed() if(FirstPersonControls.boundPlane){ speed = FirstPersonControls.boundPlane.distanceToPoint(this.currentViewport.position) speed = Math.max(1 , speed) } */ let lastIntersect = this.target || viewport.lastIntersect && (viewport.lastIntersect.location || viewport.lastIntersect)//该viewport的最近一次鼠标和点云的交点 if(!lastIntersect || !(lastIntersect instanceof THREE.Vector3))lastIntersect = viewer.bound.center let speed = camera.position.distanceTo(lastIntersect) let fov = cameraLight.getHFOVForCamera(camera, true) let ratio = speed * Math.tan(fov/2) this.translationDelta.x -= e.drag.pointerDelta.x * ratio this.translationDelta.z -= e.drag.pointerDelta.y * ratio //console.log('pan2', e.drag.pointerDelta) } } this.useAttenuation = false } if(mode.includes('scale')){//触屏缩放 this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer); //if(!this.dollyStart)return var scale = this.dollyEnd.length() / this.dollyStart.length() let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点 dolly({ pointer, scale, camera, drag:e.drag }) this.dollyStart.copy(this.dollyEnd); } //最好按ctrl可以变为dollhouse的那种旋转 }; let drop = e => { if(!this.enabled)return this.dispatchEvent({type: 'end'}); }; let dolly = (e={})=>{ if(Potree.settings.displayMode == 'showPanos' && this.currentViewport == viewer.mainViewport/* this.currentViewport.unableChangePos */){//全景时 this.dispatchEvent({type:'dollyStopCauseUnable',delta:e.delta, scale:e.scale}) return } let camera = e.camera if(camera.type == "OrthographicCamera"){ let ratio if(e.delta != void 0){//滚轮缩放 if(e.delta == 0){//mac return }else if (e.delta < 0) { ratio = 0.9 } else if (e.delta > 0) { ratio = 1.12 //增加的需要比减少的多一些,否则缩放相同次数下,r0 * ([1+s)(1-s)]^n = r0 * (1-s^2)^n < r0 } }else{ ratio = e.scale //触屏缩放 } let zoom = camera.zoom * ratio let limit = camera.zoomLimit if(limit) zoom = THREE.Math.clamp(zoom, limit.min,limit.max ) let pointerPos = new THREE.Vector3(e.pointer.x, e.pointer.y,0.5); let oldPos = pointerPos.clone().unproject(camera); if(camera.zoom != zoom){ camera.zoom = zoom camera.updateProjectionMatrix() } let newPos = pointerPos.clone().unproject(camera); //定点缩放, 恢复一下鼠标所在位置的位置改变量 let moveVec = new THREE.Vector3().subVectors(newPos,oldPos) this.translationWorldDelta.add(moveVec.negate()) this.useAttenuation = false }else{ let speed = this.currentViewport.getMoveSpeed() * 15, direction if(e.delta != void 0){//滚轮缩放 if(e.delta == 0)return //mac /*if(this.target && !e.intersect){//如果没有intersect点云且有target的话,就朝target的方向. 但无限靠近时有问题,且到背面时前进却是后退 direction = new THREE.Vector3().subVectors(this.target, camera.position).normalize() }else{ */ direction = this.viewer.inputHandler.getMouseDirection().direction //定点缩放 if(e.intersect && e.intersect.location){//和intersect的墙越接近,速度越慢,便于focus细节 let dis = camera.position.distanceTo(e.intersect.location); speed = THREE.Math.clamp(dis * 0.1, 0.3, speed) } if (e.delta < 0) { speed *= -1 } this.useAttenuation = true }else{//触屏缩放 direction = this.viewer.inputHandler.getMouseDirection(e.pointer).direction //定点缩放 if(e.drag.intersectStart){//和intersect的墙越接近,速度越慢,便于focus细节 let dis = camera.position.distanceTo(e.drag.intersectStart.location); let r = 1-1/e.scale let closeMin = 0.1, standardMin = 0.001, disBound1 = 2, disBound2 = 5 if(math.closeTo(e.scale,1,0.03)){//如果偏差小于0.01,就不限制最小值,因为平移容易正负抖动,近距离有最小值的话抖动明显 closeMin = 0 //所以若缩放不明显(双指滑动慢),就不设置最低值。(这时候穿越障碍物会比较困难。) } //console.log('closeMin',closeMin) let min = math.linearClamp(dis, disBound1, disBound2, closeMin, standardMin) //触屏和滚轮不一样,触发较为连续,所以最小值设低一点。若要保持双指相对点云位置不变,理想最小值是0,但那样就无法穿越点云(最小值太小的话穿越密集点云如树丛很困难;太大会打滑)所以当离点云近时增大最小值 speed = Math.sign(r) * THREE.Math.clamp(dis * Math.abs(r), min, speed) //console.log(speed, dis, e.scale) }else{ this.useAttenuation = true let accelerate = 80; if(math.closeTo(e.scale,1,0.02)){//缩放小的时候很可能是双指平移时,容易抖动,所以降低移动速度 accelerate *= Math.min(40*Math.abs(e.scale-1), 0.8) } // console.log('accelerate',accelerate) const constantDis = this.currentViewport.getMoveSpeed() * accelerate //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整 speed = (e.scale-1)*constantDis } } var vec = direction.multiplyScalar(speed ) //this.translationWorldDelta.copy(vec) this.translationWorldDelta.add(vec) //console.log(direction.toArray(), speed, e.scale) } return true } let scroll = (e) => { if(!this.enabled || !e.hoverViewport)return this.setCurrentViewport(e) e.camera = e.hoverViewport.camera dolly(e) }; let dblclick = (e) => { if(!this.enabled)return if(!Potree.settings.dblToFocusPoint)return;//调试时才可双击 if(Potree.settings.displayMode == 'showPointCloud'/* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse); }; this.viewer.addEventListener('global_drag', drag); /* this.viewer.addEventListener('global_touchmove', (e)=>{ if(!this.enabled)return if(e.touches.length>1){//单指的就触发上一句 //console.log('global_touchmove' ) drag(e) } }); */ this.viewer.addEventListener('global_drop', drop); this.viewer.addEventListener('global_mousewheel', scroll); this.viewer.addEventListener('global_dblclick', dblclick); let prepareScale = (e)=>{//触屏的scale this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer); e.drag.camDisToPointStart = null } let prepareRotate = (e)=>{ this.pointerDragStart = e.pointer.clone() if(e.viewer.name != 'mainViewer' )return let intersect = e.intersect || e.dragViewport.lastIntersect //在数据集外部时绕中心点旋转,在数据集内部时绕intersect点旋转(其他数据集的点也可以) 或者 原地旋转镜头 let rotAroundPoint = Potree.settings.rotAroundPoint && e.dragViewport.camera.type != 'OrthographicCamera' && (viewer.atDatasets.length == 0 || intersect) && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[32] let rotCenter2d, rotCenter if(rotAroundPoint){ let pivotType = this.target ? 'target' : viewer.atDatasets.length > 0 ? 'intersect' : viewer.inputHandler.selection.length ? 'selection' : this.target2 ? 'target2' : 'boundCenter' rotCenter = pivotType == 'target'? this.target :pivotType == 'intersect' ? intersect.location : pivotType == 'selection' ? viewer.inputHandler.selection[0].position : pivotType == 'target2' ? this.target2 :viewer.bound.center if(rotCenter){ rotCenter2d = rotCenter.clone().project(e.dragViewport.camera) //点在屏幕中的位置 }else{ rotAroundPoint = false } } this.rotateStartInfo = { rotAroundPoint, //定点旋转 rotCenter, rotCenter2d } //缺点:多数据集绕中心点转很难操作,感觉可以也改为绕lastIntersect //console.log('prepareRotate' ) } let preparePan = (e)=>{//触屏的pan点云 还是会偏移 this.pointerDragStart = e.pointer.clone() e.drag.z = void 0 //清空 drag(e) //触屏点击时更新的pointer直接用一次drag //console.log('preparePan ' ) } this.viewer.addEventListener('global_mousedown'/* 'startDragging' */, (e)=>{ if(!this.enabled)return this.setCurrentViewport(e) prepareRotate(e) }) //注意,每次增减指头都会修改pointer,需要更新下状态 this.viewer.addEventListener('global_touchstart', (e)=>{ if(!this.enabled)return if(e.touches.length==2){//只监听开头两个指头 prepareScale(e) preparePan(e) }else if(e.touches.length>=3){ preparePan(e) } }) this.viewer.addEventListener('global_touchend', (e)=>{//e.touches是剩余的指头 if(!this.enabled)return if(e.touches.length==2){//停止平移,开始scale prepareScale(e) preparePan(e) }else if(e.touches.length==1){//停止scale,开始rotate prepareRotate(e) }else if(e.touches.length>=3){//重新准备下平移(因为抬起的指头可能包含平移使用的数据),否则抬起时漂移 preparePan(e) } }) /* this.viewer.addEventListener('enableChangePos', (e)=>{ if(!this.enabled)return this.enableChangePos = e.canLeavePano }) */ } canMovePos(viewport){ if(viewport == viewer.mainViewport && (Potree.settings.displayMode == 'showPanos' || viewer.images360.bumping || viewer.images360.latestToPano))return false else return true } setEnable(enabled){ this.enabled = enabled; } setTarget(target, index){//绕该点旋转,类似orbitControl if(index == 2)this.target2 = target else this.target = target } setFPCMoveSpeed(viewport){ if(viewport.camera.type == 'OrthographicCamera'){ let s = 1 / viewport.camera.zoom viewport.setMoveSpeed(s) }else{ //根据和漫游点的最短距离算moveSpeed。缺点:对于导入的无漫游点的数据集没有意义。 if(viewport == viewer.mainViewport && viewer.images360){ let position = viewer.mainViewport.view.position let speed let pano = viewer.images360.findNearestPano() if(!pano){ if(!viewer.bound)return let boundFloor = viewer.bound.boundingBox.clone(); boundFloor.max.z = boundFloor.min.z speed = boundFloor.distanceToPoint(viewer.mainViewport.view.position) speed = Math.sqrt(speed) / 50; }else{ let dis = pano.position.distanceTo(position) let minSpeed = 0.05, minDis = 3, multiplier = 0.005; speed = dis <= minDis ? minSpeed : minSpeed + (dis-minDis) * multiplier //console.log('dis', dis, 'speed', speed, pano.id ) } viewer.setMoveSpeed(speed) } //调试场景t-FhDWmV5xur 两个数据集,大的数据集没有漫游点。 } } setCurrentViewport(o={}){//add if(!this.enabled && !o.force )return if(o.hoverViewport && this.currentViewport != o.hoverViewport ){ this.currentViewport = o.hoverViewport //this.viewer.setMoveSpeed(this.currentViewport.radius/100); this.setFPCMoveSpeed(this.currentViewport) } if(this.currentViewport.camera.type == 'OrthographicCamera'){ this.lockElevationOri = true this.lockRotation = true }else{ this.lockElevationOri = false this.lockRotation = false } } setScene (scene) { this.scene = scene; } stop(){ this.yawDelta = 0; this.pitchDelta = 0; this.translationDelta.set(0, 0, 0); } zoomToLocation(mouse){ if(!this.enabled)return let camera = this.scene.getActiveCamera(); /* let I = Utils.getMousePointCloudIntersection( mouse, camera, this.viewer, this.scene.pointclouds); */ var I = this.viewer.inputHandler.intersect if (!I) { return; } let targetRadius = 0; { let minimumJumpDistance = 0.2; let domElement = this.renderer.domElement; let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer, camera); let {origin, direction} = this.viewer.inputHandler.getMouseDirection() let raycaster = new THREE.Raycaster(); raycaster.ray.set(origin, direction); let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray); let nodes2 = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, raycaster.ray); let lastNode = nodes[nodes.length - 1]; let radius = lastNode.getBoundingSphere(new THREE.Sphere()).radius; targetRadius = Math.min(this.scene.view.radius, radius); targetRadius = Math.max(minimumJumpDistance, targetRadius); } let d = this.scene.view.direction.multiplyScalar(-1); let cameraTargetPosition = new THREE.Vector3().addVectors(I.location, d.multiplyScalar(targetRadius)); // TODO Unused: let controlsTargetPosition = I.location; let animationDuration = 600; let easing = TWEEN.Easing.Quartic.Out; { // animate let value = {x: 0}; let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration); tween.easing(easing); this.tweens.push(tween); let startPos = this.scene.view.position.clone(); let targetPos = cameraTargetPosition.clone(); let startRadius = this.scene.view.radius; let targetRadius = cameraTargetPosition.distanceTo(I.location); tween.onUpdate(() => { let t = value.x; this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x; this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y; this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z; this.scene.view.radius = (1 - t) * startRadius + t * targetRadius; this.viewer.setMoveSpeed(this.scene.view.radius / 2.5); }); tween.onComplete(() => { this.tweens = this.tweens.filter(e => e !== tween); }); tween.start(); } } update (delta=1) { if(!this.enabled)return //console.log('update') let view = this.currentViewport.view { // cancel move animations on user input let changes = [ this.yawDelta, this.pitchDelta, this.translationDelta.length(), this.translationWorldDelta.length() ]; let changeHappens = changes.some(e => Math.abs(e) > 0.001); if (changeHappens && this.tweens.length > 0) { this.tweens.forEach(e => e.stop()); this.tweens = []; } } { // accelerate while input is given let ih = this.viewer.inputHandler; let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]); let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]); let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]); let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]); let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]); let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]); let rotateLeft = this.keys.Rotate_LEFT.some(e => ih.pressedKeys[e]); let rotateRight = this.keys.Rotate_RIGHT.some(e => ih.pressedKeys[e]); let rotateUp = this.keys.Rotate_UP.some(e => ih.pressedKeys[e]); let rotateDown = this.keys.Rotate_DOWN.some(e => ih.pressedKeys[e]); this.lockElevation = this.lockElevationOri || this.keys.ALT.some(e => ih.pressedKeys[e]); if(!this.lockRotation){ if(rotateLeft){ this.yawDelta -= 0.01 }else if(rotateRight){ this.yawDelta += 0.01 } if(rotateUp){ this.pitchDelta -= 0.01 }else if(rotateDown){ this.pitchDelta += 0.01 } } if(this.canMovePos(this.currentViewport) && !this.lockKey){ if(this.lockElevation){ let dir = view.direction; dir.z = 0; dir.normalize(); if (moveForward && moveBackward) { this.translationWorldDelta.set(0, 0, 0); } else if (moveForward) { this.translationWorldDelta.copy(dir.multiplyScalar(this.currentViewport.getMoveSpeed())); } else if (moveBackward) { this.translationWorldDelta.copy(dir.multiplyScalar(-this.currentViewport.getMoveSpeed())); } }else{ if (moveForward && moveBackward) { this.translationDelta.y = 0; } else if (moveForward) { this.translationDelta.y = this.currentViewport.getMoveSpeed(); } else if (moveBackward) { this.translationDelta.y = -this.currentViewport.getMoveSpeed(); } } if (moveLeft && moveRight) { this.translationDelta.x = 0; } else if (moveLeft) { this.translationDelta.x = -this.currentViewport.getMoveSpeed(); } else if (moveRight) { this.translationDelta.x = this.currentViewport.getMoveSpeed(); } if (moveUp && moveDown) { this.translationWorldDelta.z = 0; } else if (moveUp) { this.translationWorldDelta.z = this.currentViewport.getMoveSpeed(); } else if (moveDown) { this.translationWorldDelta.z = -this.currentViewport.getMoveSpeed(); } if(moveUp || moveDown || moveForward || moveBackward){ this.useAttenuation = false } } } { // apply rotation let yaw = view.yaw; let pitch = view.pitch; yaw += this.yawDelta /* * delta; */ pitch += this.pitchDelta/* * delta; */ view.yaw = yaw; view.pitch = pitch; if(this.yawDelta || this.pitchDelta){ view.cancelFlying('rotate') } this.yawDelta = 0 this.pitchDelta = 0 } /* if(this.translationWorldDelta.length()>0) { // console.log('translationDelta') } */ { // apply translation view.translate( this.translationDelta.x, /* * delta, */ this.translationDelta.y, /* * delta, */ this.translationDelta.z, /* * delta */ ); this.translationDelta.set(0,0,0) //if(this.translationWorldDelta.length())console.log(translationWorldDelta) view.translateWorld( this.translationWorldDelta.x /* * delta */, this.translationWorldDelta.y /* * delta */, this.translationWorldDelta.z /* * delta */ ); //this.translationWorldDelta.set(0,0,0) } { // set view target according to speed //view.radius = 1 * this.currentViewport.getMoveSpeed(); /* if(viewer.bound) view.radius = view.position.distanceTo(viewer.bound.center) let speed = view.radius/100; this.viewer.setMoveSpeed(speed); */ //this.setMoveSpeed() } if(this.useAttenuation){ //只有滚轮缩放时开启 let attenuation = Math.max(0, 1 - this.fadeFactor * delta); /*this.yawDelta *= attenuation; this.pitchDelta *= attenuation; this.translationDelta.multiplyScalar(attenuation);*/ this.translationWorldDelta.multiplyScalar(attenuation); }else{ this.translationWorldDelta.set(0,0,0) } } };