import * as THREE from "../../../libs/three.js/build/three.module.js"; import math from '../utils/math.js' import browser from '../utils/browser.js' import Viewport from '../viewer/Viewport.js' const texLoader = new THREE.TextureLoader() const circleGeo = new THREE.CircleGeometry(1.45,100); const sphereGeo = new THREE.SphereBufferGeometry(0.018,10,10); const magDisMin = 1;//相机离目标位置的距离的分界线,当离得远时要缩小fov以使看到的视野固定(望远镜效果) const magDisMax = 20 /* const radius_ = 0.2; //当相机离目标位置的距离>magDistance_时,希望看到的视野的半径 const maxFov = THREE.Math.radToDeg(Math.atan(radius_ / magDisMin )) * 2//提前计算出当相机离目标位置的距离= 2 ? ( window.screen.width * window.screen.height >= maxPX ? window.devicePixelRatio/1.2 : window.devicePixelRatio/1.5)*w : w) //触屏或高分辨率的可能要放大些。但在手机上不能太大 //console.log('width2dPX', width2dPX) export default class Magnifier extends THREE.Object3D {//放大镜or望远镜 constructor (viewer) { super() this.width = this.height = width2dPX/* * window.devicePixelRatio */; this.camera = new THREE.PerspectiveCamera(50, 1, 0.01, 10000); //fov aspect near far this.camera.up = new THREE.Vector3(0,0,1) this.viewport = new Viewport( null, this.camera, { left:0, bottom:0, width:1, height: 1, name:'magnifier' , cameraLayers:['magnifierContent'], pixelRatio:1 }) this.viewport.setResolution(this.width, this.height,0,0) { let density let sizeType let colorType let opacityBefore = new Map() let visiMap = new Map() this.viewport.beforeRender = ()=>{ viewer.scene.pointclouds.forEach(e=>{//因为更改pointDensity时会自动变opacity,所以这项最先获取 visiMap.set(e,e.visible) e.visible = Potree.Utils.getObjVisiByReason(e, 'datasetSelection'); //先将隐藏的点云显示 opacityBefore.set(e,e.temp.pointOpacity) }) //使放大镜里的pointDensity是'magnifier' 最高质量。 density = Potree.settings.pointDensity Potree.settings.pointDensity = 'magnifier' viewer.scene.pointclouds.forEach(e=>{//因为全景模式的pointSizeType是fixed所以要还原下 sizeType = e.material.pointSizeType e.material.pointSizeType = Potree.config.material.pointSizeType //材质 colorType = e.material.activeAttributeName e.material.activeAttributeName = 'rgba' e.changePointOpacity(1) }) }; this.viewport.afterRender = ()=>{ Potree.settings.pointDensity = density viewer.scene.pointclouds.forEach(e=>{ e.visible = visiMap.get(e) e.material.pointSizeType = sizeType e.material.activeAttributeName = colorType e.changePointOpacity(opacityBefore.get(e)) }) } } this.renderTarget = new THREE.WebGLRenderTarget(this.width,this.height, { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat , /* type: THREE.FloatType, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, */ } ) this.rtEDL = new THREE.WebGLRenderTarget(this.width, this.height, { //好像没用到? 因为这里不绘制测量线 minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, type: THREE.FloatType, depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType) }); this.mesh = new THREE.Mesh(circleGeo, new THREE.MeshBasicMaterial({ side: THREE.DoubleSide , map: this.renderTarget.texture , transparent:true, depthTest: !1, //depthWrite: !1, })) this.overlayMesh = new THREE.Mesh(circleGeo, new THREE.MeshBasicMaterial({ side: THREE.DoubleSide , map:texLoader.load(Potree.resourcePath+'/textures/crosshair.png') , transparent:true, depthTest: !1, //depthWrite: !1, })) this.targetPoint = new THREE.Object3D; this.targetPoint.add(new THREE.Mesh(sphereGeo, new THREE.MeshBasicMaterial({ color:"#ff0000", transparent:true, opacity:0.5, }))) this.targetPoint.add(new THREE.Mesh(sphereGeo, new THREE.MeshBasicMaterial({ color:"#ff0000", transparent:true, opacity:0.2, depthTest:false //被遮挡层 }))) this.targetPoint.name = 'magnifierPointTarget' viewer.scene.scene.add(this.targetPoint) Potree.Utils.setObjectLayers(this.targetPoint, 'magnifierContent' ) this.add(this.mesh) this.add(this.overlayMesh) this.position.set(-1000,-1000,-100000)//令它看不见 this.mesh.renderOrder = 10; this.overlayMesh.renderOrder = 11; this.aimPos Potree.Utils.setObjectLayers(this, 'magnifier' ) //viewer.inputHandler.addInputListener(this) viewer.addEventListener('camera_changed',(e)=>{ // 平移、滚轮时更新 if(e.viewport == viewer.mainViewport) this.update() //不过intersectPoint没更新 }) this.mesh.layers.set(Potree.config.renderLayers.magnifier); this.overlayMesh.layers.set(Potree.config.renderLayers.magnifier); //this.layers.set(Potree.config.renderLayers.magnifier);//这句在外层写没用 this.dontRender = false viewer.addEventListener('global_drag', (e)=>{//拖拽时不渲染。主要是右键平移时渲染延迟了,会闪烁。 this.dontRender = true }) viewer.addEventListener('global_drop', (e)=>{ this.dontRender = false }) viewer.addEventListener('global_mouseup', (e)=>{//测量时拖拽场景再mouseup this.dontRender = false }) var updateVisi = (e)=>{ if(e.hoverViewport == viewer.mainViewport){ Potree.Utils.updateVisible(this,"atViewport", true) this.update(e.intersect && e.intersect.location) }else{ Potree.Utils.updateVisible(this,"atViewport", false) //小地图不显示 } } viewer.addEventListener('global_mousemove', updateVisi) viewer.addEventListener('global_touchstart', updateVisi) /* viewer.addEventListener("beginSplitView",()=>{ this.updateVisible("splitView", false) }) viewer.addEventListener("finishSplitView",()=>{ this.updateVisible("splitView", true) }) */ this.addEventListener("setEnable",(e)=>{ Potree.Utils.updateVisible(this, "enable", e.value) //界面开关 /* if(Potree.settings.displayMode == 'showPanos') && e.value){ Potree.settings.pointDensity = 'magnifier' }else if() */ }) if(Potree.settings.isOfficial){ Potree.Utils.updateVisible(this, "enable", false) }else{ Potree.Utils.updateVisible(this, "measure", false) viewer.addEventListener("measureMovePoint",()=>{//测量开始 //Potree.Utils.updateVisible(this, "measure", true) }) viewer.addEventListener("endMeasureMove",()=>{ Potree.Utils.updateVisible(this, "measure", false) }) } viewer.scene.view.addEventListener('flyingDone',()=>{ if(!this.visible)return let pickWindowSize = 100 let intersect = viewer.inputHandler.getIntersect(viewer.mainViewport, viewer.mainViewport.camera, true, pickWindowSize ) this.update(intersect && intersect.location) }) } //注意:在鼠标没有移动的时候,无法获取到最新的intersect, 放大镜内的内容可能是错误的。全景模式下更奇怪,原因未知 update(aimPos){//相机靠近 navvis的做法 var dontRender = this.dontRender || !(aimPos instanceof THREE.Vector3) || Potree.settings.displayMode == 'showPanos' && viewer.images360.flying aimPos = aimPos instanceof THREE.Vector3 ? aimPos : this.aimPos if(!aimPos || !this.visible)return var playerCamera = viewer.scene.getActiveCamera() var playerPos = playerCamera.position;//viewer.scene.view.getPivot() var dis = playerPos.distanceTo(aimPos); var dirToCamera = new THREE.Vector3().subVectors(playerPos, aimPos ).normalize() const fareast = 300; //相机位置 var finalDisToAim = dis>magDisMin ? dis > fareast ? magDisMax : (dis-magDisMin) / (fareast-magDisMin) * (magDisMax-magDisMin) + magDisMin : dis / 2; //dis>magDistance_ ? magDistance_ : dis / 2; this.camera.position.copy(aimPos).add(dirToCamera.multiplyScalar(finalDisToAim)) this.camera.lookAt(aimPos) this.camera.fov = playerCamera.fov / 2 this.camera.updateProjectionMatrix() //自身位置 //let pos2d = viewer.inputHandler.pointer.clone(); //跟随鼠标 let pos2d = Potree.Utils.getPos2d(aimPos, playerCamera, viewer.renderArea, viewer.mainViewport).vector //更新目标点的实时二维位置 let margin = 0.4, maxY = 0.4 let screenPos = pos2d.clone().setY(pos2d.y + (pos2d.y>maxY ? -margin : margin )) let newPos = new THREE.Vector3(screenPos.x,screenPos.y,0.8).unproject(playerCamera); //z:-1朝外 let dir = newPos.clone().sub(playerPos).normalize().multiplyScalar(10);//这个数值要大于playerCamera.near let s = finalDisToAim // dis>magDisMin ? 1 : dis / magDisMin ; //let s = dis>magDisMin ? dis > fareast ? magDisMax : (dis-magDisMin) / (fareast-magDisMin) * (magDisMax-magDisMin) + magDisMin : dis / magDisMin this.position.copy(playerPos.clone().add(dir)) this.quaternion.copy(playerCamera.quaternion); this.targetPoint.position.copy(aimPos); this.targetPoint.scale.set(s,s,s) this.aimPos = aimPos var scale = math.getScaleForConstantSize({// width2d : width2dPX, camera:viewer.scene.getActiveCamera(), position: this.getWorldPosition(new THREE.Vector3()), resolution: viewer.mainViewport.resolution2 }) this.scale.set(scale, scale, scale); if(!dontRender){ this.waitRender = true } viewer.dispatchEvent('content_changed') } /* update(aimPos){ //仅改fov的版本 aimPos = aimPos instanceof THREE.Vector3 ? aimPos : this.aimPos if(!aimPos || !this.visible)return //相机位置 var playerCamera = viewer.scene.getActiveCamera() var playerPos = playerCamera.position;//viewer.scene.view.getPivot() var dis = playerPos.distanceTo(aimPos); if(dismaxY ? -margin : margin )) let newPos = new THREE.Vector3(screenPos.x,screenPos.y,0.8).unproject(playerCamera); //z:-1朝外 let dir = newPos.clone().sub(playerPos).normalize().multiplyScalar(10);//这个数值要大于playerCamera.near this.position.copy(playerPos.clone().add(dir)) this.aimPos = aimPos this.targetPoint.position.copy(aimPos); var scale = math.getScaleForConstantSize({// width2d : width2dPX, camera:viewer.scene.getActiveCamera(), position: this.getWorldPosition(new THREE.Vector3()), resolution: viewer.mainViewport.resolution2 }) this.scale.set(scale, scale, scale); if(!this.dontRender){ this.waitRender = true } }//位置需要计算,不仅仅是点云,所以需要深度图 */ render(){ if(!this.visible || !this.waitRender && !viewer.needRender)return //viewer.needRender为true要渲染是因为可能是点云node加载完 viewer.render({ target : this.renderTarget, viewports : [this.viewport], camera : this.camera, magnifier : true, rtEDL: this.rtEDL /* width :this.renderTarget.width, height: this.renderTarget.height, */ }) this.waitRender = false viewer.dispatchEvent('content_changed') } }