import * as THREE from "../../libs/three.js/build/three.module.js"; import {transitions, easing, lerp} from '../custom/utils/transitions.js' import math from '../custom/utils/math.js' import Common from '../custom/utils/Common.js' import {View} from './View.js' let sid = 0 class ExtendView extends View { constructor () { super() this.yaw = 0 //Math.PI / 4; // = 4dkk lon + 90 this._pitch = 0 //-Math.PI / 4; //上下旋转 = 4dkk lat this.sid = sid++ this.LookTransition = 'LookTransition'+this.sid this.FlyTransition = 'FlyTransition'+this.sid } //add------ applyToCamera(camera){ camera.position.copy(this.position); camera.rotation.copy(this.rotation) camera.updateMatrix(); camera.updateMatrixWorld(); //camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } get rotation(){ var rotation = new THREE.Euler; rotation.order = "ZXY"; rotation.x = Math.PI / 2 + this.pitch; rotation.z = this.yaw; return rotation } set rotation(rotation){//这个在数字很小(垂直向上看)的时候水平旋转精度可能损失,导致突变到另一个角度去了,用 set quaternion比较好 //因为 rotation的y不一定是0 , 所以不能直接逆着写。 this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation) } get quaternion(){ return new THREE.Quaternion().setFromEuler(this.rotation) } set quaternion(q){ this.direction = new THREE.Vector3(0,0,-1).applyQuaternion(q) } copy(a){ Common.CopyClassObject(this, a, {ignoreList: ['_listeners']}) } clone () { return Common.CloneClassObject(this, {ignoreList: ['_listeners']}) } //---------- setCubeView(dir) { switch(dir) { case "front": this.yaw = 0; this.pitch = 0; break; case "back": this.yaw = Math.PI; this.pitch = 0; break; case "left": this.yaw = -Math.PI / 2; this.pitch = 0; break; case "right": this.yaw = Math.PI / 2; this.pitch = 0; break; case "top": this.yaw = 0; this.pitch = -Math.PI / 2; break; case "bottom": this.yaw = -Math.PI; this.pitch = Math.PI / 2; break; } } /* pan (x, y) { let dir = new THREE.Vector3(0, 1, 0); dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch); dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); // let side = new THREE.Vector3(1, 0, 0); // side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); let side = this.getSide(); let up = side.clone().cross(dir); let pan = side.multiplyScalar(x).add(up.multiplyScalar(y)); this.position = this.position.add(pan); // this.target = this.target.add(pan); } */ pan (x, y) { //发现pan其实就是translate this.translate(x, 0, y) } translate (x, y, z, forceHorizon ) { //相机方向 let dir = new THREE.Vector3(0, 1, 0); dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), forceHorizon ? 0 : this.pitch); //上下角度 dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);//水平角度 let side = new THREE.Vector3(1, 0, 0); side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); //垂直于相机当前水平朝向的 左右方向 let up = side.clone().cross(dir); //垂直于相机当前水平朝向的 向上方向 let t = side.multiplyScalar(x) //x影响 左右分量 .add(dir.multiplyScalar(y)) //y影响 前后分量 .add(up.multiplyScalar(z)); //z影响 上下分量 this.position = this.position.add(t); if((!math.closeTo(x, 0, 1e-4) || !math.closeTo(y, 0, 1e-4) || !math.closeTo(z, 0, 1e-4)) && Potree.settings.displayMode != 'showPanos'){ this.cancelFlying('pos') } this.restrictPos() } translateWorld (x, y, z) { super.translateWorld(x, y, z) if((!math.closeTo(x, 0, 1e-4) || !math.closeTo(y, 0, 1e-4) || !math.closeTo(z, 0, 1e-4)) && Potree.settings.displayMode != 'showPanos'){ this.cancelFlying('pos') } this.restrictPos() } restrictPos(position){//add if(this.limitBound){ (position || this.position).clamp(this.limitBound.min, this.limitBound.max) } } isFlying(type='all'){ let a = transitions.getById(this.FlyTransition).length > 0 let b = transitions.getById(this.LookTransition).length > 0 return type == 'pos' ? a : type == 'rotate' ? b : (a || b) } cancelFlying(type='all', dealCancel=true){//外界只能通过这个来cancel type == 'pos' ? transitions.cancelById(this.FlyTransition, dealCancel ) : type == 'rotate' ? transitions.cancelById(this.LookTransition, dealCancel ) : (transitions.cancelById(this.FlyTransition, dealCancel ), transitions.cancelById(this.LookTransition, dealCancel )) //console.log('cancelFlying ' , this.sid, type) } setView( info = {}){ // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun this.cancelFlying() let posWaitDone, rotWaitDone let posDone = ()=>{ rotWaitDone || done() posWaitDone = false } let rotDone = ()=>{ if(endTarget){ this.lookAt(endTarget); //compute radius for orbitcontrol }else if(endQuaternion){ this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion) } posWaitDone || done() rotWaitDone = false } let done = ()=>{ //一定要旋转和位移都结束了才能执行 let f = ()=>{ info.callback && info.callback() this.dispatchEvent('flyingDone') } if(info.duration){ setTimeout(f,1)//延迟是为了使isFlying先为false }else{ f() //有的需要迅速执行回调 } } let endPosition = new THREE.Vector3().copy(info.position) let startPosition = this.position.clone(); let startQuaternion, endQuaternion, endTarget = null, needRot; this.restrictPos(endPosition) if(info.target ){ endTarget = new THREE.Vector3().copy(info.target) endQuaternion = math.getQuaFromPosAim(endPosition,endTarget) //若为垂直,会自动偏向x负的方向 let dir = new THREE.Vector3().subVectors(endTarget, endPosition).normalize() //console.log(dir, this.direction) let view = this.clone(); view.direction = dir; console.log(view.yaw, view.pitch, this.yaw, this.pitch) }else if(info.quaternion){ endQuaternion = info.quaternion.clone() } if(endQuaternion){ startQuaternion = new THREE.Quaternion().setFromEuler(this.rotation) needRot = endQuaternion /* const startTarget = this.getPivot(); let startQuaternion = math.getQuaFromPosAim(startPosition,startTarget) */ //console.log('startQuaternion',startQuaternion, this.rotation) //console.log('endQuaternion', endQuaternion, new THREE.Euler().setFromQuaternion(endQuaternion)) } if(!info.duration){ this.position.copy(endPosition); this.restrictPos() posWaitDone = true, rotWaitDone = true info.onUpdate && info.onUpdate(1) posDone() rotDone() }else{ info.onUpdate && info.onUpdate(0) //初始化progress let posChange = !this.position.equals(endPosition) if(posChange){ posWaitDone = true transitions.start(lerp.vector(this.position, endPosition, (pos, progress, delta)=>{ info.onUpdate && info.onUpdate(progress, delta) }), info.duration, posDone , 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.FlyTransition, ()=>{ //中途取消 if(endTarget ){ /* endPosition = new THREE.Vector3().copy(this.position)//更改旋转的endQuaternion endQuaternion = math.getQuaFromPosAim(endPosition,endTarget) */ //直接改变endQuaternion会突变,所以还是cancel吧 this.cancelFlying('rotate') } posWaitDone = false info.cancelFun && info.cancelFun() }, info.ignoreFirstFrame); } if(endQuaternion){ rotWaitDone = true transitions.start( (progress, delta )=>{ let quaternion = (new THREE.Quaternion()).copy(startQuaternion) lerp.quaternion(quaternion, endQuaternion)(progress) //在垂直的视角下的角度突变的厉害,这时候可能渐变yaw比较好 //this.rotation = new THREE.Euler().setFromQuaternion(quaternion) this.quaternion = quaternion posChange || info.onUpdate && info.onUpdate(progress, delta) }, info.duration, rotDone , 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.LookTransition, ()=>{ //中途取消 rotWaitDone = false info.cancelFun && info.cancelFun() }, info.ignoreFirstFrame); } /* transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{ let t = progress if(endQuaternion){ let quaternion = (new THREE.Quaternion()).copy(startQuaternion) lerp.quaternion(quaternion, endQuaternion)(progress), this.rotation = new THREE.Euler().setFromQuaternion(quaternion) } this.restrictPos() //console.log('setView flying') info.onUpdate && info.onUpdate(t)//add }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.LookTransition, info.cancelFun); //easeInOutQuad */ } } //平移Ortho相机 moveOrthoCamera(viewport, info, duration, easeName){//boundSize优先于endZoom。 let camera = viewport.camera let startZoom = camera.zoom let endPosition = info.endPosition let boundSize = info.boundSize let endZoom = info.endZoom let margin = info.margin || {x:0,y:0}/* 200 */ //像素 if(info.bound){//需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时 endPosition = endPosition || info.bound.getCenter(new THREE.Vector3()) let matrixRot = new THREE.Matrix4().makeRotationFromEuler(this.rotation).invert() let boundingBox = info.bound.clone().applyMatrix4(matrixRot) boundSize = boundingBox.getSize(new THREE.Vector3()) } if(boundSize && boundSize.x == 0 && boundSize.y == 0){ boundSize.set(1,1) //避免infinity } this.setView( Object.assign(info, { position:endPosition, duration, onUpdate:(progress)=>{ if(boundSize || endZoom){ if(boundSize){ let aspect = boundSize.x / boundSize.y let w, h; if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定 h = boundSize.y endZoom = (viewport.resolution.y - margin.y) / h //注意,要在resolution不为0时执行 }else{ w = boundSize.x; endZoom = (viewport.resolution.x - margin.x) / w } //onUpdate时更新endzoom是因为画布大小可能更改 } camera.zoom = endZoom * progress + startZoom * (1 - progress) camera.updateProjectionMatrix() } }, Easing:easeName })) } zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress){//定点缩放 let startZoom = camera.zoom let pointerPos = new THREE.Vector3(pointer.x, pointer.y,0.5); transitions.start(( progress)=>{ let oldPos = pointerPos.clone().unproject(camera); camera.zoom = endZoom * progress + startZoom * (1 - progress) camera.updateProjectionMatrix() let newPos = pointerPos.clone().unproject(camera); //定点缩放, 恢复一下鼠标所在位置的位置改变量 let moveVec = new THREE.Vector3().subVectors(newPos, oldPos) camera.position.sub(moveVec) this.position.copy(camera.position) onProgress && onProgress() } , duration, null/* done */, 0, easing.easeInOutSine, null, "zoomInView"/* , info.cancelFun */); } }; Object.assign(ExtendView.prototype, THREE.EventDispatcher.prototype) export {ExtendView}