import * as THREE from "../../libs/three.js/build/three.module.js"; import {transitions, easing, lerp} from '../utils/transitions.js' import math from '../utils/math.js' import Common from '../utils/Common.js' let sid = 0 export class View extends THREE.EventDispatcher{ constructor () { super() this.position = new THREE.Vector3(0, 0, 0); this.yaw = 0//Math.PI / 4; // = 4dkk lon + 90 this._pitch = 0//-Math.PI / 4; //上下旋转 = 4dkk lat this.radius = 1; this.maxPitch = Math.PI / 2; this.minPitch = -Math.PI / 2; this.sid = sid++ this.LookTransition = 'LookTransition'+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){ //因为 rotation的y不一定是0 , 所以不能直接逆着写。 this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation) } copy(a){ Common.CopyClassObject(this, a) } clone () { /* let c = new View(); c.yaw = this.yaw; c._pitch = this.pitch; c.radius = this.radius; c.maxPitch = this.maxPitch; c.minPitch = this.minPitch; return c; */ return Common.CloneClassObject(this) } //---------- get pitch () { return this._pitch; } set pitch (angle) { this._pitch = Math.max(Math.min(angle, this.maxPitch), this.minPitch); } get direction () { 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); return dir; } set direction (dir) { //if(dir.x === dir.y){ if(dir.x === 0 && dir.y === 0){ this.pitch = Math.PI / 2 * Math.sign(dir.z); }else{ let yaw = Math.atan2(dir.y, dir.x) - Math.PI / 2; let pitch = Math.atan2(dir.z, Math.sqrt(dir.x * dir.x + dir.y * dir.y)); this.yaw = yaw; this.pitch = pitch; } } lookAt(t){//setPivot let V; if(arguments.length === 1){ V = new THREE.Vector3().subVectors(t, this.position); }else if(arguments.length === 3){ V = new THREE.Vector3().subVectors(new THREE.Vector3(...arguments), this.position); } let radius = V.length(); let dir = V.normalize(); this.radius = radius; this.direction = dir; } getPivot () { return new THREE.Vector3().addVectors(this.position, this.direction.multiplyScalar(this.radius)); } getSide () { let side = new THREE.Vector3(1, 0, 0); side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); return side; } /* 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); this.restrictPos() } translateWorld (x, y, z) { this.position.x += x; this.position.y += y; this.position.z += z; this.restrictPos() } restrictPos(){//add if(this.limitBound){ this.position.clamp(this.limitBound.min, this.limitBound.max) } } 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; } } isFlying(){ return transitions.getById(this.LookTransition).length > 0 } cancelFlying(){//外界只能通过这个来cancel transitions.cancelById(this.LookTransition, true ); } setView( info = {}){ // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun this.cancelFlying() let done = ()=>{ if(endTarget){ this.lookAt(endTarget); //compute radius for orbitcontrol }else if(endQuaternion){ this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion) } 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 ; if(info.target ){ endTarget = new THREE.Vector3().copy(info.target) endQuaternion = math.getQuaFromPosAim(endPosition,endTarget) }else if(info.quaternion){ endQuaternion = info.quaternion.clone() } if(endQuaternion){ startQuaternion = new THREE.Quaternion().setFromEuler(this.rotation) /* const startTarget = this.getPivot(); let startQuaternion = math.getQuaFromPosAim(startPosition,startTarget) */ } if(!info.duration){ this.position.copy(endPosition); this.restrictPos() info.onUpdate && info.onUpdate(1) done() }else{ 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() info.onUpdate && info.onUpdate(t)//add }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine /*easeInOutQuad */,null, this.LookTransition, info.cancelFun); } } //平移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({ position:endPosition, duration, callback:()=>{//done }, 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 */); } };