xzw hace 2 años
padre
commit
849de5ccb5

+ 628 - 0
src/ExtendPointCloudOctree.js

@@ -0,0 +1,628 @@
+
+import * as THREE from "../libs/three.js/build/three.module.js";
+ 
+import {PointCloudMaterial} from "./materials/PointCloudMaterial.js";
+ 
+import {PointCloudOctree} from './PointCloudOctree.js'
+import {PointSizeType } from "./defines.js";
+import math from './custom/utils/math.js'
+
+export class ExtendPointCloudOctree extends PointCloudOctree{
+    constructor(geometry, material){
+        
+        super(geometry, material)
+        
+        
+        this.boundingBox = this.pcoGeometry.tightBoundingBox//this.pcoGeometry.boundingBox;  //boundingBox是正方体,所以换掉
+		this.boundingSphere = this.boundingBox.getBoundingSphere(new THREE.Sphere());
+        this.nodeMaxLevel = 0;//add
+        this.maxLevel = Infinity;
+        this.temp = { sizeFitToLevel:{}, opacity:{}}//add
+        //add 
+        
+        this.panos = []
+        this.matrixAutoUpdate = false   //最好禁止updateMatrix  直接使用matrixWorld
+        this.orientationUser = 0  
+        this.translateUser = new THREE.Vector3;
+        
+        this.rotateMatrix = new THREE.Matrix4;
+        this.transformMatrix = new THREE.Matrix4;// 数据集的变化矩阵
+        this.transformInvMatrix = new THREE.Matrix4; 
+        this.rotateInvMatrix = new THREE.Matrix4; 
+        
+        
+        this.nodeMaxLevelPredict = this.predictNodeMaxLevel()//预测maxNodeLevel  
+        this.testMaxNodeCount = this.testMaxNodeCount2 = 0
+        
+        this.material.spacing = this.pcoGeometry.spacing;//初始化一下 以便于设置pointsize
+        this._visible = true;
+        this.pcoGeometry.addEventListener('updateNodeMaxLevel', this.updateNodeMaxLevel.bind(this))
+        this.isPointcloud = true    //add
+    }
+    
+    /* 
+    
+    注释:node的level从最大的box 0开始。
+    且加载任意一个node必定也会加载它的所有祖先。(如visibleNodes中有一个level为4,则一定有3,2,1,0) 
+    visibleNodes就是所有可见的node,比如:
+    如果相机在0这个位置朝下,这时候的visibleNodes中只有一个level为0的node;
+    而如果朝上看,上方的几个node如果在视野中占据足够大的位置的话,就会加载。
+    如果相机在2这个位置朝上,这时候的visibleNodes中所包含的level为: 0,1,2
+    
+    ________________
+    |   |   |       |
+    |__2|   |       |
+    |     1 |   1   |
+    |_______|_______|
+    |               |    
+    |               |    
+    |      0        |    
+    |_______________|
+    
+    查看box可在potree中开启
+     */
+     
+    updateNodeMaxLevel(e){//目前点云包含node的最高level  
+        var level = Math.max(e.level, this.nodeMaxLevel)
+        if(level != this.nodeMaxLevel){
+            this.nodeMaxLevel = level 
+            //viewer.dispatchEvent({type:'updateNodeMaxLevel', pointcloud: this, nodeMaxLevel:level}) 
+             
+            //console.log('updateNodeMaxLevel ' + this.dataset_id + " : "+ this.nodeMaxLevel)                
+              
+            this.setPointLevel()//重新计算
+             
+            if(!Potree.settings.sizeFitToLevel){
+                this.changePointSize() 
+            }   
+        }
+    }//注:在没有加载到真实的 nodeMaxLevel之前,点云会显示得偏大
+      
+     
+     
+   //panoEdit时比预测值小很多?
+     
+    testMaxNodeLevel(){//手动使maxLevel达到最高,从而迫使updateNodeMaxLevel。  因为Potree.settings.pointDensity 不为 'high'时,maxLevel不是所加载的最高,就很容易加载不出下一个层级,导致无法知道nodeMaxLevel
+        if(this.testMaxNodeLevelDone ) return
+        //if(this.nodeMaxLevel > this.nodeMaxLevelPredict.min  )return 
+        
+        
+        if( this.nodeMaxLevel==0 )return true
+        if( !viewer.atDatasets.includes(this))return true //否则老远就count++
+        
+        let levels = this.visibleNodes.map(e=>e.getLevel())
+        let actMaxLevel = Math.max.apply(null, levels) //实际加载到的最高的node level
+        if(actMaxLevel <  this.maxLevel)return true// 还没加载到能加载到的最高。  但在细节设置较低时,排除作用微弱。
+        
+         
+        //尝试加载出更高级的level 
+        let old = this.maxLevel
+        this.maxLevel = 12;
+        //var visibleNodes1 = this.visibleNodes.map(e=>e.getLevel())
+        //console.log('visibleNodes1',visibleNodes1)
+        Potree.updatePointClouds([this],  viewer.scene.getActiveCamera(), viewer.mainViewport.resolution );
+        //不在camera可视范围内还是加载不出来。即使临时修改位置
+        
+        
+        var visibleNodes2 = this.visibleNodes.map(e=>e.getLevel())
+        //console.log('visibleNodes2',visibleNodes2) 
+        this.maxLevel = old;
+        
+        
+        
+        this.testMaxNodeCount ++
+        if(this.testMaxNodeCount > 500){
+            console.log('testMaxNodeLevel次数超出,强制结束:',this.dataset_id,  this.nodeMaxLevel,  this.nodeMaxLevelPredict.min) 
+            this.testMaxNodeLevelDone = 'moreThanMaxCount'
+            return; //在可以看见点云的情况下,超时,有可能是预测的max是错的    
+        }
+        if(this.nodeMaxLevel < this.nodeMaxLevelPredict.min) return  true //仍需要继续testMaxNodeLevel
+        
+        this.testMaxNodeCount2 ++; // 已经> this.nodeMaxLevelPredict.min 后,开始计数。因为min可能低于真实nodeMaxLevel所以要再试几次
+        
+        /* if(this.name == 'SS-t-CWmVgzP4XU'){
+            console.log('SS-t-CWmVgzP4XU count++')
+        } */
+        
+        if(this.testMaxNodeCount2 < 50)  return  true //再试几次 ( 主要是细节调得低时需要多测几次才加载到
+        this.testMaxNodeLevelDone = true
+        
+        
+           
+         
+        
+    }       
+      
+     
+     
+     
+     
+    
+    setPointLevel(){
+        
+        var pointDensity = Potree.settings.pointDensity
+        var config = Potree.config.pointDensity[pointDensity];
+        if(!config)return
+        
+        
+        /* if(this.testingMaxLevel){
+            this.maxLevel = 12;//先加载到最大的直到测试完毕。由于5个level为一组来加载,所以如果写4最高能加载到5,如果写5最高能加载到下一个级别的最高也就是10
+            //console.log('maxLevel: '+e.maxLevel +  ' testingMaxLevel中 '  )                                
+        }else{ */
+            let percent = config.percentByUser && Potree.settings.UserDensityPercent != void 0  ? Potree.settings.UserDensityPercent : config.maxLevelPercent  
+            this.maxLevel = Math.round( percent * this.nodeMaxLevel); 
+            //console.log('maxLevel: '+e.maxLevel +  ',   density : '+Potree.settings.pointDensity,  ",  percent :"+percent);
+            
+            if(Potree.settings.sizeFitToLevel){
+                this.changePointSize() 
+            } 
+            this.changePointOpacity()
+        //}   
+    }
+    
+    //预测可能的nodeMaxLevel:
+     
+    predictNodeMaxLevel(){//预测maxNodeLevel。  可能只适用于我们相机拍的点云
+        let spacing = {min:0.005, max:0.014};//最小节的两点间的距离  ,获得方法:spacing / Math.pow(2, nodeMaxLevel)。 目前观测的我们自己拍的这个数值的范围大概是这样
+        let min = Math.log2(this.material.spacing / spacing.max); //有见过最大是0.01368 
+        let max = Math.log2(this.material.spacing / spacing.min); //大部分是 0.006
+        //console.log('predictNodeMaxLevel:', this.name ,  min, max ) 
+    
+    
+        return {min, max}
+    } 
+    
+    getHighestNodeSpacing(){
+        return this.material.spacing / Math.pow(2, this.nodeMaxLevel) //前提是这个nodeMaxLevel是准确的
+    } 
+    
+    updateMaterial (material, visibleNodes, camera, renderer, resolution) {//改
+		material.fov = camera.fov * (Math.PI / 180);
+		/* material.screenWidth = renderer.domElement.clientWidth;
+		material.screenHeight = renderer.domElement.clientHeight; */
+        material.resolution = resolution
+        
+        
+		//material.spacing = this.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z);  //应该不需要
+		material.near = camera.near;
+		material.far = camera.far;
+		material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x;
+	} 
+    
+    pick(viewer, viewport, camera, ray, params = {}){//改
+  
+		let renderer = viewer.renderer;
+		let pRenderer = viewer.pRenderer;
+
+		performance.mark("pick-start");
+
+		let getVal = (a, b) => a != void 0 ? a : b;
+        
+        
+        let pickWindowSize_ = THREE.Math.clamp( Math.round((1.1-this.maxLevel/this.nodeMaxLevel)*80),  5, 100)
+        
+		let pickWindowSize = getVal(params.pickWindowSize, pickWindowSize_    ); /* 65 */ //拾取像素边长,越小越精准,但点云稀疏的话可能容易出现识别不到的情况。 另外左下侧会有缝隙无法识别到,缝隙大小和这个值有关
+ 
+		let pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false);
+
+		let size = viewport ? viewport.resolution : renderer.getSize(new THREE.Vector2());
+
+		let width = Math.ceil(getVal(params.width, size.width)); //renderTarget大小。影响识别精度  
+		let height = Math.ceil(getVal(params.height, size.height));
+        
+        let screenshot = ()=>{
+            if(window.testScreen){            
+                let dataUrl = Potree.Utils.renderTargetToDataUrl(pickState.renderTarget, width, height, renderer)
+             
+                Common.downloadFile(dataUrl, 'screenshot.jpg')  //为什么图片上不是只有pickWindowSize区域有颜色??
+                window.testScreen = 0
+            } 
+        }
+        
+      
+
+		let pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType);
+		let pointSize = getVal(params.pointSize, this.material.size);
+
+		let nodes = this.nodesOnRay(this.visibleNodes, ray);
+
+		if (nodes.length === 0) { 
+            
+			return null;
+		}
+        //console.log('nodes.length != 0', this.name)
+		if (!this.pickState) {
+			let scene = new THREE.Scene();
+
+			let material = new Potree.PointCloudMaterial();
+			material.activeAttributeName = "indices";
+
+			let renderTarget = new THREE.WebGLRenderTarget(
+				1, 1,
+				{ minFilter: THREE.LinearFilter,
+					magFilter: THREE.NearestFilter,
+					format: THREE.RGBAFormat }
+			);
+
+			this.pickState = {
+				renderTarget: renderTarget,
+				material: material,
+				scene: scene
+			};
+		};
+
+		let pickState = this.pickState;
+		let pickMaterial = pickState.material;
+
+		{ // update pick material
+			pickMaterial.pointSizeType = pointSizeType;
+			//pickMaterial.shape = this.material.shape;
+			pickMaterial.shape = Potree.PointShape.PARABOLOID;
+
+			pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value;
+			pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value;
+			pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value;
+			pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value;
+
+			pickMaterial.activeAttributeName = "indices";
+
+			pickMaterial.size = pointSize;
+			pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
+			pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
+			pickMaterial.classification = this.material.classification;
+			pickMaterial.recomputeClassification();
+
+			if(params.pickClipped){
+				pickMaterial.clipBoxes = this.material.clipBoxes;
+				pickMaterial.uniforms.clipBoxes = this.material.uniforms.clipBoxes;
+				if(this.material.clipTask === Potree.ClipTask.HIGHLIGHT){
+					pickMaterial.clipTask = Potree.ClipTask.NONE;
+				}else{
+					pickMaterial.clipTask = this.material.clipTask;
+				}
+				pickMaterial.clipMethod = this.material.clipMethod;
+			}else{
+				pickMaterial.clipBoxes = [];
+			}
+
+			this.updateMaterial(pickMaterial, nodes, camera, renderer, new THREE.Vector2(width, height));
+		}
+
+		pickState.renderTarget.setSize(width, height); //仅绘制屏幕大小的,而不乘以devicePixelRatio
+
+		let pixelPos = new THREE.Vector2(params.x, params.y);
+
+		let gl = renderer.getContext();
+		gl.enable(gl.SCISSOR_TEST);
+		gl.scissor(  //规定渲染范围,只渲染一小块
+			parseInt(pixelPos.x - (pickWindowSize - 1) / 2),
+			parseInt(pixelPos.y - (pickWindowSize - 1) / 2),
+			parseInt(pickWindowSize), parseInt(pickWindowSize));
+
+
+		renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
+		renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
+		renderer.state.setBlending(THREE.NoBlending);
+
+		{ // RENDER
+			renderer.setRenderTarget(pickState.renderTarget);
+			gl.clearColor(0, 0, 0, 0);
+			renderer.clear(true, true, true);
+
+			let tmp = this.material;
+			this.material = pickMaterial;
+            
+			pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
+            screenshot();
+
+
+			this.material = tmp;
+		}
+
+		let clamp = (number, min, max) => Math.min(Math.max(min, number), max);
+
+		let x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width));
+		let y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height));
+		/* let w = parseInt(Math.min(x + pickWindowSize, width) - x);
+		let h = parseInt(Math.min(y + pickWindowSize, height) - y); */
+        
+		let pixelCount = pickWindowSize * pickWindowSize//w * h;
+		let buffer = new Uint8Array(4 * pixelCount);
+        //w<pickWindowSize会报错
+        
+		gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
+
+		renderer.setRenderTarget(null);
+		renderer.state.reset();
+		renderer.setScissorTest(false);
+		gl.disable(gl.SCISSOR_TEST);
+
+		let pixels = buffer;
+		let ibuffer = new Uint32Array(buffer.buffer); //四个数整合成一个
+
+		// find closest hit inside pixelWindow boundaries
+		let min = Number.MAX_VALUE;
+		let hits = [];
+		for (let u = 0; u < pickWindowSize; u++) {
+			for (let v = 0; v < pickWindowSize; v++) {
+				let offset = (u + v * pickWindowSize);
+				let distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2);
+
+				let pcIndex = pixels[4 * offset + 3];//nodes index(第四位)
+				pixels[4 * offset + 3] = 0; //去除nodes index后剩下的是index(前三位)
+				let pIndex = ibuffer[offset]; //index
+
+				if(!(pcIndex === 0 && pIndex === 0) && (pcIndex !== undefined) && (pIndex !== undefined)){
+					let hit = {
+						pIndex: pIndex,
+						pcIndex: pcIndex,
+						distanceToCenter: distance
+					};
+
+					if(params.all){
+						hits.push(hit);
+					}else{
+						if(hits.length > 0){
+							if(distance < hits[0].distanceToCenter){
+								hits[0] = hit;
+							}
+						}else{
+							hits.push(hit);
+						}
+					}
+
+
+				}
+			}
+		}
+
+		
+		// { // DEBUG: show panel with pick image
+		// 	let img = Utils.pixelsArrayToImage(buffer, w, h);
+		// 	let screenshot = img.src;
+		
+		// 	if(!this.debugDIV){
+		// 		this.debugDIV = $(`
+		// 			<div id="pickDebug"
+		// 			style="position: absolute;
+		// 			right: 400px; width: 300px;
+		// 			bottom: 44px; width: 300px;
+		// 			z-index: 1000;
+		// 			"></div>`);
+		// 		$(document.body).append(this.debugDIV);
+		// 	}
+		
+		// 	this.debugDIV.empty();
+		// 	this.debugDIV.append($(`<img src="${screenshot}"
+		// 		style="transform: scaleY(-1); width: 300px"/>`));
+		// 	//$(this.debugWindow.document).append($(`<img src="${screenshot}"/>`));
+		// 	//this.debugWindow.document.write('<img src="'+screenshot+'"/>');
+		// }
+
+
+		for(let hit of hits){
+			let point = {};
+
+			if (!nodes[hit.pcIndex]) {
+				return null;
+			}
+
+			let node = nodes[hit.pcIndex];
+			let pc = node.sceneNode;
+			let geometry = node.geometryNode.geometry;
+
+			for(let attributeName in geometry.attributes){
+				let attribute = geometry.attributes[attributeName];
+
+				if (attributeName === 'position') {
+					let x = attribute.array[3 * hit.pIndex + 0];
+					let y = attribute.array[3 * hit.pIndex + 1];
+					let z = attribute.array[3 * hit.pIndex + 2];
+
+					let position = new THREE.Vector3(x, y, z);
+                    
+					position.applyMatrix4( pc.matrixWorld );
+                    
+					point[attributeName] = position;
+				} else if (attributeName === 'indices') {
+
+				} else {
+
+					let values = attribute.array.slice(attribute.itemSize * hit.pIndex, attribute.itemSize * (hit.pIndex + 1)) ;
+
+					if(attribute.potree){
+						const {scale, offset} = attribute.potree;
+						values = values.map(v => v / scale + offset);
+					}
+
+					point[attributeName] = values;
+
+					//debugger;
+					//if (values.itemSize === 1) {
+					//	point[attribute.name] = values.array[hit.pIndex];
+					//} else {
+					//	let value = [];
+					//	for (let j = 0; j < values.itemSize; j++) {
+					//		value.push(values.array[values.itemSize * hit.pIndex + j]);
+					//	}
+					//	point[attribute.name] = value;
+					//}
+				}
+
+			}
+
+			hit.point = point;
+		}
+
+		performance.mark("pick-end");
+		performance.measure("pick", "pick-start", "pick-end");
+
+		if(params.all){
+			return hits.map(hit => hit.point);
+		}else{
+			if(hits.length === 0){
+				return null;
+			}else{
+				return hits[0].point;
+				//let sorted = hits.sort( (a, b) => a.distanceToCenter - b.distanceToCenter);
+
+				//return sorted[0].point;
+			}
+		}
+
+	} 
+
+
+    // 设置点大小
+    changePointSize(num, sizeFitToLevel) {
+ 
+        if(this.material.pointSizeType != PointSizeType.ATTENUATED){
+            return num && (this.material.size = num)
+        }
+        if (num == void 0) {
+            num = this.temp.pointSize
+        } else {
+            this.temp.pointSize = num
+            
+        }
+        num /= (Potree.config.material.realPointSize / Potree.config.material.pointSize) //兼容 
+         
+        
+        
+        
+        num = Math.pow(num, 1.05) * 6 
+        
+        
+        
+        
+        if(sizeFitToLevel || Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本:    近似将pointSizeType换成ADAPTIVE
+            let str = this.temp.pointSize+':'+this.maxLevel+':'+this.nodeMaxLevel
+            let value = this.temp.sizeFitToLevel[str]  //储存。防止每次渲染(反复切换density)都要算。
+            if(value){
+                this.material.size = value
+            }else{
+                
+                let base = this.material.spacing / Math.pow(2, this.maxLevel) //点云大小在level为0时设置为spacing,每长一级,大小就除以2
+                base *= this.nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(this.maxLevel / this.nodeMaxLevel, 1.3)) : 0.1 //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙
+
+                this.material.size = base * 3 * num/*  * window.devicePixelRatio */
+                //在t-8BCqxQAr93 会议室 和 t-e2Kb2iU 隧道 两个场景里调节,因为它们的spacing相差较大,观察会议室墙壁的龟裂程度
+                this.temp.sizeFitToLevel[str] = this.material.size
+            }
+        }else{ 
+        
+            let base = 0.007;  //let base = this.material.spacing / Math.pow(2, this.nodeMaxLevel) //点云大小在level为0时设置为spacing,每长一级,大小就除以2
+            //base的数值理论上应该是右侧算出来的,但发现有的场景nodeMaxLevel和nodeMaxLevelPredict差别较大的点云显示也过大,而直接换成固定值反而可以适应所有场景。该固定值来源于 getHighestNodeSpacing 最小值,修改了下。(会不会是我们的相机其实该值是固定的,根据该值算出的spacing才是有误差的? 如果换了相机是否要改值?)
+            this.material.size = base * 5 * num /* * window.devicePixelRatio  */
+        } 
+        
+        
+        //console.log('changePointSize  '  + this.dataset_id + '  , num : ' + num + ' , size : ' + this.material.size, this.material.spacing)
+
+         
+    }  
+    
+    
+    
+    // 设置点透明度
+    changePointOpacity(num, canMoreThanOne) {
+        //num:0-1   navvis用的是亮度
+        if (num == void 0) {
+            num = this.temp.pointOpacity
+        } else {
+            this.temp.pointOpacity = num
+        }
+        
+        if(Potree.settings.editType == 'merge'){ //not AdditiveBlending
+            return this.material.opacity = num
+        }
+      
+        if (num == 1) {
+            this.material.opacity = 1
+        } else { 
+
+            let str = (Potree.settings.sizeFitToLevel?'sizeFit:':'')+ (canMoreThanOne ? 'canMoreThanOne:':'') +this.temp.pointOpacity+':'+this.maxLevel+':'+this.nodeMaxLevel
+            let value = this.temp.opacity[str]  //储存。防止每次渲染(反复切换density)都要算。
+            if(value){
+                this.material.opacity = value
+            }else{
+                if(Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本:
+                    let base = this.material.spacing / Math.pow(1.4, this.maxLevel) //随着level提高,点云重叠几率增多
+                    let minBase = this.material.spacing / Math.pow(1.4, this.nodeMaxLevel)
+                    let ratio = Math.min(1 / base, 1 / minBase / 3) //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
+                    this.material.opacity = base * ratio * num
+                    if(!canMoreThanOne){
+                        this.material.opacity = THREE.Math.clamp(this.material.opacity, 0, 0.999) //到1就不透明了(可能出现一段一样)
+                    }
+                }else{
+                    let base = this.material.spacing / Math.pow(1.8, this.maxLevel) 
+                    let minBase = this.material.spacing / Math.pow(1.8, this.nodeMaxLevel)
+                    //console.log(1 / base, 1 / minBase / 6)
+                    let ratio = Math.min(1 / base, 1 / minBase / 6) //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
+                    this.material.opacity = base * ratio * num
+                    if(!canMoreThanOne){
+                        this.material.opacity = THREE.Math.clamp(this.material.opacity, 0, 0.999) //到1就不透明了(可能出现一段一样)
+                    }
+                }
+                this.temp.opacity[str] = this.material.opacity
+            }
+                
+            //缺点:防止颜色过亮主要是相机离远时,当在漫游点处由于离点云太近,可能会导致高质量点云看起来很暗。
+        }
+        //console.log('changePointOpacity ' + this.dataset_id + ', num : ' + num + ' , opacity : ' + this.material.opacity) //检查是否做到了低质量时num==opacity,中质量opacity稍小于num,高质量更小
+         
+    } 
+     
+
+
+    updateBound(){
+        var boundingBox_ = this.pcoGeometry.tightBoundingBox.clone().applyMatrix4(this.matrixWorld)
+        this.bound = boundingBox_
+    } 
+    getPanosBound(){
+        if(this.panos.length > 0){
+            let minSize = new THREE.Vector3(1,1,1)
+            this.panosBound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize)
+        }else{
+            this.panosBound = null
+        } 
+    } 
+    
+    getUnrotBoundPoint(type){//获取没有旋转的tightBounding的水平四个点
+        //如果alighment支持任意轴旋转,水平面上看到的可能就是六边形了,失去意义,到时候不能用这个。也可以若只绕z旋转, 使用tightBoundingBox,否则bound
+        let bound = this.pcoGeometry.tightBoundingBox 
+
+        if(type == 'all'){
+            return [new THREE.Vector3(bound.min.x, bound.min.y, bound.min.z),
+                new THREE.Vector3(bound.max.x, bound.min.y, bound.min.z),
+                new THREE.Vector3(bound.max.x, bound.max.y,bound.min.z),
+                new THREE.Vector3(bound.min.x, bound.max.y,bound.min.z),
+                new THREE.Vector3(bound.min.x, bound.min.y, bound.max.z),
+                new THREE.Vector3(bound.max.x, bound.min.y, bound.max.z),
+                new THREE.Vector3(bound.max.x, bound.max.y,bound.max.z),
+                new THREE.Vector3(bound.min.x, bound.max.y,bound.max.z),
+            ].map(e=>e.applyMatrix4(this.matrixWorld)) 
+
+        }else 
+            return [new THREE.Vector3(bound.min.x, bound.min.y,0),
+                new THREE.Vector3(bound.max.x, bound.min.y,0),
+                new THREE.Vector3(bound.max.x, bound.max.y,0),
+                new THREE.Vector3(bound.min.x, bound.max.y,0),
+            ].map(e=>e.applyMatrix4(this.matrixWorld)) 
+    } 
+     
+    
+    
+    ifContainsPoint(pos){
+        if(!this.bound || !this.bound.containsPoint(pos))return
+        var points = this.getUnrotBoundPoint()
+        return math.isPointInArea(points, null, pos)  
+    } 
+     
+    getVolume(){
+        var points = this.getUnrotBoundPoint()
+        var area = Math.abs(math.getArea(points))
+        return area * (this.bound.max.z - this.bound.min.z)
+    }  
+    
+}

+ 15 - 28
src/PointCloudOctreeGeometry.js

@@ -1,12 +1,13 @@
+
+
 import * as THREE from "../libs/three.js/build/three.module.js";
 import {PointCloudTreeNode} from "./PointCloudTree.js";
 import {XHRFactory} from "./XHRFactory.js";
 import {Utils} from "./utils.js";
 
-export class PointCloudOctreeGeometry extends THREE.EventDispatcher{
+export class PointCloudOctreeGeometry{
 
 	constructor(){
-        super()
 		this.url = null;
 		this.octreeDir = null;
 		this.spacing = 0;
@@ -91,7 +92,7 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 		} else if (version.upTo('1.3')) {
 			url = this.pcoGeometry.octreeDir + '/' + this.name;
 		}
-       
+
 		return url;
 	}
 
@@ -116,8 +117,7 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 		child.parent = this;
 	}
 
-	load(){  
-    
+	load(){
 		if (this.loading === true || this.loaded === true || Potree.numNodesLoading >= Potree.maxNodesLoading) {
 			return;
 		}
@@ -141,7 +141,7 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 		this.pcoGeometry.loader.load(this);
 	}
 
-	loadHierachyThenPoints(pointcloud){
+	loadHierachyThenPoints(){
 		let node = this;
 
 		// load hierarchy
@@ -198,8 +198,6 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 				let parentName = name.substring(0, name.length - 1);
 				let parentNode = nodes[parentName];
 				let level = name.length - 1;
-                pco.dispatchEvent({type:'updateNodeMaxLevel',level});//add
-                
 				let boundingBox = Utils.createChildAABB(parentNode.boundingBox, index);
 
 				let currentNode = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
@@ -213,8 +211,8 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 
 			let duration = performance.now() - tStart;
 			if(duration > 5){
-				/* let msg = `duration: ${duration}ms, numNodes: ${decoded.length}`;
-				console.log(msg); */
+				let msg = `duration: ${duration}ms, numNodes: ${decoded.length}`;
+				console.log(msg);
 			}
 
 			node.loadPoints();
@@ -222,8 +220,7 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 		if ((node.level % node.pcoGeometry.hierarchyStepSize) === 0) {
 			// let hurl = node.pcoGeometry.octreeDir + "/../hierarchy/" + node.name + ".hrc";
 			let hurl = node.pcoGeometry.octreeDir + '/' + node.getHierarchyPath() + '/' + node.name + '.hrc';
-            hurl += '?m='+node.pcoGeometry.timeStamp //add
-            
+
 			let xhr = XHRFactory.createXMLHttpRequest();
 			xhr.open('GET', hurl, true);
 			xhr.responseType = 'arraybuffer';
@@ -243,14 +240,14 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 				xhr.send(null);
 			} catch (e) {
 				console.log('fehler beim laden der punktwolke: ' + e);
-			} 
+			}
 		}
-	} 
+	}
 
-	getNumPoints(){  
+	getNumPoints(){
 		return this.numPoints;
 	}
- 
+
 	dispose(){
 		if (this.geometry && this.parent != null) {
 			this.geometry.dispose();
@@ -266,17 +263,7 @@ export class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
 			this.oneTimeDisposeHandlers = [];
 		}
 	}
-    
-	traverse(t, e){//add from navvis 25.js
-        void 0 === e && (e = !0);
-        for (var n, i = e ? [this] : []; void 0 !== (n = i.pop()); ) {
-            t(n);
-            for (var o = 0, r = n.children; o < r.length; o++) {
-                var a = r[o];
-                null !== a && i.push(a)
-            }
-        }
-    }
+	
 }
 
-PointCloudOctreeGeometryNode.IDCount = 0;
+PointCloudOctreeGeometryNode.IDCount = 0;

+ 11 - 2
src/Potree.js

@@ -1,4 +1,7 @@
     
+  
+  export * from "./custom/start.js";
+  export {settings, config} from './custom/settings.js'  
    
 export * from "./Actions.js";
 export * from "./AnimationPath.js";
@@ -76,7 +79,7 @@ export * from "./utils/TransformationTool.js";
 export * from "./utils/Volume.js";
 export * from "./utils/VolumeTool.js";
 export * from "./utils/Compass.js";
-export * from "./viewer/viewer.js";
+    export * from "./custom/viewer/ExtendViewer.js";
 											 
 export * from "./viewer/Scene.js";
 export * from "./viewer/HierarchicalSlider.js";
@@ -92,7 +95,9 @@ export {FirstPersonControls} from "./navigation/FirstPersonControls.js";
 export {EarthControls} from "./navigation/EarthControls.js";
 export {DeviceOrientationControls} from "./navigation/DeviceOrientationControls.js";
 export {VRControls} from "./navigation/VRControls.js";
-												 
+	
+
+    
 import "./extensions/OrthographicCamera.js";
 import "./extensions/PerspectiveCamera.js";
 import "./extensions/Ray.js";
@@ -105,6 +110,10 @@ import {ExtendPointCloudOctree} from "./ExtendPointCloudOctree.js";
 												//import {ExtendPointCloudOctree} from "./custom/ExtendPointCloudOctree.js";
 import {WorkerPool} from "./WorkerPool.js";
 
+
+
+
+
 export const workerPool = new WorkerPool();
 
 export const version = {

+ 4 - 1
src/PotreeRenderer.js

@@ -868,7 +868,10 @@ export class Renderer {
 			}
 
 			const geometry = node.geometryNode.geometry;
-
+/* if(!geometry){
+    console.error('no geometry', node)
+    continue
+} */
 			if(geometry.attributes["gps-time"]){
 				const bufferAttribute = geometry.attributes["gps-time"];
 				const attGPS = octree.getAttribute("gps-time");

+ 29 - 4
src/custom/note.txt

@@ -1,12 +1,13 @@
+主文件:Potree.js 
 
-主文件:start.js
 
 
-start.js可访问全局变量Potree,引用当前目录的其他文件(都是新增的),并修改Potree。
-
+-------旧的---------------
+主文件:start.js 
+start.js可访问全局变量Potree,引用当前目录的其他文件(都是新增的),并修改Potree。 
 start.js除了通过引用shim文件来修改Potree, 也可在此文件修改,更多是执行加载点云等程序。
 
-
+------------------------
 
 
 
@@ -19,9 +20,33 @@ start.js除了通过引用shim文件来修改Potree, 也可在此文件修改,
 
 
 
+======================
+
+bug:
+
+pick时是showPanos模式会报错,为什么node.geometry没有?!
+
+
+
+
+
+
+
+
+
+
+
+
+
+==================
+
+
+
+Volume.js   VolumeTool.js  我原本在构造函数去掉了一些不用的内容,现在恢复了,如果报错再删掉
 
 
 
+有一些注释我暂时删掉了  过后校验新文件时补一下
 
 
 ===============================

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 382 - 592
src/custom/potree.shim.js


+ 13 - 0
src/custom/three.shim.js

@@ -225,7 +225,20 @@ THREE.EventDispatcher.prototype.traverse = function(callback){
 		}
 }
 
+
+
+
+
+
+
+
+
+
+
+
+
 /* ,
+
 THREE.WebGLState = function(e, t, i) {
     var r = this
       , o = new THREE.Vector4

+ 2 - 19
src/loader/BinaryLoader.js

@@ -5,9 +5,6 @@ import {Version} from "../Version.js";
 import {XHRFactory} from "../XHRFactory.js";
 
 
-
-
-//加载 解析点云
 export class BinaryLoader{
 
 	constructor(version, boundingBox, scale){
@@ -31,8 +28,7 @@ export class BinaryLoader{
 		if (this.version.equalOrHigher('1.4')) {
 			url += '.bin';
 		}
-        url += '?m='+node.pcoGeometry.timeStamp //add
-        
+
 		let xhr = XHRFactory.createXMLHttpRequest();
 		xhr.open('GET', url, true);
 		xhr.responseType = 'arraybuffer';
@@ -56,7 +52,7 @@ export class BinaryLoader{
 		}
 	};
 
-	parse(node, buffer){ //解析点云
+	parse(node, buffer){
 		let pointAttributes = node.pcoGeometry.pointAttributes;
 		let numPoints = buffer.byteLength / node.pcoGeometry.pointAttributes.byteSize;
 
@@ -137,20 +133,7 @@ export class BinaryLoader{
 			node.loading = false;
 			node.estimatedSpacing = data.estimatedSpacing;
 			Potree.numNodesLoading--;
-            
-            
-            
-             
-            
-            
-            
-            
-            
-            
-            
 		};
- 
-
 
 		let message = {
 			buffer: buffer,

+ 0 - 139
src/materials/DepthBasicMaterial.js

@@ -1,139 +0,0 @@
-
-import * as THREE from "../../libs/three.js/build/three.module.js";
-import {Shaders} from "../../build/shaders/shaders.js";
-import {Features} from "../Features.js";
-
- 
-export default class DepthBasicMaterial extends THREE.ShaderMaterial{ 
-    constructor(o={}){
-        let {width, height} = viewer.renderer.getSize(new THREE.Vector2());
-        
-        let uniforms = {
-			resolution:    { type: 'v2',  value: new THREE.Vector2(width, height ) },
-            viewportOffset: { type: 'v2',  value: new THREE.Vector2(0, 0 ) }, //left, top    
-			nearPlane:     { type: 'f', 	value: 0.1 },
-			farPlane:      { type: 'f', 	value: 10000 }, 
-			depthTexture:   { type: 't', 	value: null }, 
-			opacity:        { type: 'f',	value: 1  },
-			map:             { type: 't', 	value: o.map }, 
-            baseColor:     {type:'v3',      value: o.color ?  new THREE.Color(o.color) :  new THREE.Color("#ffffff")},
-            backColor:     {type:'v3',      value: o.backColor ?  new THREE.Color(o.backColor) :  new THREE.Color("#ddd")},
-            clipDistance :     { type: 'f', 	value:o.clipDistance || 4}, //消失距离
-            occlusionDistance :     { type: 'f', 	value: o.occlusionDistance || 1 }, //变为backColor距离
-            maxClipFactor :  { type: 'f', 	value: o.maxClipFactor || 1 },  //0-1
-      
-
-		}  
-        
-        let defines = {};
-        
-        let useDepth = o.useDepth && Features.EXT_DEPTH.isSupported()/*  && Potree.settings.matUseDepth */
-        if(useDepth )defines.useDepth = ''
-        if(o.map)defines.use_map = '' 
-        super({ 
-            uniforms,
-            vertexShader: Shaders['depthBasic.vs'],   
-            fragmentShader: Shaders['depthBasic.fs'],
-            depthWrite: !1,
-            depthTest: !1,
-            transparent: o.transparent == void 0 ?  true : o.transparent,
-            side: o.side || 0 /* THREE.DoubleSide */,
-            defines, 
-        })
-        if(o.opacity != void 0){
-            this.opacity = o.opacity
-        }
-       
-        if(useDepth) this.useDepth_ = true
-        
-        
-      
-        
-        if(this.useDepth){  
-        
-            let setSize = (e)=>{//如果出现横条状的异常,往往是viewportOffset出错 
-                let viewport = e.viewport
-                let viewportOffset = viewport.offset || new THREE.Vector2() 
-                this.uniforms.resolution.value.copy(viewport.resolution2) 
-                this.uniforms.viewportOffset.value.copy(viewportOffset)
-                
-                //console.log('depth '+viewportOffset.toArray())
-            }
-            
-            let viewport = viewer.mainViewport;
-                 
-            setSize( {viewport} )
-            
-            viewer.addEventListener('resize',(e)=>{ 
-                if(!this.useDepth || !e.viewport || e.viewport.camera.isPerspectiveCamera){//地图不需要
-                    setSize(e) 
-                } 
-            })  
-        
-        
-            
-            /* viewer.addEventListener('camera_changed', (e)=>{
-                if(e.viewport.name != 'mapViewport') this.updateDepthParams(e) 
-            }) */ 
-         
-            viewer.addEventListener("render.begin", (e)=>{//before render  如果有大于两个viewport的话,不同viewport用不同的depthTex
-                if(e.viewport.camera.isPerspectiveCamera) this.updateDepthParams(e)
-            })
-            
-            this.updateDepthParams()
-        }
-         
-        
-        //点云变化时要一直触发updateDepthParams??
-        //viewer.once("render.pass.end",this.updateDepthParams.bind(this))
-    }
-    
-    updateDepthParams(e={}){//主要用于点云遮住mesh
-        if(this.useDepth){ 
-            var viewport = e.viewport || viewer.mainViewport;
-            var camera = viewport.camera;
-            /* if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.depthTex){
-                this.uniforms.depthTexture.value = viewer.images360.currentPano.depthTex
-            }else{ */
-                this.uniforms.depthTexture.value = viewer.getPRenderer().getRtEDL(viewport).depthTexture   //其实只赋值一次就行
-            //}
-            this.uniforms.nearPlane.value = camera.near;
-            this.uniforms.farPlane.value = camera.far;
-            
-        }            
-    } 
-    set map(map){
-        this.uniforms.map.value = map; 
-    }
-    
-    get useDepth(){
-        return this.useDepth_
-    } 
-    
-    set useDepth(value){//如果不支持 EXT_DEPTH 的话会失效
-        if(this.useDepth_ != value){
-            if(value && Features.EXT_DEPTH.isSupported()){
-                this.defines.useDepth = ''
-                this.updateDepthParams()
-            }else{
-                delete this.defines.useDepth
-            }
-            this.useDepth_ = value
-            this.needsUpdate = true
-        }
-    } 
-    
-    
-    get opacity(){
-        return this.uniforms.opacity.value
-    }
-    set opacity(o){
-        this.uniforms && (this.uniforms.opacity.value = o)
-    }
-    
-    /* dispose(){ 
-        super.dispose()
-        viewer.depthBasic
-    } */
-    
-}

+ 0 - 396
src/materials/ModelTextureMaterial.js

@@ -1,396 +0,0 @@
-import * as THREE from "../../libs/three.js/build/three.module.js";
-import Common from '../utils/Common.js'
-import {Features} from "../Features.js";
-
-const prefixVertex ="precision highp float;\nprecision highp int;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\n"
-const prefixFragment ="precision highp float;\nprecision highp int;\n\nuniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"
- 
-let shader = {
-	 
-		uniforms: { 
-			
-			opacity: {
-				type: "f",
-				value: 1
-			},
-			progress: {
-				type: "f",
-				value: 0
-			},
-			
-			pano0Map: {
-				type: "t",
-				value: null
-			}, 
-            pano1Map: {
-				type: "t",
-				value: null
-			}, 
-            depthMap0: {
-				type: "t",
-				value: null
-			},
-            depthMap1: {
-				type: "t",
-				value: null
-			}, 
-			pano0Position: {
-				type: "v3",
-				value: new THREE.Vector3
-			},
-			pano0Matrix: {
-				type: "m4",
-				value: new THREE.Matrix4
-			},
-			
-			pano1Position: {
-				type: "v3",
-				value: new THREE.Vector3
-			},
-			pano1Matrix: {
-				type: "m4",
-				value: new THREE.Matrix4
-            },
-			/* pano1Matrix2: {
-				type: "m4",
-				value: new THREE.Matrix4
-            },
-            */
-            
-            inverseProjectionMatrix: {
-                value: new THREE.Matrix4
-            },  
-            /* projectionMatrix:{//需要再写一遍吗
-                value: new THREE.Matrix4
-            }, */
-            viewport: {
-                value: new THREE.Vector4
-            },
-            //如     {x: 0, y: 0, z: 428, w: 969}  xy应该是offset, zw是宽高 
-            cameraHeight0: {
-				type: "f",
-				value: 1
-			},
-            cameraHeight1: {
-				type: "f",
-				value: 1
-			},
-            
-        },
-       
-        vertexShader: prefixVertex + `
-
-            uniform vec3 pano0Position;
-            uniform mat4 pano0Matrix;
-            
-            uniform vec3 pano1Position;
-            uniform mat4 pano1Matrix;
-            //uniform mat4 pano1Matrix2;
-
-           
-            varying vec2 vUv; 
-            varying vec3 vWorldPosition0;
-            varying vec3 vWorldPosition1;
-            varying vec3 vWorldPosition12;
-            
-            vec3 transformAxis( vec3 direction ) //navvis->4dkk
-            {
-                float y = direction.y;
-                direction.y = direction.z;
-                direction.z = -y;
-                return  direction;
-            }
-             
-            
-            void main() {
-            
-                vUv = uv;
-                vec4 worldPosition = modelMatrix * vec4(position, 1.0);
-                
-                
-            
-                vec3 positionLocalToPanoCenter0 = worldPosition.xyz - pano0Position;
-                vWorldPosition0 = (vec4(positionLocalToPanoCenter0, 1.0) * pano0Matrix).xyz;
-                vWorldPosition0.x *= -1.0;
-                vWorldPosition0 = transformAxis(vWorldPosition0);
-                
-                vec3 positionLocalToPanoCenter1 = worldPosition.xyz - pano1Position;
-                vWorldPosition1 = (vec4(positionLocalToPanoCenter1, 1.0) * pano1Matrix).xyz;
-                vWorldPosition1.x *= -1.0;
-                vWorldPosition1 = transformAxis(vWorldPosition1);
-                
-                /* 
-                vec3 positionLocalToPanoCenter12 = worldPosition.xyz - pano1Position;
-                vWorldPosition12 = (vec4(positionLocalToPanoCenter12, 1.0) * pano1Matrix2).xyz;
-                vWorldPosition12.x *= -1.0;
-                vWorldPosition12 = transformAxis(vWorldPosition12);
-                 */
-                
-                
-                
-                gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
-            
-            }
-
-        `,
-        fragmentShader: prefixFragment + `
-             
-            #define PI 3.141592653 
-            
-             
-            uniform float modelAlpha;
-            uniform float opacity;
-            uniform float progress;
-            uniform int blackout;
-            uniform vec3 pano0Position;
-            uniform vec3 pano1Position;
-            uniform float maxDistance;
-            uniform float minDistance;
-            uniform float minOpa;
-            
-            uniform float cameraHeight0;
-            uniform float cameraHeight1;
-
-          
-       
-            /* uniform sampler2D pano0Map;
-            uniform sampler2D pano1Map;    */   
-            uniform samplerCube pano0Map;
-            uniform samplerCube pano1Map;
-          
-            
-            varying vec2 vUv; 
-            varying vec3 vWorldPosition0;
-            varying vec3 vWorldPosition1;
-            //varying vec3 vWorldPosition12;
-          
-            /* vec2 getSamplerCoord( vec3 direction ) 
-            {
-                direction = normalize(direction);
-                float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;
-                float ty=acos(direction.z)/PI;
-
-                return vec2(tx,ty);
-            } */
-
-            vec2 getSamplerCoord2( vec3 direction ) 
-            { 
-                direction = normalize(direction);
-                float tx=atan(direction.x,direction.z)/(PI*2.0)+0.5;
-                float ty=acos(direction.y)/PI;
-
-                return vec2(tx,ty); 
-            }
-            
-            #extension GL_EXT_frag_depth : enable
-            #if defined(GL_EXT_frag_depth) && defined(hasDepthTex)  
-                uniform sampler2D depthMap0;
-                uniform sampler2D depthMap1;
-                uniform mat4 inverseProjectionMatrix;
-                uniform mat4 projectionMatrix;
-                uniform vec4 viewport; 
-            
-                vec2 getDepth(vec3 dir, sampler2D depthMap, float height, vec4 eyePos){
-                    vec2 depthValue = vec2(0.0, 0.0);
-                    vec2 uv2 = getSamplerCoord2(/* vWorldPosition12 */dir.xyz);  //暂时只用基于目标漫游点的方向
-                    uv2.x -= 0.25;    //全景图和Cube的水平采样起始坐标相差90度,这里矫正 0.25 个采样偏移
-                    vec4 depth = texture2D(depthMap, uv2);
-                    //float distance = depth.r + 256. * (depth.g + 256. * depth.b);
-                    //distance *= 255. * .001;           // distance is now in meters
-                    
-                    //更改
-                    float distance = (depth.g + depth.r / 256.) * 255.;  //为什么要乘以255 
-                    
-                    if(distance == 0.0){//漫游点底部识别不到的区域,给一个地板高度 
-                         if(uv2.y > 0.75)distance = height / dir.y; 
-                         else distance = 100000.0;//给个超级远的值
-                    } 
-                    depthValue.x = distance;
-                    
-                   // return  r[1] + r[0] / 256  
-                    distance += .1;          // add a safety margin
-
-                    vec4 eyePos2 = vec4(normalize(eyePos.xyz) * distance, 1.);
-                    vec4 clipPos2 = projectionMatrix * eyePos2;
-                    vec4 ndcPos2 = clipPos2 * 1. / clipPos2.w;
-
-                    
-                    depthValue.y = 0.5 * ((gl_DepthRange.far - gl_DepthRange.near) * ndcPos2.z
-                            + gl_DepthRange.near + gl_DepthRange.far); 
-                    return depthValue;      
-                }
-                //注:未加载好的话,depth为0,导致第一次漫游过去的时候许多mesh会立刻被遮挡,所以要确保加载完
-            #endif
-            
-            void main()
-            {
-                
-                /* vec2 samplerCoord0 = getSamplerCoord(vWorldPosition0.xyz);
-                vec2 samplerCoord1 = getSamplerCoord(vWorldPosition1.xyz);  
-                vec4 colorFromPano0=texture2D(pano0Map,samplerCoord0);
-                vec4 colorFromPano1=texture2D(pano1Map,samplerCoord1); */
-                
-                vec4 colorFromPano0 = vec4(0.0,0.0,0.0,0.0);
-                if(progress < 1.0){//通常是1
-                    colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);
-                }
-                vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);
- 
-                gl_FragColor=mix(colorFromPano0,colorFromPano1,progress);
-              
-              
-                
-              
-                //深度图修改深度
-              
-                #if defined(GL_EXT_frag_depth) && defined(hasDepthTex)  
-                    vec4 ndcPos;
-                    ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1.;
-                    ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /
-                        (gl_DepthRange.far - gl_DepthRange.near);
-                    ndcPos.w = 1.0;
-
-                    vec4 clipPos = ndcPos / gl_FragCoord.w;
-                    vec4 eyePos = inverseProjectionMatrix * clipPos;
-                    vec2 depth0 = vec2(0.0,0.0); 
-                    if(progress < 1.0){
-                        depth0 = getDepth(vWorldPosition0, depthMap0, cameraHeight0, eyePos);
-                    }
-                    vec2 depth1 = getDepth(vWorldPosition1, depthMap1, cameraHeight1, eyePos);
-                    
-                    /* if(progress < 1.0 && depth1.x == 0.0 && depth0.x > 0.0){
-                        gl_FragDepthEXT = depth0.y; 
-                    }else{ */
-                        gl_FragDepthEXT = mix(depth0.y,depth1.y,progress);
-                    //}
-                    
-                    
-
-                #endif
-
-                
-            }
-        `
-    }
-            
-            
-
-export default class ModelTextureMaterial extends THREE.RawShaderMaterial { 
-	constructor( ){ 
-    
-        let defines = {}
-         
-       
-        
-        super({
-            fragmentShader: shader.fragmentShader,
-			vertexShader: shader.vertexShader,
-			uniforms: THREE.UniformsUtils.clone(shader.uniforms),
-            side:THREE.DoubleSide,
-			name: "ModelTextureMaterial",
-            defines
-        })
-    
-        
-            
-        let setSize = (e)=>{ 
-            let viewport = e.viewport
-            let viewportOffset = viewport.offset || new Vector2()  
-            let resolution = viewport.resolution2 
-            this.uniforms.viewport.value.set(viewportOffset.x, viewportOffset.y, resolution.x, resolution.y) 
-        }
-        let viewport = viewer.mainViewport;
-         
-        setSize({viewport})
-
-
-        viewer.addEventListener('resize',(e)=>{
-            setSize(e)     
-        }) 
-        
-        
-        //var supportExtDepth = !!Features.EXT_DEPTH.isSupported()  
-        {
-         
-            //add
-            
-            viewer.addEventListener('camera_changed', (e)=>{
-                //this.uniforms.projectionMatrix.value.copy(e.camera.projectionMatrix) 
-                this.uniforms.inverseProjectionMatrix.value.copy(e.camera.projectionMatrixInverse)
-            })   
-
-        } 
-
-        
-		//-------------------------------------
-	}
-
-	/**
-	 * 
-	 * @param {Panorama} pano0 
-	 * @param {Panorama} pano1 
-	 * @param {boolean} flag 
-     
-     更新全景图的材质uniforms 
-     
-	 */
-     
-      
-     
-	setProjectedPanos(pano0, pano1, progressValue ){
-        
- 		progressValue!=void 0 && (this.uniforms.progress.value = progressValue);
-		//pano0.ensureSkyboxReadyForRender();
-        
-        
-        if(pano0){
-            this.uniforms.pano0Map.value = pano0.getSkyboxTexture();//pano0.texture
-            this.uniforms.pano0Position.value.copy(pano0.position)
-            this.uniforms.pano0Matrix.value.copy(pano0.panoMatrix/* pano0.mesh.matrixWorld */ );
-            this.uniforms.cameraHeight0.value = pano0.floorPosition.distanceTo(pano0.position)
-            
-            //pano1.ensureSkyboxReadyForRender();
-        }
-        
-		
-		this.uniforms.pano1Map.value = pano1.getSkyboxTexture()//pano1.texture;
-		this.uniforms.pano1Position.value.copy(pano1.position)
-		this.uniforms.pano1Matrix.value.copy(pano1.panoMatrix /* pano1.mesh.matrixWorld */ );
-        this.uniforms.cameraHeight1.value = pano1.floorPosition.distanceTo(pano1.position)
-        this.pano0 = pano0
-        this.pano1 = pano1
-        
-        this.updateDepthTex(pano0)  
-        this.updateDepthTex(pano1)
-        
-        
-        //console.log('setProjectedPanos', pano0&&pano0.id, pano1&&pano1.id)
-        this.needsUpdate = true;
- 	}
-    
-    
-    
-    updateDepthTex(pano){
-        if( !Potree.settings.useDepthTex || !pano || !pano.depthTex || pano!=this.pano0 && pano!=this.pano1)return
-        //console.log('updateDepthTex', pano.id,  this.pano0 && this.pano0.id,  this.pano1 && this.pano1.id)
-        this.uniforms.depthMap0.value = this.pano0 && this.pano0.depthTex 
-        this.uniforms.depthMap1.value = this.pano1 && this.pano1.depthTex 
-        this.updateDepthTexEnable()
-    }
-    
-    updateDepthTexEnable(){
-        let hasDepthTex = this.pano0 && this.pano1 && this.pano0.pointcloud.hasDepthTex && this.pano1.pointcloud.hasDepthTex  //暂时不知道一个有图一个没图怎么写所以
-        
-        Common.addOrRemoveDefine(this, 'hasDepthTex', hasDepthTex?'add':'remove' )
-        
-        
-    }
-    
-    /* EnableDepthTex(){//开启DepthTex
-        if(this.defines['hasDepthTex']){
-            return 
-        }
-        this.defines['hasDepthTex'] = ''
-        this.needsUpdate = true;
-    } */
-}

+ 113 - 625
src/navigation/FirstPersonControls.js

@@ -14,536 +14,99 @@
  */
 
 import * as THREE from "../../libs/three.js/build/three.module.js";
-import {Buttons} from "../defines.js";
-import {Utils} from "../utils.js"; 
-import cameraLight from "../utils/cameraLight.js"; 
-import Common from "../utils/Common.js"; 
+import {MOUSE} from "../defines.js";
+import {Utils} from "../utils.js";
 import {EventDispatcher} from "../EventDispatcher.js";
 
+
 export class FirstPersonControls extends EventDispatcher {
-	constructor (viewer, viewport) {
+	constructor (viewer) {
 		super();
-        
+
 		this.viewer = viewer;
 		this.renderer = viewer.renderer;
 
-		this.scene = viewer.scene;
-		 
+		this.scene = null;
+		this.sceneControls = new THREE.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],
-
+		this.lockElevation = false;
 
-            Rotate_LEFT : ['L'.charCodeAt(0)],
-            Rotate_RIGHT :    ['J'.charCodeAt(0)],
-            Rotate_UP : ['K'.charCodeAt(0)],
-            Rotate_DOWN   : ['I'.charCodeAt(0)], 
-
-        };
+		this.keys = {
+			FORWARD: ['W'.charCodeAt(0), 38],
+			BACKWARD: ['S'.charCodeAt(0), 40],
+			LEFT: ['A'.charCodeAt(0), 37],
+			RIGHT: ['D'.charCodeAt(0), 39],
+			UP: ['R'.charCodeAt(0), 33],
+			DOWN: ['F'.charCodeAt(0), 34]
+		};
 
-		this.fadeFactor = 20;
+		this.fadeFactor = 50;
 		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)=>{
-            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 = '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) {///???????
+			if (e.drag.object !== null) {
+				return;
+			}
+
+			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(Potree.settings.rotAroundPoint && this.intersectStart && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[17]){//定点旋转:   以当前intersect的点为target旋转,不改点在屏幕中的位置
-                    let distance = camera.position.distanceTo(this.intersectStart.location)                                               //不按下ctrl的话                 
-                      
-                    //按照orbitControl的方式旋转:
-                    let rotationSpeed = 2.5;  
-                    
-                    this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed;
-                    this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed;
-
-                   
-                    //先更新一下相机:    
-                    this.update() 
-                    view.applyToCamera(camera)
-                    
-                    //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。
-                    let newPointerDir = viewer.inputHandler.getMouseDirection(this.intersectStart.pointer).direction.clone().multiplyScalar(distance)
-                    let pivot = new THREE.Vector3().addVectors(camera.position, newPointerDir)  //新的3d点
-                     
-                    let moveVec = new THREE.Vector3().subVectors(pivot, this.intersectStart.location)
-                     
-                    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
-                                    || handleState == 'rotate' ) 
-                                {  
-                                    pointclouds = PanoEditor.selectedClouds
-                                }  
-                            }else{
-                                PanoEditor.dispatchEvent('needToDisConnect')
-                                console.warn('选中的漫游点连通了整个数据集,不允许移动')
-                            } 
-                        }
-                        
-                        if(!pointclouds && e.buttons === Buttons.LEFT && viewport.rotateSide){
-                            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(pointclouds){
-                        this.dispatchEvent({
-                            type : "transformPointcloud", 
-                            intersect: e.intersect.orthoIntersect,   
-                            intersectStart: e.drag.intersectStart.orthoIntersect,
-                            moveVec,        
-                            pointclouds, 
-                            camera
-                        }) 
-                    }else{ 
-                        
-                        this.translationWorldDelta.add(moveVec.negate()) 
-                        
-                    }
-                    
-                    
-                    
-                }else{  
-                    if(e.drag.intersectStart){//如果拖拽着点云 
-                        
-                        if(e.drag.z == void 0){//拖拽开始
-                            let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置
-                            e.drag.z = pointerStartPos2d.z //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变
-                            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);
-                            /* this.viewer.dispatchEvent({ 
-                                type: 'dragPanBegin', 
-                                projectionMatrixInverse : e.drag.projectionMatrixInverse
-                            }); */
-                            //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.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 = viewport.lastIntersect ? (viewport.lastIntersect.location || viewport.lastIntersect) : viewer.bound.center  //该viewport的最近一次鼠标和点云的交点
-                        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
+
+			let moveSpeed = this.viewer.getMoveSpeed();
+
+			let ndrag = {
+				x: e.drag.lastDrag.x / this.renderer.domElement.clientWidth,
+				y: e.drag.lastDrag.y / this.renderer.domElement.clientHeight
+			};
+
+			if (e.drag.mouse === MOUSE.LEFT) {
+				this.yawDelta += ndrag.x * this.rotationSpeed;
+				this.pitchDelta += ndrag.y * this.rotationSpeed;
+			} else if (e.drag.mouse === MOUSE.RIGHT) {
+				this.translationDelta.x -= ndrag.x * moveSpeed * 100;
+				this.translationDelta.z += ndrag.y * moveSpeed * 100;
 			}
-            
-            
-            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()
-
-                //console.log('scale ',scale)
-                 
-                let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
-                
-                dolly({
-                    pointer,
-                    scale, camera
-                })
-                this.dollyStart.copy(this.dollyEnd);
-                
-            }
-            //最好按ctrl可以变为dollhouse的那种旋转
 		};
 
 		let drop = e => {
-            if(!this.enabled)return    
 			this.dispatchEvent({type: 'end'});
-            
 		};
 
+		let scroll = (e) => {
+			let speed = this.viewer.getMoveSpeed();
 
-        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.1
-                    } 
-                }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 , direction
-                 
-                
-                if(e.delta != void 0){//滚轮缩放 
-                    speed =  this.currentViewport.getMoveSpeed() * 15
-                    
-                    //var direction = this.currentViewport.view.direction.clone();
-                    direction = this.viewer.inputHandler.getMouseDirection().direction  //定点缩放
-                 
-                    
-                    if(e.delta == 0){//mac
-                        return 
-                    }else if (e.delta < 0) {
-                        speed *= -1
-                    } 
-                }else{
-                    const constantDis =  this.currentViewport.getMoveSpeed() * 200 //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整
-                    speed = (e.scale-1)*constantDis //触屏缩放
-                    //pointer = new THREE.Vector2().addVectors().multiplyScalar(0.5);//两个指头的中心点
-                    direction = this.viewer.inputHandler.getMouseDirection(e.pointer).direction  //定点缩放
-                }
-                 
-                this.useAttenuation = true
-                var vec = direction.multiplyScalar(speed )
-                this.translationWorldDelta.copy(vec)
-                
-            }
-        }
+			if (e.delta < 0) {
+				speed = speed * 0.9;
+			} else if (e.delta > 0) {
+				speed = speed / 0.9;
+			}
 
-		let scroll = (e) => {
-            if(!this.enabled || !e.hoverViewport)return 
-            this.setCurrentViewport(e)
-            
-                
-            e.camera = e.hoverViewport.camera                
-            dolly(e) 
+			speed = Math.max(speed, 0.1);
+
+			this.viewer.setMoveSpeed(speed);
 		};
 
-		let dblclick = (e) => { 
-            if(!this.enabled)return
-            
-            if(!Potree.settings.dblToFocusPoint)return;//调试时才可双击
-            
-            if(Potree.settings.displayMode == 'showPointCloud'/* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse);
+		let dblclick = (e) => {
+			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);
-        }
-        let prepareRotate = (e)=>{ 
-            this.pointerDragStart = e.pointer.clone()  
-            this.intersectStart = e.intersect && e.intersect.location && {
-                location : e.intersect.location,
-                pointer :  e.intersect.location.clone().project(e.dragViewport.camera) //intersect点在屏幕中的位置
-            }
-            //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)
-            }else if(e.touches.length>=3){
-                preparePan(e)
-            }
-        })
-        this.viewer.addEventListener('global_touchend', (e)=>{
-            if(!this.enabled)return
-            if(e.touches.length==2){//停止平移,开始scale
-                prepareScale(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 
-        }) */
-
-
-       
+		this.addEventListener('drag', drag);
+		this.addEventListener('drop', drop);
+		this.addEventListener('mousewheel', scroll);
+		this.addEventListener('dblclick', dblclick);
 	}
-    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;
-         
-    }
-
-    setFPCMoveSpeed(viewport){
-        if(viewport.camera.type == 'OrthographicCamera'){
-            let s = 1 / viewport.camera.zoom
-            viewport.setMoveSpeed(s)   
-            
-        }else{
-            if(viewport == viewer.mainViewport && FirstPersonControls.boundPlane){
-                let s = FirstPersonControls.boundPlane.distanceToPoint(viewer.mainViewport.view.position)
-                s = Math.sqrt(s) / 10;
-                s = Math.max(FirstPersonControls.standardSpeed , s)
-                s  *= Potree.config.moveSpeedAdujust;
-                viewer.setMoveSpeed(s)
-            }
-            
-        }
-        
-    }
-
-
-
-
-    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(){
@@ -551,20 +114,17 @@ export class FirstPersonControls extends EventDispatcher {
 		this.pitchDelta = 0;
 		this.translationDelta.set(0, 0, 0);
 	}
-    
-    
 	
 	zoomToLocation(mouse){
-        if(!this.enabled)return
 		let camera = this.scene.getActiveCamera();
 		
-		/* let I = Utils.getMousePointCloudIntersection(
+		let I = Utils.getMousePointCloudIntersection(
 			mouse,
 			camera,
 			this.viewer,
-			this.scene.pointclouds); */
-        var I = this.viewer.inputHandler.intersect
-		if (!I) {
+			this.scene.pointclouds);
+
+		if (I === null) {
 			return;
 		}
 
@@ -573,17 +133,9 @@ export class FirstPersonControls extends EventDispatcher {
 			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 ray = Utils.mouseToRay(mouse, camera, domElement.clientWidth, domElement.clientHeight);
+
 			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);
@@ -626,11 +178,8 @@ export class FirstPersonControls extends EventDispatcher {
 		}
 	}
 
-	update (delta=1) {
-        if(!this.enabled)return
- 
-        //console.log('update')
-		let view = this.currentViewport.view  
+	update (delta) {
+		let view = this.scene.view;
 
 		{ // cancel move animations on user input
 			let changes = [ this.yawDelta,
@@ -653,142 +202,81 @@ export class FirstPersonControls extends EventDispatcher {
 			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
-                }
-                
-            } 
+
+			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.viewer.getMoveSpeed()));
+				} else if (moveBackward) {
+					this.translationWorldDelta.copy(dir.multiplyScalar(-this.viewer.getMoveSpeed()));
+				}
+			}else{
+				if (moveForward && moveBackward) {
+					this.translationDelta.y = 0;
+				} else if (moveForward) {
+					this.translationDelta.y = this.viewer.getMoveSpeed();
+				} else if (moveBackward) {
+					this.translationDelta.y = -this.viewer.getMoveSpeed();
+				}
+			}
+
+			if (moveLeft && moveRight) {
+				this.translationDelta.x = 0;
+			} else if (moveLeft) {
+				this.translationDelta.x = -this.viewer.getMoveSpeed();
+			} else if (moveRight) {
+				this.translationDelta.x = this.viewer.getMoveSpeed();
+			}
+
+			if (moveUp && moveDown) {
+				this.translationWorldDelta.z = 0;
+			} else if (moveUp) {
+				this.translationWorldDelta.z = this.viewer.getMoveSpeed();
+			} else if (moveDown) {
+				this.translationWorldDelta.z = -this.viewer.getMoveSpeed();
+			}
 		}
 
 		{ // apply rotation
 			let yaw = view.yaw;
 			let pitch = view.pitch;
-             
-            
-			yaw += this.yawDelta /* * delta; */
-			pitch += this.pitchDelta/*  * delta; */
+
+			yaw -= this.yawDelta * delta;
+			pitch -= this.pitchDelta * delta;
 
 			view.yaw = yaw;
 			view.pitch = pitch;
-            
-
-            
-            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.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.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()
-            
-            
+			view.radius = 3 * this.viewer.getMoveSpeed();
 		}
- 
 
-        if(this.useAttenuation){ //只有滚轮缩放时开启
+		{ // decelerate over time
 			let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
-          
-			 /*this.yawDelta *= attenuation;
-			this.pitchDelta *= attenuation; 
-			this.translationDelta.multiplyScalar(attenuation);*/
+			this.yawDelta *= attenuation;
+			this.pitchDelta *= attenuation;
+			this.translationDelta.multiplyScalar(attenuation);
 			this.translationWorldDelta.multiplyScalar(attenuation);
-		}else{
-            
-            this.translationWorldDelta.set(0,0,0)
-            
-        }  
+		}
 	}
 };

+ 795 - 0
src/navigation/FirstPersonControlsNew.js

@@ -0,0 +1,795 @@
+/**
+ * @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"; 
+ 
+ 
+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)=>{
+            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 = '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(Potree.settings.rotAroundPoint && this.intersectStart && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[17]){//定点旋转:   以当前intersect的点为target旋转,不改点在屏幕中的位置
+                    let distance = camera.position.distanceTo(this.intersectStart.location)                                               //不按下ctrl的话                 
+                      
+                    //按照orbitControl的方式旋转:
+                    let rotationSpeed = 2.5;  
+                    
+                    this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed;
+                    this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed;
+
+                   
+                    //先更新一下相机:    
+                    this.update() 
+                    view.applyToCamera(camera)
+                    
+                    //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。
+                    let newPointerDir = viewer.inputHandler.getMouseDirection(this.intersectStart.pointer).direction.clone().multiplyScalar(distance)
+                    let pivot = new THREE.Vector3().addVectors(camera.position, newPointerDir)  //新的3d点
+                     
+                    let moveVec = new THREE.Vector3().subVectors(pivot, this.intersectStart.location)
+                     
+                    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
+                                    || handleState == 'rotate' ) 
+                                {  
+                                    pointclouds = PanoEditor.selectedClouds
+                                }  
+                            }else{
+                                PanoEditor.dispatchEvent('needToDisConnect')
+                                console.warn('选中的漫游点连通了整个数据集,不允许移动')
+                            } 
+                        }
+                        
+                        if(!pointclouds && e.buttons === Buttons.LEFT && viewport.rotateSide){
+                            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(pointclouds){
+                        this.dispatchEvent({
+                            type : "transformPointcloud", 
+                            intersect: e.intersect.orthoIntersect,   
+                            intersectStart: e.drag.intersectStart.orthoIntersect,
+                            moveVec,        
+                            pointclouds, 
+                            camera
+                        }) 
+                    }else{ 
+                        
+                        this.translationWorldDelta.add(moveVec.negate()) 
+                        
+                    }
+                    
+                    
+                    
+                }else{  
+                    if(e.drag.intersectStart){//如果拖拽着点云 
+                        
+                        if(e.drag.z == void 0){//拖拽开始
+                            let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置
+                            e.drag.z = pointerStartPos2d.z //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变
+                            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);
+                            /* this.viewer.dispatchEvent({ 
+                                type: 'dragPanBegin', 
+                                projectionMatrixInverse : e.drag.projectionMatrixInverse
+                            }); */
+                            //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.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 = viewport.lastIntersect ? (viewport.lastIntersect.location || viewport.lastIntersect) : viewer.bound.center  //该viewport的最近一次鼠标和点云的交点
+                        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()
+
+                //console.log('scale ',scale)
+                 
+                let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
+                
+                dolly({
+                    pointer,
+                    scale, camera
+                })
+                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.1
+                    } 
+                }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 , direction
+                 
+                
+                if(e.delta != void 0){//滚轮缩放 
+                    speed =  this.currentViewport.getMoveSpeed() * 15
+                    
+                    //var direction = this.currentViewport.view.direction.clone();
+                    direction = this.viewer.inputHandler.getMouseDirection().direction  //定点缩放
+                 
+                    
+                    if(e.delta == 0){//mac
+                        return 
+                    }else if (e.delta < 0) {
+                        speed *= -1
+                    } 
+                }else{
+                    const constantDis =  this.currentViewport.getMoveSpeed() * 200 //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整
+                    speed = (e.scale-1)*constantDis //触屏缩放
+                    //pointer = new THREE.Vector2().addVectors().multiplyScalar(0.5);//两个指头的中心点
+                    direction = this.viewer.inputHandler.getMouseDirection(e.pointer).direction  //定点缩放
+                }
+                 
+                this.useAttenuation = true
+                var vec = direction.multiplyScalar(speed )
+                this.translationWorldDelta.copy(vec)
+                
+            }
+        }
+
+		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);
+        }
+        let prepareRotate = (e)=>{ 
+            this.pointerDragStart = e.pointer.clone()  
+            this.intersectStart = e.intersect && e.intersect.location && {
+                location : e.intersect.location,
+                pointer :  e.intersect.location.clone().project(e.dragViewport.camera) //intersect点在屏幕中的位置
+            }
+            //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)
+            }else if(e.touches.length>=3){
+                preparePan(e)
+            }
+        })
+        this.viewer.addEventListener('global_touchend', (e)=>{
+            if(!this.enabled)return
+            if(e.touches.length==2){//停止平移,开始scale
+                prepareScale(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;
+         
+    }
+
+    setFPCMoveSpeed(viewport){
+        if(viewport.camera.type == 'OrthographicCamera'){
+            let s = 1 / viewport.camera.zoom
+            viewport.setMoveSpeed(s)   
+            
+        }else{
+            if(viewport == viewer.mainViewport && FirstPersonControls.boundPlane){
+                let s = FirstPersonControls.boundPlane.distanceToPoint(viewer.mainViewport.view.position)
+                s = Math.sqrt(s) / 10;
+                s = Math.max(FirstPersonControls.standardSpeed , s)
+                s  *= Potree.config.moveSpeedAdujust;
+                viewer.setMoveSpeed(s)
+            }
+            
+        }
+        
+    }
+
+
+
+
+    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 && 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;
+            
+
+            
+            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)
+            
+        }  
+	}
+};

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 339 - 1037
src/navigation/InputHandler.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1416 - 0
src/navigation/InputHandlerNew.js


+ 40 - 236
src/navigation/OrbitControls.js

@@ -14,12 +14,10 @@
  */
 
 import * as THREE from "../../libs/three.js/build/three.module.js";
-import {Buttons} from "../defines.js";
-import {Utils} from "../utils.js"; 
+import {MOUSE} from "../defines.js";
+import {Utils} from "../utils.js";
 import {EventDispatcher} from "../EventDispatcher.js";
 
-let minRadius = 2
-
  
 export class OrbitControls extends EventDispatcher{
 	
@@ -32,11 +30,9 @@ export class OrbitControls extends EventDispatcher{
 		this.scene = null;
 		this.sceneControls = new THREE.Scene();
 
-		this.rotationSpeed = 3;  //旋转速度
-        
-         
+		this.rotationSpeed = 5;
 
-		this.fadeFactor = 100;
+		this.fadeFactor = 20;
 		this.yawDelta = 0;
 		this.pitchDelta = 0;
 		this.panDelta = new THREE.Vector2(0, 0);
@@ -45,119 +41,49 @@ export class OrbitControls extends EventDispatcher{
 		this.doubleClockZoomEnabled = true;
 
 		this.tweens = [];
-        this.dollyStart = new THREE.Vector2
-        this.dollyEnd = new THREE.Vector2
-        
-        
-        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)], 
-        };
-        
-        
-		let drag = (e) => {
-            if(!this.enabled)return
- 
-            let viewport = e.dragViewport;
-            if(!viewport || viewport.camera.type == "OrthographicCamera" )return
-            //let camera = viewport.camera 
-          
-
 
+		let drag = (e) => {
 			if (e.drag.object !== null) {
 				return;
 			}
-            let mode
-            
-            if(e.isTouch){
-               
-                if(e.touches.length == 1){
-                    mode = 'rotate'  
-                }else{  
-                    mode = 'scale-pan'
-                }  
-            }else{
-                mode = e.buttons === Buttons.LEFT ? 'rotate' : 'pan'
-            }
 
-
-
-
-            
 			if (e.drag.startHandled === undefined) {
-				e.drag.startHandled = true; 
+				e.drag.startHandled = true;
+
 				this.dispatchEvent({type: 'start'});
 			}
 
-			 
-            let ndrag = e.drag.pointerDelta.clone()//.add(new THREE.Vector2(1,1)).multiplyScalar(0.5)
-            ndrag.y *= -1
+			let ndrag = {
+				x: e.drag.lastDrag.x / this.renderer.domElement.clientWidth,
+				y: e.drag.lastDrag.y / this.renderer.domElement.clientHeight
+			};
 
-			if (mode == 'rotate') { 
-                 
+			if (e.drag.mouse === MOUSE.LEFT) {
 				this.yawDelta += ndrag.x * this.rotationSpeed;
 				this.pitchDelta += ndrag.y * this.rotationSpeed;
 
-				
-			} else if(mode == 'pan'){
-               
+				this.stopTweens();
+			} else if (e.drag.mouse === MOUSE.RIGHT) {
 				this.panDelta.x += ndrag.x;
 				this.panDelta.y += ndrag.y;
 
-				 
-			}else if(mode == 'scale-pan'){ //add
-                this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer); 
-                var scale = this.dollyEnd.length() / this.dollyStart.length() 
-                  
-                this.dollyStart.copy(this.dollyEnd); 
-                this.radiusDelta = (1-scale) * this.scene.view.radius 
-			  
-                //------------------------
-                //平移
-                let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
-                 
-                let delta = new THREE.Vector2().subVectors(pointer, this.lastScalePointer)
-                delta.y *= -1
-                this.panDelta.add(delta)
-                
-                this.lastScalePointer = pointer.clone()
-                
-                
-                
-                //console.log('scale ',scale, this.radiusDelta)
-                
-            }
-            
-            this.stopTweens();
-            
-            
+				this.stopTweens();
+			}
 		};
-        
-        
-        
-         
-        
-        
+
 		let drop = e => {
-            if(!this.enabled)return
 			this.dispatchEvent({type: 'end'});
 		};
 
 		let scroll = (e) => {
-            if(!this.enabled)return
 			let resolvedRadius = this.scene.view.radius + this.radiusDelta;
-            if(resolvedRadius < 0.1 && e.delta>0)return; //防止缩放太小,导致很慢
+
 			this.radiusDelta += -e.delta * resolvedRadius * 0.1;
-            
+
 			this.stopTweens();
 		};
 
 		let dblclick = (e) => {
-            if(!this.enabled)return
 			if(this.doubleClockZoomEnabled){
 				this.zoomToLocation(e.mouse);
 			}
@@ -173,7 +99,6 @@ export class OrbitControls extends EventDispatcher{
 		};
 
 		let touchMove = e => {
-            if(!this.enabled)return
 			if (e.touches.length === 2 && previousTouch.touches.length === 2){
 				let prev = previousTouch;
 				let curr = e;
@@ -219,52 +144,16 @@ export class OrbitControls extends EventDispatcher{
 		this.addEventListener('touchstart', touchStart);
 		this.addEventListener('touchend', touchEnd);
 		this.addEventListener('touchmove', touchMove);
-		this.viewer.addEventListener('global_drag', drag);
-		this.viewer.addEventListener('global_drop', drop);
-		this.viewer.addEventListener('global_mousewheel', scroll);
-		this.viewer.addEventListener('global_dblclick', dblclick);
-        this.viewer.addEventListener('global_touchmove', (e)=>{ 
-            if(e.touches.length>1){//单指的就触发上一句 
-                //console.log('global_touchmove' )
-                drag(e)
-            }
-        });
-        let prepareScale = (e)=>{//触屏的scale
-            this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
-            this.lastScalePointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
-              
-        }
-         
-        this.viewer.addEventListener('global_touchstart', (e)=>{
-            if(this.enabled && e.touches.length==2){//只监听开头两个指头
-                prepareScale(e)
-            }
-        })
-        /* this.viewer.addEventListener('global_touchend', (e)=>{
-            if(!this.enabled)return
-            if(e.touches.length==1){//停止scale,开始rotate
-                prepareRotate(e)
-                //this.pointerDragStart = null
-                //console.log('只剩一个', e.pointer.toArray())
-            }
-        }) */
-        
-        
-        this.viewer.addEventListener('focusOnObject',(o)=>{
-            if(o.position && o.CamTarget){
-                let distance = o.position.distanceTo(o.CamTarget)
-                if(distance < minRadius) minRadius = distance * 0.5 //融合页面当focus一个很小的物体时,需要将minRadius也调小
-            }
-        })
-        
+		this.addEventListener('drag', drag);
+		this.addEventListener('drop', drop);
+		this.addEventListener('mousewheel', scroll);
+		this.addEventListener('dblclick', dblclick);
 	}
 
 	setScene (scene) {
 		this.scene = scene;
 	}
-    setEnable(enabled){
-        this.enabled = enabled
-    }
+
 	stop(){
 		this.yawDelta = 0;
 		this.pitchDelta = 0;
@@ -272,27 +161,26 @@ export class OrbitControls extends EventDispatcher{
 		this.panDelta.set(0, 0);
 	}
 	
-	/* zoomToLocation(mouse){
-        if(!this.enabled)return
+	zoomToLocation(mouse){
 		let camera = this.scene.getActiveCamera();
 		
 		let I = Utils.getMousePointCloudIntersection(
-            null, mouse, this.viewer.inputHandler.pointer,   
+			mouse,
 			camera,
 			this.viewer,
 			this.scene.pointclouds,
 			{pickClipped: true});
-         
+
 		if (I === null) {
 			return;
-		} 
+		}
 
 		let targetRadius = 0;
 		{
 			let minimumJumpDistance = 0.2;
 
 			let domElement = this.renderer.domElement;
-			let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer , camera, domElement.clientWidth, domElement.clientHeight);
+			let ray = Utils.mouseToRay(mouse, camera, domElement.clientWidth, domElement.clientHeight);
 
 			let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
 			let lastNode = nodes[nodes.length - 1];
@@ -335,37 +223,7 @@ export class OrbitControls extends EventDispatcher{
 
 			tween.start();
 		}
-	} */
-
-
-
-
-    
-    zoomToLocation(mouse){
-        let I = viewer.inputHandler.intersect;
-        
-        if(!I)return
-        
-        let object = I.object || I.pointcloud;
-        I = I.location
-        
-        
-        if(!I || !object)return;
-        
-        let dis = this.scene.view.position.distanceTo(I); 
-        
-        
-        let bound = object.boundingBox.clone().applyMatrix4(object.matrixWorld)
-        let size = bound.getSize(new THREE.Vector3); 
-        let len = size.length()
-         
-        let distance = THREE.Math.clamp(dis, 0.1, Math.max(len * 0.1, 3) );
-        
-        minRadius = distance
-        viewer.focusOnObject({ position:I }, 'point', null, {distance})
-        
-    }
-
+	}
 
 	stopTweens () {
 		this.tweens.forEach(e => e.stop());
@@ -373,49 +231,8 @@ export class OrbitControls extends EventDispatcher{
 	}
 
 	update (delta) {
-        if(!this.enabled)return
 		let view = this.scene.view;
 
-
-
-
-        { // 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 px = 0 , py = 0, pz = 0
-            if(moveForward){
-                py = 1
-            }else if(moveBackward){
-                py = -1
-            }
-            
-            if(moveLeft){
-                px = -1
-            }else if(moveRight){
-                px = 1
-            }
-            if(moveUp){
-                pz = 1
-            }else if(moveDown){
-                pz = -1
-            }
-            
-            (px!=0 || py!=0 || pz!=0) && view.translate(px, py, pz, true); 
-             
-        }
-                
- 
-
-
 		{ // apply rotation
 			let progression = Math.min(1, this.fadeFactor * delta);
 
@@ -436,12 +253,9 @@ export class OrbitControls extends EventDispatcher{
 		}
 
 		{ // apply pan
-			/* let progression = Math.min(1, this.fadeFactor * delta);
-			let panDistance = progression * view.radius * 3; */
-            let camera = this.scene.getActiveCamera()
-            let panDistance = 2 * view.radius * Math.tan(THREE.Math.degToRad(camera.fov / 2));//参照4dkk。 平移target(也就是平移镜头位置),但还是难以保证跟手(navvis也不一定跟手,但是很奇怪在居中时中心点居然是跟手的,可能计算方式不同)
-            //计算了下确实是这么算的。 平移pivot。 
-            
+			let progression = Math.min(1, this.fadeFactor * delta);
+			let panDistance = progression * view.radius * 3;
+
 			let px = -this.panDelta.x * panDistance;
 			let py = this.panDelta.y * panDistance;
 
@@ -449,22 +263,15 @@ export class OrbitControls extends EventDispatcher{
 		}
 
 		{ // apply zoom
-			let progression = 1//Math.min(1, this.fadeFactor * delta);
-             
-            
-            
+			let progression = Math.min(1, this.fadeFactor * delta);
+
 			// let radius = view.radius + progression * this.radiusDelta * view.radius * 0.1;
 			let radius = view.radius + progression * this.radiusDelta;
-                      
+
 			let V = view.direction.multiplyScalar(-radius);
 			let position = new THREE.Vector3().addVectors(view.getPivot(), V);
-			
-            if(this.constantlyForward) {// 到达中心点后还能继续向前移动,也就是能推进中心点
-                if(radius < minRadius){  
-                    radius = minRadius
-                } 
-            }
-            view.radius = radius;            
+			view.radius = radius;
+
 			view.position.copy(position);
 		}
 
@@ -474,17 +281,14 @@ export class OrbitControls extends EventDispatcher{
 		}
 
 		{ // decelerate over time
-			/* let progression = Math.min(1, this.fadeFactor * delta);
+			let progression = Math.min(1, this.fadeFactor * delta);
 			let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
 
 			this.yawDelta *= attenuation;
 			this.pitchDelta *= attenuation;
 			this.panDelta.multiplyScalar(attenuation);
 			// this.radiusDelta *= attenuation;
-			this.radiusDelta -= progression * this.radiusDelta; */
-            
-            //取消衰减,直接stop
-            this.stop()  
+			this.radiusDelta -= progression * this.radiusDelta;
 		}
 	}
 };

+ 489 - 0
src/navigation/OrbitControlsNew.js

@@ -0,0 +1,489 @@
+/**
+ * @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"; 
+ 
+
+let minRadius = 2
+
+ 
+export class OrbitControls extends THREE.EventDispatcher{
+	
+	constructor(viewer){
+		super();
+		
+		this.viewer = viewer;
+		this.renderer = viewer.renderer;
+
+		this.scene = null;
+		this.sceneControls = new THREE.Scene();
+
+		this.rotationSpeed = 3;  //旋转速度
+        
+         
+
+		this.fadeFactor = 100;
+		this.yawDelta = 0;
+		this.pitchDelta = 0;
+		this.panDelta = new THREE.Vector2(0, 0);
+		this.radiusDelta = 0;
+
+		this.doubleClockZoomEnabled = true;
+
+		this.tweens = [];
+        this.dollyStart = new THREE.Vector2
+        this.dollyEnd = new THREE.Vector2
+        
+        
+        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)], 
+        };
+        
+        
+		let drag = (e) => {
+            if(!this.enabled)return
+ 
+            let viewport = e.dragViewport;
+            if(!viewport || viewport.camera.type == "OrthographicCamera" )return
+            //let camera = viewport.camera 
+          
+
+
+			if (e.drag.object !== null) {
+				return;
+			}
+            let mode
+            
+            if(e.isTouch){
+               
+                if(e.touches.length == 1){
+                    mode = 'rotate'  
+                }else{  
+                    mode = 'scale-pan'
+                }  
+            }else{
+                mode = e.buttons === Potree.defines.Buttons.LEFT ? 'rotate' : 'pan'
+            }
+
+
+
+
+            
+			if (e.drag.startHandled === undefined) {
+				e.drag.startHandled = true; 
+				this.dispatchEvent({type: 'start'});
+			}
+
+			 
+            let ndrag = e.drag.pointerDelta.clone()//.add(new THREE.Vector2(1,1)).multiplyScalar(0.5)
+            ndrag.y *= -1
+
+			if (mode == 'rotate') { 
+                 
+				this.yawDelta += ndrag.x * this.rotationSpeed;
+				this.pitchDelta += ndrag.y * this.rotationSpeed;
+
+				
+			} else if(mode == 'pan'){
+               
+				this.panDelta.x += ndrag.x;
+				this.panDelta.y += ndrag.y;
+
+				 
+			}else if(mode == 'scale-pan'){ //add
+                this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer); 
+                var scale = this.dollyEnd.length() / this.dollyStart.length() 
+                  
+                this.dollyStart.copy(this.dollyEnd); 
+                this.radiusDelta = (1-scale) * this.scene.view.radius 
+			  
+                //------------------------
+                //平移
+                let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
+                 
+                let delta = new THREE.Vector2().subVectors(pointer, this.lastScalePointer)
+                delta.y *= -1
+                this.panDelta.add(delta)
+                
+                this.lastScalePointer = pointer.clone()
+                
+                
+                
+                //console.log('scale ',scale, this.radiusDelta)
+                
+            }
+            
+            this.stopTweens();
+            
+            
+		};
+        
+        
+        
+         
+        
+        
+		let drop = e => {
+            if(!this.enabled)return
+			this.dispatchEvent({type: 'end'});
+		};
+
+		let scroll = (e) => {
+            if(!this.enabled)return
+			let resolvedRadius = this.scene.view.radius + this.radiusDelta;
+            if(resolvedRadius < 0.1 && e.delta>0)return; //防止缩放太小,导致很慢
+			this.radiusDelta += -e.delta * resolvedRadius * 0.1;
+            
+			this.stopTweens();
+		};
+
+		let dblclick = (e) => {
+            if(!this.enabled)return
+			if(this.doubleClockZoomEnabled){
+				this.zoomToLocation(e.mouse);
+			}
+		};
+
+		let previousTouch = null;
+		let touchStart = e => {
+			previousTouch = e;
+		};
+
+		let touchEnd = e => {
+			previousTouch = e;
+		};
+
+		let touchMove = e => {
+            if(!this.enabled)return
+			if (e.touches.length === 2 && previousTouch.touches.length === 2){
+				let prev = previousTouch;
+				let curr = e;
+
+				let prevDX = prev.touches[0].pageX - prev.touches[1].pageX;
+				let prevDY = prev.touches[0].pageY - prev.touches[1].pageY;
+				let prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY);
+
+				let currDX = curr.touches[0].pageX - curr.touches[1].pageX;
+				let currDY = curr.touches[0].pageY - curr.touches[1].pageY;
+				let currDist = Math.sqrt(currDX * currDX + currDY * currDY);
+
+				let delta = currDist / prevDist;
+				let resolvedRadius = this.scene.view.radius + this.radiusDelta;
+				let newRadius = resolvedRadius / delta;
+				this.radiusDelta = newRadius - resolvedRadius;
+
+				this.stopTweens();
+			}else if(e.touches.length === 3 && previousTouch.touches.length === 3){
+				let prev = previousTouch;
+				let curr = e;
+
+				let prevMeanX = (prev.touches[0].pageX + prev.touches[1].pageX + prev.touches[2].pageX) / 3;
+				let prevMeanY = (prev.touches[0].pageY + prev.touches[1].pageY + prev.touches[2].pageY) / 3;
+
+				let currMeanX = (curr.touches[0].pageX + curr.touches[1].pageX + curr.touches[2].pageX) / 3;
+				let currMeanY = (curr.touches[0].pageY + curr.touches[1].pageY + curr.touches[2].pageY) / 3;
+
+				let delta = {
+					x: (currMeanX - prevMeanX) / this.renderer.domElement.clientWidth,
+					y: (currMeanY - prevMeanY) / this.renderer.domElement.clientHeight
+				};
+
+				this.panDelta.x += delta.x;
+				this.panDelta.y += delta.y;
+
+				this.stopTweens();
+			}
+
+			previousTouch = e;
+		};
+
+		this.addEventListener('touchstart', touchStart);
+		this.addEventListener('touchend', touchEnd);
+		this.addEventListener('touchmove', touchMove);
+		this.viewer.addEventListener('global_drag', drag);
+		this.viewer.addEventListener('global_drop', drop);
+		this.viewer.addEventListener('global_mousewheel', scroll);
+		this.viewer.addEventListener('global_dblclick', dblclick);
+        this.viewer.addEventListener('global_touchmove', (e)=>{ 
+            if(e.touches.length>1){//单指的就触发上一句 
+                //console.log('global_touchmove' )
+                drag(e)
+            }
+        });
+        let prepareScale = (e)=>{//触屏的scale
+            this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
+            this.lastScalePointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
+              
+        }
+         
+        this.viewer.addEventListener('global_touchstart', (e)=>{
+            if(this.enabled && e.touches.length==2){//只监听开头两个指头
+                prepareScale(e)
+            }
+        })
+        /* this.viewer.addEventListener('global_touchend', (e)=>{
+            if(!this.enabled)return
+            if(e.touches.length==1){//停止scale,开始rotate
+                prepareRotate(e)
+                //this.pointerDragStart = null
+                //console.log('只剩一个', e.pointer.toArray())
+            }
+        }) */
+        
+        
+        this.viewer.addEventListener('focusOnObject',(o)=>{
+            if(o.position && o.CamTarget){
+                let distance = o.position.distanceTo(o.CamTarget)
+                if(distance < minRadius) minRadius = distance * 0.5 //融合页面当focus一个很小的物体时,需要将minRadius也调小
+            }
+        })
+        
+	}
+
+	setScene (scene) {
+		this.scene = scene;
+	}
+    setEnable(enabled){
+        this.enabled = enabled
+    }
+	stop(){
+		this.yawDelta = 0;
+		this.pitchDelta = 0;
+		this.radiusDelta = 0;
+		this.panDelta.set(0, 0);
+	}
+	
+	/* zoomToLocation(mouse){
+        if(!this.enabled)return
+		let camera = this.scene.getActiveCamera();
+		
+		let I = Utils.getMousePointCloudIntersection(
+            null, mouse, this.viewer.inputHandler.pointer,   
+			camera,
+			this.viewer,
+			this.scene.pointclouds,
+			{pickClipped: true});
+         
+		if (I === null) {
+			return;
+		} 
+
+		let targetRadius = 0;
+		{
+			let minimumJumpDistance = 0.2;
+
+			let domElement = this.renderer.domElement;
+			let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer , camera, domElement.clientWidth, domElement.clientHeight);
+
+			let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, 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);
+			});
+
+			tween.onComplete(() => {
+				this.tweens = this.tweens.filter(e => e !== tween);
+			});
+
+			tween.start();
+		}
+	} */
+
+
+
+
+    
+    zoomToLocation(mouse){
+        let I = viewer.inputHandler.intersect;
+        
+        if(!I)return
+        
+        let object = I.object || I.pointcloud;
+        I = I.location
+        
+        
+        if(!I || !object)return;
+        
+        let dis = this.scene.view.position.distanceTo(I); 
+        
+        
+        let bound = object.boundingBox.clone().applyMatrix4(object.matrixWorld)
+        let size = bound.getSize(new THREE.Vector3); 
+        let len = size.length()
+         
+        let distance = THREE.Math.clamp(dis, 0.1, Math.max(len * 0.1, 3) );
+        
+        minRadius = distance
+        viewer.focusOnObject({ position:I }, 'point', null, {distance})
+        
+    }
+
+
+	stopTweens () {
+		this.tweens.forEach(e => e.stop());
+		this.tweens = [];
+	}
+
+	update (delta) {
+        if(!this.enabled)return
+		let view = this.scene.view;
+
+
+
+
+        { // 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 px = 0 , py = 0, pz = 0
+            if(moveForward){
+                py = 1
+            }else if(moveBackward){
+                py = -1
+            }
+            
+            if(moveLeft){
+                px = -1
+            }else if(moveRight){
+                px = 1
+            }
+            if(moveUp){
+                pz = 1
+            }else if(moveDown){
+                pz = -1
+            }
+            
+            (px!=0 || py!=0 || pz!=0) && view.translate(px, py, pz, true); 
+             
+        }
+                
+ 
+
+
+		{ // apply rotation
+			let progression = Math.min(1, this.fadeFactor * delta);
+
+			let yaw = view.yaw;
+			let pitch = view.pitch;
+			let pivot = view.getPivot();
+
+			yaw -= progression * this.yawDelta;
+			pitch -= progression * this.pitchDelta;
+
+			view.yaw = yaw;
+			view.pitch = pitch;
+
+			let V = this.scene.view.direction.multiplyScalar(-view.radius);
+			let position = new THREE.Vector3().addVectors(pivot, V);
+
+			view.position.copy(position);
+		}
+
+		{ // apply pan
+			/* let progression = Math.min(1, this.fadeFactor * delta);
+			let panDistance = progression * view.radius * 3; */
+            let camera = this.scene.getActiveCamera()
+            let panDistance = 2 * view.radius * Math.tan(THREE.Math.degToRad(camera.fov / 2));//参照4dkk。 平移target(也就是平移镜头位置),但还是难以保证跟手(navvis也不一定跟手,但是很奇怪在居中时中心点居然是跟手的,可能计算方式不同)
+            //计算了下确实是这么算的。 平移pivot。 
+            
+			let px = -this.panDelta.x * panDistance;
+			let py = this.panDelta.y * panDistance;
+
+			view.pan(px, py);
+		}
+
+		{ // apply zoom
+			let progression = 1//Math.min(1, this.fadeFactor * delta);
+             
+            
+            
+			// let radius = view.radius + progression * this.radiusDelta * view.radius * 0.1;
+			let radius = view.radius + progression * this.radiusDelta;
+                      
+			let V = view.direction.multiplyScalar(-radius);
+			let position = new THREE.Vector3().addVectors(view.getPivot(), V);
+			
+            if(this.constantlyForward) {// 到达中心点后还能继续向前移动,也就是能推进中心点
+                if(radius < minRadius){  
+                    radius = minRadius
+                } 
+            }
+            view.radius = radius;            
+			view.position.copy(position);
+		}
+
+		{
+			let speed = view.radius;
+			this.viewer.setMoveSpeed(speed);
+		}
+
+		{ // decelerate over time
+			/* let progression = Math.min(1, this.fadeFactor * delta);
+			let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
+
+			this.yawDelta *= attenuation;
+			this.pitchDelta *= attenuation;
+			this.panDelta.multiplyScalar(attenuation);
+			// this.radiusDelta *= attenuation;
+			this.radiusDelta -= progression * this.radiusDelta; */
+            
+            //取消衰减,直接stop
+            this.stop()  
+		}
+	}
+};

+ 0 - 112
src/utils/file.js

@@ -1,112 +0,0 @@
-//import { i18n } from "@/lang/index"
-// 媒体名称
-/* export const mediaTypes = {
-    image: i18n.t("common.photo"),
-    video: i18n.t("common.video"),
-    audio: i18n.t("common.voice"),
-} */
-
-// 媒体扩展类型
-export const mediaMimes = {
-    image: ["jpg", "png", "jpeg", "bmp", "gif"],
-    audio: ["mp3", "aac", "ogg", "wav" /* , "m4a" */],
-    video: ["mp4", "mov", "quicktime", "webm" /* "rmvb", "wmv" */], //ios:mov
-}
-
-// 媒体大小显示(MB)
-export const mediaMaxSize = {
-    image: 10,
-    video: 20,
-    audio: 5,
-}
-
-/**
- * 获取媒体扩展类型
- * @param {Stirng} filename 文件名称
- */
-export const getMime = filename => {
-    if (!filename || filename.indexOf(".") === -1) {
-        return ""
-    }
-
-    return filename
-        .split(".")
-        .pop()
-        .toLowerCase()
-}
-
-/**
- * 在路径中获取文件名
- * @param {*} path
- */
-export const getFilename = path => {
-    const segment = (path || "").split("/")
-    return segment[segment.length - 1]
-}
-
-/**
- * 检测媒体文件是否超过预设限制
- * @param {String} type 媒体类型
- * @param {Number} size 文件大小
- */
-export const checkSizeLimit = (type, size) => {
-    size = size / 1024 / 1024
-
-    return size <= mediaMaxSize[type]
-}
-
-export const checkSizeLimitFree = (size, limit) => {
-    size = size / 1024 / 1024
-
-    return size <= limit
-}
-
-/**
- * 检测媒体类型
- * @param {String} type 媒体类型
- * @param {String} filename 文件名称
- */
-export const checkMediaMime = (type, filename) => {
-    const mime = getMime(filename)
-    const find = mediaMimes[type]
-    if (!find) {
-        return false
-    }
-
-    return find.indexOf(mime) !== -1
-}
-
-export const checkMediaMimeByAccept = (accept, filename) => {
-    let mime = getMime(filename)
-    let type = accept
-    if (type && type.indexOf("jpg") == -1 && type.indexOf("jpeg") != -1) {
-        type += ",image/jpg"
-    }
-    return (type || "").indexOf(mime) != -1
-}
-
-export const base64ToBlob = base64 => {
-    let arr = base64.split(","),
-        mime = arr[0].match(/:(.*?);/)[1],
-        bstr = atob(arr[1]),
-        n = bstr.length,
-        u8arr = new Uint8Array(n)
-    while (n--) {
-        u8arr[n] = bstr.charCodeAt(n)
-    }
-    return new Blob([u8arr], { type: mime })
-}
-
-export const base64ToDataURL = base64 => {
-    return window.URL.createObjectURL(base64ToBlob(base64))
-}
-
-export const blobToBase64 = function(blob) {
-    return new Promise(resolve => {
-        var reader = new FileReader()
-        reader.onload = function() {
-            resolve(reader.result)
-        }
-        reader.readAsDataURL(blob)
-    })
-}

+ 0 - 489
src/utils/request.js

@@ -1,489 +0,0 @@
-/*
- * @Author: Rindy
- * @Date: 2019-08-06 16:25:08
- * @LastEditors: Rindy
- * @LastEditTime: 2021-08-27 12:33:49
- * @Description: Request
- */
-
-/** http请求模块
- * @category utils
- * @module utils/request
- */
-
- 
- 
-import browser from "./browser.js"
-import { base64ToBlob } from "./file.js"
-//import { $alert, $confirm, $loginTips } from "@/components/shared/message"
-//import { $waiting } from "@/components/shared/loading"
-//import { checkLogin } from "@/api"
-//import { LoginDetector } from "@/core/starter"
-//import { i18n } from "@/lang"
-//import { password } from "@/utils/string"
-//import store from "../Store"
-
-// 空函数
-const noop = function() {}
-// 请求回调队列
-let postQueue = []
-
-/**
- * @property {number} NEXT              - 继续执行
- * @property {number} SUCCESS           - 成功
- * @property {number} EXCEPTION         - 异常错误
- * @property {number} FAILURE_CODE_3001 - 缺少必要参数
- * @property {number} FAILURE_CODE_3002 - 访问异常
- * @property {number} FAILURE_CODE_3003 - 非法访问
- * @property {number} FAILURE_CODE_3004 - 用户未登录
- * @property {number} FAILURE_CODE_3005 - 验证码已过期
- * @property {number} FAILURE_CODE_3006 - 验证码错误
- * @property {number} FAILURE_CODE_3007 - 昵称已存在
- * @property {number} FAILURE_CODE_3008 - 该手机已被注册
- * @property {number} FAILURE_CODE_3009 - 两次输入的密码不一致
- * @property {number} FAILURE_CODE_3010 - 昵称长度错误
- * @property {number} FAILURE_CODE_3011 - 密码长度错误
- * @property {number} FAILURE_CODE_3012 - 昵称包含敏感词
- * @property {number} FAILURE_CODE_3013 - 手机号码格式错误
- * @property {number} FAILURE_CODE_3014 - 账号或密码不正确
- * @property {number} FAILURE_CODE_3015 - 用户不存在
- * @property {number} FAILURE_CODE_3016 - 您没有权限,请联系管理员
- * @property {number} FAILURE_CODE_3017 - 空文件
- * @property {number} FAILURE_CODE_3018 - 需要上传或使用的文件不存在
- * @property {number} FAILURE_CODE_5010 - 找不到该场景对应的相机
- * @property {number} FAILURE_CODE_5012 - 数据不正常
- * @property {number} FAILURE_CODE_5014 - 无权操作该场景
- * @property {number} FAILURE_CODE_5005 - 场景不存在
- */
-export const statusCode = {
-    NEXT: -999,
-    SUCCESS: 0,
-    EXCEPTION: -1,
-    FAILURE_CODE_3001: 3001,
-    FAILURE_CODE_3002: 3002,
-    FAILURE_CODE_3003: 3003,
-    FAILURE_CODE_3004: 3004,
-    FAILURE_CODE_3005: 3005,
-    FAILURE_CODE_3006: 3006,
-    FAILURE_CODE_3007: 3007,
-    FAILURE_CODE_3008: 3008,
-    FAILURE_CODE_3009: 3009,
-    FAILURE_CODE_3010: 3010,
-    FAILURE_CODE_3011: 3011,
-    FAILURE_CODE_3012: 3012,
-    FAILURE_CODE_3013: 3013,
-    FAILURE_CODE_3014: 3014,
-    FAILURE_CODE_3015: 3015,
-    FAILURE_CODE_3016: 3016,
-    FAILURE_CODE_3017: 3017,
-    FAILURE_CODE_3018: 3018,
-    FAILURE_CODE_5010: 5010,
-    FAILURE_CODE_5012: 5012,
-    FAILURE_CODE_5014: 5014,
-    FAILURE_CODE_5005: 5005,
-}
-
- 
-/**
- * 获取Token
- *  @function
- */
-export function getToken() {
-    var urlToken = browser.urlHasValue("token", true)
-    if (urlToken) {
-        // 设置token共享给用户中心
-        localStorage.setItem("token", urlToken)
-    }
-    return urlToken || localStorage.getItem("token") || ""
-}
-
-/**
- * 根据状态码的结果处理后续操作
- * @function
- * @param {number} code - 状态码
- * @param {function} callback - 回调
- */
-export function statusCodesHandler(code, callback) {
-    if (code == statusCode.EXCEPTION) {
-        return $alert({ content: i18n.t("tips.exception") })
-    }
-
-    if (
-        code == statusCode.FAILURE_CODE_3002 ||
-        code == statusCode.FAILURE_CODE_3003 ||
-        code == statusCode.FAILURE_CODE_3004
-    ) {
-        callback(code)
-        return showLoginTips()
-    }
-
-    if (code == statusCode.FAILURE_CODE_3001) {
-        callback(code)
-        return $alert({ content: i18n.t("tips.params_notfound") })
-    }
-
-    if (code == statusCode.FAILURE_CODE_3017) {
-        callback(code)
-        return $alert({ content: i18n.t("tips.file_notfound") })
-    }
-
-    if (code == statusCode.FAILURE_CODE_5005) {
-        /* if (!config.isEdit) {
-            return (location.href = config.pages.NotFound)
-        } */
-        callback(code)
-        return $alert({ content: i18n.t("tips.scene_notfound") })
-    }
-
-    if (code == statusCode.FAILURE_CODE_5010) {
-        callback(code)
-        return $alert({ content: i18n.t("tips.camera_notfound") })
-    }
-
-    if (code == statusCode.FAILURE_CODE_5012) {
-        callback(code)
-        return $alert({ content: i18n.t("tips.data_error") })
-    }
-
-    if (code == statusCode.FAILURE_CODE_5014) {
-        callback(code)
-        return $alert({ content: i18n.t("tips.auth_deny") })
-    }
-
-    return statusCode.NEXT
-}
-
-$.ajaxSetup({
-    headers: {},
-    beforeSend: function(xhr) {
-        const token = getToken()
-        if (token) {
-            xhr.setRequestHeader("token", token)
-        } else if (!token && this.url.indexOf("isLogin") != -1) {
-            showLoginTips()
-        }
-
-        /* if (config.oem == "localshow") {
-            // 本地版本兼容当前目录
-            if (this.url.indexOf("http") == -1 && this.url.indexOf("/") == 0) {
-                this.url = this.url.substr(1)
-            }
-        } */
-
-        // if(this.url.indexOf('http')==-1 && this.url.indexOf('/') !=0){
-        //     this.url = '/'+this.url
-        // }
-    },
-    error: function(xhr, status, error) {
-        // 出错时默认的处理函数
-        if (this.url.indexOf("/scene.json") != -1 && xhr.status == 404) {
-            return $alert({ content: i18n.t("tips.scene_notfound") })
-        } else if (this.type === "POST") {
-            return $alert({ content: i18n.t("tips.network_error") })
-        }
-    },
-    success: function(result) {},
-    complete: function() {
-        // Post类型请求无论成功或失败都关闭等待提示
-        if (this.type === "POST") {
-            http.__loading && $waiting.hide()
-        }
-
-        http.__loading = true
-    },
-})
-
-/**
- * @namespace http
- * @type {Object}
- */
-export const http = {
-    statusCode,
-    __loading: true,
-    __request(xhr, method, url, data, done, fail) {
-        if (typeof done != "function") {
-            done = noop
-        }
-        if (typeof fail != "function") {
-            fail = noop
-        }
-
-        xhr.done(result => {
-            if (typeof result.code !== "undefined") {
-                const flag = statusCodesHandler(result.code, function(code) {
-                    // 需要登录的状态
-                    if (
-                        code == statusCode.FAILURE_CODE_3001 ||
-                        code == statusCode.FAILURE_CODE_3002 ||
-                        code == statusCode.FAILURE_CODE_3003 ||
-                        code == statusCode.FAILURE_CODE_3004
-                    ) {
-                        if (url.indexOf("isLogin") == -1 && url.indexOf("openSceneBykey") == -1) {
-                            postQueue.push(function() {
-                                http[method](url, data, done, fail)
-                            })
-                        }
-                    }
-                    fail()
-                })
-
-                if (flag === statusCode.NEXT) {
-                    done(result, result.code == 0)
-                }
-            } else {
-                done(result)
-            }
-        })
-
-        xhr.fail(fail)
-
-        xhr.always(() => (xhr = null))
-
-        return xhr
-    },
-    /**
-     * Get请求
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    get(url, data = {}, done, fail) {
-        if (/\.json/.test(url)) {
-            // json文件格式自动调用getJson方法
-            return this.getJson(url, data, done, fail)
-        }
-        return this.__request($.get(url, data), "get", url, data, done, fail)
-    },
-    /**
-     * Get Blob请求
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    getText(url, data = {}, done, fail) {
-        return this.__request(
-            $.ajax({
-                url: url,
-                dataType: "text",
-            }),
-            "getText",
-            url,
-            data,
-            done,
-            fail
-        )
-    },
-    /**
-     * GetJson请求 读取json文件数据
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    getJson(url, data = {}, done, fail) {
-        return this.__request($.getJSON(url, data), "get", url, data, done, fail)
-    },
-    /**
-     * Get Blob请求
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    getBlob(url, data = {}, done, fail) {
-        return this.__request(
-            $.ajax({
-                url: url,
-                dataType: "blob",
-            }),
-            "getBlob",
-            url,
-            data,
-            done,
-            fail
-        )
-    },
-    /**
-     * Get Arraybuffer请求
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    getArraybuffer(url, data = {}, done, fail) {
-        return this.__request(
-            $.ajax({
-                url: url,
-                dataType: "arraybuffer",
-            }),
-            "getArraybuffer",
-            url,
-            data,
-            done,
-            fail
-        )
-    },
-    /**
-     * Post 请求
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    post(url, data = {}, done, fail) {
-        if (url.indexOf("isLogin") == -1) {
-            http.__loading && $waiting.show()
-        }
-
-        return this.__request($.post(url, data), "post", url, data, done, fail)
-    },
-    /**
-     * PostJson 请求
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    postJson(url, data = {}, done, fail) {
-        http.__loading && $waiting.show()
-        return this.__request(
-            $.ajax({
-                type: "POST",
-                url: url,
-                contentType: "application/json",
-                data: JSON.stringify(data),
-            }),
-            "postJson",
-            url,
-            data,
-            done,
-            fail
-        )
-    },
-    /**
-     * Post 表单 支持文件上传
-     * @param {String} url 请求地址
-     * @param {FormData?} formData 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    postForm(url, formData, done, fail, onProgress) {
-        if (typeof onProgress === "function") {
-            return this.__request(
-                $.ajax({
-                    type: "POST",
-                    url: url,
-                    processData: false,
-                    contentType: false,
-                    data: formData,
-                    xhr: function() {
-                        const xhr = new XMLHttpRequest()
-                        xhr.upload.addEventListener("progress", function(e) {
-                            onProgress((e.loaded / e.total) * 100 + "%")
-                        })
-                        return xhr
-                    },
-                }),
-                "postForm",
-                url,
-                formData,
-                done,
-                fail
-            )
-        } else {
-            http.__loading && $waiting.show()
-            return this.__request(
-                $.ajax({
-                    type: "POST",
-                    url: url,
-                    processData: false,
-                    contentType: false,
-                    data: formData,
-                }),
-                "postForm",
-                url,
-                formData,
-                done,
-                fail
-            )
-        }
-    },
-    /**
-     * 加载图片
-     * @param {String} url 请求地址
-     * @param {Number?} retry 重试次数,默认为3
-     */
-    loadImage(url, retry = 3) {
-        const def = $.Deferred()
-        const img = new Image()
-
-        /* if (process.env.VUE_APP_REGION == "AWS" && url.indexOf("x-oss-process=image") != -1) {
-            var arr = url.split("?")
-            url = arr[0] + encodeURIComponent("?" + arr[1].replace(/\//g, "@"))
-        } */
-
-        const load = () => {
-            console.warn("Retrying load image: " + url)
-            this.loadImage(url, retry - 1)
-                .done(def.resolve.bind(def))
-                .progress(def.notify.bind(def))
-                .fail(def.reject.bind(def))
-        }
-
-        img.onerror = function() {
-            retry > 0 ? setTimeout(() => load(), 1e3) : def.reject(`[${url}]加载失败`)
-        }
-
-        img.onload = function() {
-            def.resolve(img)
-        }
-
-        img.crossOrigin = "anonymous"
-        img.src = url
-
-        return def
-    },
-    /**
-     * 上传文件
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    uploadFile(url, data = {}, done, fail, onProgress) {
-        const form = new FormData()
-        // if (file.needTransfer) { //ie和苹果都不支持dataURLtoFile得传送,所以只能用blob
-        //     form.append("file", common.dataURLtoBlob(file.file), file.name || file.file.name);
-        // } else {
-        //     form.append("file", file.file, file.name || file.file.name);
-        // }
-        for (let key in data) {
-            if (key == "file") {
-                form.append("file", data[key], data.filename || data[key].name)
-            } else if (key != "filename") {
-                form.append(key, data[key])
-            }
-        }
-        return this.postForm(url, form, done, fail, onProgress)
-    },
-    /**
-     * 上传文件
-     * @param {String} url 请求地址
-     * @param {Object?} data 请求参数 {file:'base64 string',filename:'image.jpg',...}
-     * @param {Function?} done 成功回调
-     * @param {Function?} fail 失败回调
-     */
-    uploadBlobFile(url, data = {}, done, fail) {
-        const form = new FormData()
-        for (let key in data) {
-            if (key === "file") {
-                form.append("file", base64ToBlob(data.file), data.filename)
-            } else if (key != "filename") {
-                form.append(key, data[key])
-            }
-        }
-        return this.postForm(url, form, done, fail)
-    },
-}

+ 256 - 301
src/viewer/EDLRenderer.js

@@ -2,29 +2,24 @@
 import * as THREE from "../../libs/three.js/build/three.module.js";
 import {PointCloudSM} from "../utils/PointCloudSM.js";
 import {EyeDomeLightingMaterial} from "../materials/EyeDomeLightingMaterial.js";
-import {SphereVolume} from "../objects/tool/Volume.js";
+import {SphereVolume} from "../utils/Volume.js";
 import {Utils} from "../utils.js";
-import {copyShader} from '../materials/shaders/otherShaders.js'
-import {Features} from "../Features.js";
 
-export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
+export class EDLRenderer{
 	constructor(viewer){
 		this.viewer = viewer;
 
 		this.edlMaterial = null;
 
-		//this.rtRegular;
-		this.rtEDLs = new Map
+		this.rtRegular;
+		this.rtEDL;
 
 		this.gl = viewer.renderer.getContext();
-        //反正也没用到,注释了:
-		//this.shadowMap = new PointCloudSM(this.viewer.pRenderer);
-        
-        viewer.addEventListener('resize',this.resize.bind(this))
-        this.initEDL(viewer) 
+
+		this.shadowMap = new PointCloudSM(this.viewer.pRenderer);
 	}
 
-	initEDL(viewer){
+	initEDL(){
 		if (this.edlMaterial != null) {
 			return;
 		}
@@ -33,106 +28,126 @@ export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
 		this.edlMaterial.depthTest = true;
 		this.edlMaterial.depthWrite = true;
 		this.edlMaterial.transparent = true;
-        
-         
 
-		/* this.rtRegular = new THREE.WebGLRenderTarget(viewer.mainViewport.resolution2.x, viewer.mainViewport.resolution2.y, {
+		this.rtEDL = new THREE.WebGLRenderTarget(1024, 1024, {
+			minFilter: THREE.NearestFilter,
+			magFilter: THREE.NearestFilter,
+			format: THREE.RGBAFormat,
+			type: THREE.FloatType,
+			depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
+		});
+
+		this.rtRegular = new THREE.WebGLRenderTarget(1024, 1024, {
 			minFilter: THREE.NearestFilter,
 			magFilter: THREE.NearestFilter,
 			format: THREE.RGBAFormat,
 			depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
 		});
-         */
-        
-        
-        
-        
-       /*  let copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
-          
-        this.copyMaterial = new THREE.ShaderMaterial(	{
-            uniforms: copyUniforms,
-            vertexShader: copyShader.vertexShader,
-            fragmentShader: copyShader.fragmentShader,
-            //premultipliedAlpha: true,
-            transparent: true,
-            //blending: THREE.AdditiveBlending,
-            depthTest: false,
-            depthWrite: false
-        }); */
-        
 	};
 
-	resize(e ){
-        if(Features.EXT_DEPTH.isSupported()){  
-            let viewport = e.viewport
-             
-            this.getRtEDL(viewport).setSize(viewport.resolution2.x, viewport.resolution2.y);
-        }
+	resize(width, height){
+		if(this.screenshot){
+			width = this.screenshot.target.width;
+			height = this.screenshot.target.height;
+		}
+
+		this.rtEDL.setSize(width , height);
+		this.rtRegular.setSize(width , height);
 	}
 
-	
+	makeScreenshot(camera, size, callback){
+
+		if(camera === undefined || camera === null){
+			camera = this.viewer.scene.getActiveCamera();
+		}
+
+		if(size === undefined || size === null){
+			size = this.viewer.renderer.getSize(new THREE.Vector2());
+		}
+
+		let {width, height} = size;
+
+		//let maxTextureSize = viewer.renderer.capabilities.maxTextureSize;
+		//if(width * 4 < 
+		width = 2 * width;
+		height = 2 * height;
+
+		let target = new THREE.WebGLRenderTarget(width, height, {
+			format: THREE.RGBAFormat,
+		});
+
+		this.screenshot = {
+			target: target
+		};
+
+		// HACK? removed because of error, was this important?
+		//this.viewer.renderer.clearTarget(target, true, true, true);
+
+		this.render();
 
-	clearTargets(params={}){
+		let pixelCount = width * height;
+		let buffer = new Uint8Array(4 * pixelCount);
+
+		this.viewer.renderer.readRenderTargetPixels(target, 0, 0, width, height, buffer);
+
+		// flip vertically
+		let bytesPerLine = width * 4;
+		for(let i = 0; i < parseInt(height / 2); i++){
+			let j = height - i - 1;
+
+			let lineI = buffer.slice(i * bytesPerLine, i * bytesPerLine + bytesPerLine);
+			let lineJ = buffer.slice(j * bytesPerLine, j * bytesPerLine + bytesPerLine);
+			buffer.set(lineJ, i * bytesPerLine);
+			buffer.set(lineI, j * bytesPerLine);
+		}
+
+		this.screenshot.target.dispose();
+		delete this.screenshot;
+
+		return {
+			width: width,
+			height: height,
+			buffer: buffer
+		};
+	}
+
+	clearTargets(){
 		const viewer = this.viewer;
 		const {renderer} = viewer;
 
 		const oldTarget = renderer.getRenderTarget();
-        
-        if(params.target){//add
-            renderer.setRenderTarget( params.target);
-            renderer.clear() 
-        }
-        
-        
-        
-        if(Features.EXT_DEPTH.isSupported()){ 
-            if(params.rtEDL){
-                renderer.setRenderTarget( params.rtEDL);
-                renderer.clear() 
-            }else{
-                var rtEDL = this.getRtEDL(params.viewport)
-                if(rtEDL){
-                    renderer.setRenderTarget( rtEDL );
-                    renderer.clear( true, true, true );
-                }
-                
-            }
-        }
-        
-        
-        
-		//renderer.setRenderTarget( this.rtRegular );
-		//renderer.clear( true, true, false );
+
+		renderer.setRenderTarget( this.rtEDL );
+		renderer.clear( true, true, true );
+
+		renderer.setRenderTarget( this.rtRegular );
+		renderer.clear( true, true, false );
 
 		renderer.setRenderTarget(oldTarget);
 	}
- 
- 
-    getRtEDL(viewport){//根据不同viewport返回rtEDL的texture
-        if(!viewport){
-            console.warn('getRtEDL没传viewport!!!! !!!!!!!!!!')
-            viewport = viewer.mainViewport
-        }
-        var rtEDL = this.rtEDLs.get(viewport)
-        if(!rtEDL){
-            if(Features.EXT_DEPTH.isSupported()){ 
-                rtEDL = new THREE.WebGLRenderTarget(viewport.resolution2.x, viewport.resolution2.y, {
-                    minFilter: THREE.NearestFilter,
-                    magFilter: THREE.NearestFilter,
-                    format: THREE.RGBAFormat,
-                    type: THREE.FloatType,
-                    depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
-                }); 
-                //注: 部分手机在resize时会崩溃,经检验去掉rtEDL的resize可以解决,所以更应该注释掉这个
-            
-                
-                this.rtEDLs.set(viewport, rtEDL)
-            }  
-        } 
-    
-        return rtEDL
-    }
- 
+
+	clear(){
+		this.initEDL();
+		const viewer = this.viewer;
+
+		const {renderer, background} = viewer;
+
+		if(background === "skybox"){
+			renderer.setClearColor(0x000000, 0);
+		} else if (background === 'gradient') {
+			renderer.setClearColor(0x000000, 0);
+		} else if (background === 'black') {
+			renderer.setClearColor(0x000000, 1);
+		} else if (background === 'white') {
+			renderer.setClearColor(0xFFFFFF, 1);
+		} else {
+			renderer.setClearColor(0x000000, 0);
+		}
+		
+		renderer.clear();
+
+		this.clearTargets();
+	}
 
 	renderShadowMap(visiblePointClouds, camera, lights){
 
@@ -141,7 +156,7 @@ export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
 		const doShadows = lights.length > 0 && !(lights[0].disableShadowUpdates);
 		if(doShadows){
 			let light = lights[0];
- 
+
 			this.shadowMap.setLight(light);
 
 			let originalAttributes = new Map();
@@ -169,220 +184,160 @@ export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
 
 	}
 
-	render(params={}){
- 
-         
-        /* 
-            渲染顺序:
-                底层:背景 -> skybox(也可中间)
-                中间层(含有深度信息):1 点云、marker等mesh,
-                                        2 测量线(现在被做成借用depthTex
-                顶层:maginifier 
-            magnifier的贴图渲染不需要顶层、中间层只需要点云。
-         */
-        
-        const viewer = this.viewer; 
+	render(params){
+		this.initEDL();
+
+		const viewer = this.viewer;
 		let camera = params.camera ? params.camera : viewer.scene.getActiveCamera();
-		const resolution = params.viewport ? params.viewport.resolution2 : this.viewer.renderer.getSize(new THREE.Vector2());
+		const {width, height} = this.viewer.renderer.getSize(new THREE.Vector2());
+
+
+		viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
 		
-        
-        //viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
-	
-		       
+		this.resize(width, height);
+
+		const visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible);
+
+		if(this.screenshot){
+			let oldBudget = Potree.pointBudget;
+			Potree.pointBudget = Math.max(10 * 1000 * 1000, 2 * oldBudget);
+			let result = Potree.updatePointClouds(
+				viewer.scene.pointclouds, 
+				camera, 
+				viewer.renderer);
+			Potree.pointBudget = oldBudget;
+		}
+
 		let lights = [];
-		/* viewer.scene.scene.traverse(node => {
+		viewer.scene.scene.traverse(node => {
 			if(node.type === "SpotLight"){
 				lights.push(node);
 			}
-		}); */
-
-
-        viewer.renderer.setRenderTarget(params.target || null);
-        let background = params.background || viewer.background;
-        let backgroundOpacity = params.backgroundOpacity == void 0 ? viewer.backgroundOpacity : params.backgroundOpacity//如果想完全透明,只需要backgroundOpacity为0
-        if(backgroundOpacity != 0){//绘制背景
-            if(background === "skybox"){
-                viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation);
-                viewer.skybox.camera.fov = viewer.scene.cameraP.fov;
-                viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect;
-
-                viewer.skybox.parent.rotation.x = 0;
-                viewer.skybox.parent.updateMatrixWorld();
-
-                viewer.skybox.camera.updateProjectionMatrix();
-                
-                viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera);
-            }else if(background === 'gradient'){ 
-                viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
-                viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
-            }else if(background === 'overlayColor'){//在不clear的前提下加一层背景色 
-                viewer.scene.bg2.material.color.copy(params.backgroundColor)
-                viewer.scene.bg2.material.opacity = params.backgroundOpacity
-                viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg2);
-                viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
-            }  
-            
-        }
-        
-        //skybox  全景图
-        if(!params.magnifier){
-            viewer.setCameraLayers(camera, ['skybox'])
-            let useDepthTex
-            if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.pointcloud.hasDepthTex && Features.EXT_DEPTH.isSupported()){//渲染深度图
-                useDepthTex = true
-                viewer.renderer.setRenderTarget(params.rtEDL || this.getRtEDL(params.viewport)) //将带有深度图的skybox画在rtEDL一下,这样就不需要绘制后边的点云了
-                viewer.renderer.render(viewer.scene.scene, camera);
-                viewer.renderer.setRenderTarget(params.target || null);
-            } 
-            viewer.renderer.render(viewer.scene.scene, camera);
-            if(useDepthTex)return 
-        } 
-         
-        
-		const visiblePointClouds2 = viewer.scene.pointclouds.filter(pc => viewer.getObjVisiByReason(pc,'datasetSelection')  ); //需要绘制到rtEDL的
-		const showPointClouds = params.magnifier ? visiblePointClouds2.length>0 : viewer.scene.pointclouds.some(e=>e.visible) //是否有需要绘制到屏幕的
-         
-        visiblePointClouds2.forEach(e=>{//为了绘制到depthTexture,先显示(展示全景图时隐藏了点云,所以需要显示下。且放大镜需要绘制点云)
-            e.oldVisi = e.visible
-            e.visible = true; 
-        })
-
-        
-         
-        viewer.setCameraLayers(camera, ['pointcloud'])
-        camera.layers.set(Potree.config.renderLayers.pointcloud);
-		
-        //TODO adapt to multiple lights
-		//this.renderShadowMap(visiblePointClouds2, camera, lights);  //???????
-
-		{ 
-			for (let pointcloud of visiblePointClouds2) {
-                
-                let material = pointcloud.material; 
-                let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x;
-                material.fov = THREE.Math.degToRad(camera.fov) 
-                /* material.screenWidth = width;
-                material.screenHeight = height; */
-                material.resolution = resolution 
-               
-                
-                
-                material.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z);
-                material.near = camera.near;
-                material.far = camera.far;
-                material.uniforms.octreeSize.value = octreeSize
-	
-                if(viewer.useEDL && Potree.settings.displayMode != 'showPanos'){ 
-                    /* material.weighted = false;
-                    material.useLogarithmicDepthBuffer = false;  */ 
-                    material.useEDL = true;
-                    //material.fakeEDL = false; //add
-                }else{
-                    material.useEDL = false;
-                    //material.fakeEDL = true; //add 使也输出深度
-                }   
-                
+		});
+
+		if(viewer.background === "skybox"){
+			viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation);
+			viewer.skybox.camera.fov = viewer.scene.cameraP.fov;
+			viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect;
+
+			viewer.skybox.parent.rotation.x = 0;
+			viewer.skybox.parent.updateMatrixWorld();
+
+			viewer.skybox.camera.updateProjectionMatrix();
+			viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera);
+		} else if (viewer.background === 'gradient') {
+			viewer.renderer.render(viewer.scene.sceneBG, viewer.scene.cameraBG);
+		} 
+
+		//TODO adapt to multiple lights
+		this.renderShadowMap(visiblePointClouds, camera, lights);
+
+		{ // COLOR & DEPTH PASS
+			for (let pointcloud of visiblePointClouds) {
+				let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x;
+
+				let material = pointcloud.material;
+				material.weighted = false;
+				material.useLogarithmicDepthBuffer = false;
+				material.useEDL = true;
+
+				material.screenWidth = width;
+				material.screenHeight = height;
+				material.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture;
+				material.uniforms.octreeSize.value = octreeSize;
+				material.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(pointcloud.scale.x, pointcloud.scale.y, pointcloud.scale.z);
 			}
-            
-            
-            if(Features.EXT_DEPTH.isSupported() && !params.dontRenderRtEDL){  
-                //借用rtEDL存储深度信息  
-                viewer.renderer.setRenderTarget(params.rtEDL || this.getRtEDL(params.viewport)/* this.rtEDL */);
-               
-               
-                if(visiblePointClouds2.length>0){ 
-                    //渲染scenePointCloud到rtEDL
-                    viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, params.rtEDL || this.getRtEDL(params.viewport), {
-                        shadowMaps:  lights.length > 0 ? [this.shadowMap] : null,
-                        clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
-                        transparent: false,
-                    });
-                } 
-                if(Potree.settings.intersectOnObjs){// model也要渲染到rtEDL
-                    camera.layers.set(Potree.config.renderLayers.model);
-                    viewer.objs.traverse(e=>{if(e.material)e._OlddepthWrite = e.material.depthWrite, e.material.depthWrite = true}) //否则半透明的mesh无法遮住测量线
-                    viewer.renderer.render(viewer.scene.scene, camera);
-                    viewer.objs.traverse(e=>{if(e.material)e.material.depthWrite = e._OlddepthWrite})
-                    //缺点:半透明的model 就算完全透明, 也会遮住测量线
-                }
-                //test
-                /* {
-                    viewer.objs.traverse((obj)=>{
-                        if(obj.material){
-                            obj.material = obj.depthMat
-                        }
-                    })
-                    viewer.setCameraLayers(camera, ['sceneObjects'])
-                    viewer.renderer.render(viewer.scene.scene, camera)
-                    viewer.objs.traverse((obj)=>{
-                        if(obj.material){
-                            obj.material = obj.standardMat
-                        }
-                    })
-                } */ 
-            } 
+			
+			// TODO adapt to multiple lights
+			viewer.renderer.setRenderTarget(this.rtEDL);
+			
+			if(lights.length > 0){
+				viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtEDL, {
+					clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
+					shadowMaps: [this.shadowMap],
+					transparent: false,
+				});
+			}else{
+
+				
+				// let test = camera.clone();
+				// test.matrixAutoUpdate = false;
+
+				// //test.updateMatrixWorld = () => {};
+
+				// let mat = new THREE.Matrix4().set(
+				// 	1, 0, 0, 0,
+				// 	0, 0, 1, 0,
+				// 	0, -1, 0, 0,
+				// 	0, 0, 0, 1,
+				// );
+				// mat.invert()
+
+				// test.matrix.multiplyMatrices(mat, test.matrix);
+				// test.updateMatrixWorld();
+
+				//test.matrixWorld.multiplyMatrices(mat, test.matrixWorld);
+				//test.matrixWorld.multiply(mat);
+				//test.matrixWorldInverse.invert(test.matrixWorld);
+				//test.matrixWorldInverse.multiplyMatrices(test.matrixWorldInverse, mat);
+				
+
+				viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtEDL, {
+					clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
+					transparent: false,
+				});
+			}
+
+			
+		}
+
+		viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer, renderTarget: this.rtRegular});
+		viewer.renderer.setRenderTarget(null);
+		viewer.renderer.render(viewer.scene.scene, camera);
+
+		{ // EDL PASS
+
+			const uniforms = this.edlMaterial.uniforms;
+
+			uniforms.screenWidth.value = width;
+			uniforms.screenHeight.value = height;
+
+			let proj = camera.projectionMatrix;
+			let projArray = new Float32Array(16);
+			projArray.set(proj.elements);
+
+			uniforms.uNear.value = camera.near;
+			uniforms.uFar.value = camera.far;
+			uniforms.uEDLColor.value = this.rtEDL.texture;
+			uniforms.uEDLDepth.value = this.rtEDL.depthTexture;
+			uniforms.uProj.value = projArray;
+
+			uniforms.edlStrength.value = viewer.edlStrength;
+			uniforms.radius.value = viewer.edlRadius;
+			uniforms.opacity.value = viewer.edlOpacity; // HACK
+			
+			Utils.screenPass.render(viewer.renderer, this.edlMaterial);
+
+			if(this.screenshot){
+				Utils.screenPass.render(viewer.renderer, this.edlMaterial, this.screenshot.target);
+			}
+
 		}
-        
-        
-        
-         
-        //渲染到rtEDL完毕
-		viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer/* , renderTarget: this.rtRegular */});
-		viewer.renderer.setRenderTarget(params.target || null);
-        
-        if(!params.magnifier)visiblePointClouds2.forEach(e=>{//放大镜显示点云
-            e.visible = e.oldVisi
-        })
-        
-         
-         
-        if(showPointClouds){ //绘制点云到画布
-            if(viewer.useEDL) {  //设置edlMaterial
-                //Features.EXT_DEPTH不支持的话不会到这一块
-                 
-                const uniforms = this.edlMaterial.uniforms;
-                //if(viewer.useEDL){
-                    /* uniforms.screenWidth.value = width;
-                    uniforms.screenHeight.value = height; */
-                    uniforms.resolution.value.copy(resolution)
-                    
-                    uniforms.edlStrength.value = viewer.edlStrength;
-                    uniforms.radius.value = viewer.edlRadius;
-                    uniforms.useEDL.value = 1;//add
-                /* }else{
-                    uniforms.useEDL.value = 0;//add
-                } */
-                
-                let proj = camera.projectionMatrix;
-                let projArray = new Float32Array(16);
-                projArray.set(proj.elements);
-                uniforms.uProj.value = projArray;
-             
-                uniforms.uEDLColor.value = (params.rtEDL || this.getRtEDL(params.viewport)).texture;
-                //uniforms.uEDLDepth.value = (params.rtEDL || this.getRtEDL(params.viewport)).depthTexture; //其实没用到
-                 
-                uniforms.opacity.value = viewer.edlOpacity; // HACK
-                 
-                
-                Utils.screenPass.render(viewer.renderer, this.edlMaterial, params.target);
-            }else{
-                //渲染点云 (直接用rtEDL上的会失去抗锯齿)
-                
-                let prop = {
-                    shadowMaps:  lights.length > 0 ? [this.shadowMap] : null,
-                    clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)) ,
-                    notAdditiveBlending: Potree.settings.notAdditiveBlending//add 否则透明的点云会挡住后面的模型。 加上这句后竟然透明不会叠加了!
-                }
-                 
-                viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, null , prop);  
-            }
-        }        
-          
-		//viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
-        visiblePointClouds2.forEach(e=>{
-            e.visible = e.oldVisi
-        })
-        
+
+		viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer});
+
+		viewer.renderer.clearDepth();
+
+		viewer.transformationTool.update();
+
+		viewer.dispatchEvent({type: "render.pass.perspective_overlay",viewer: viewer});
+
+		viewer.renderer.render(viewer.controls.sceneControls, camera);
+		viewer.renderer.render(viewer.clippingTool.sceneVolume, camera);
+		viewer.renderer.render(viewer.transformationTool.scene, camera);
+		
+		viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
+
 	}
 }
 

+ 388 - 0
src/viewer/EDLRendererNew.js

@@ -0,0 +1,388 @@
+
+import * as THREE from "../../libs/three.js/build/three.module.js";
+import {PointCloudSM} from "../utils/PointCloudSM.js";
+import {EyeDomeLightingMaterial} from "../materials/EyeDomeLightingMaterial.js";
+import {SphereVolume} from "../utils/Volume.js";
+import {Utils} from "../utils.js";
+import {copyShader} from '../materials/shaders/otherShaders.js'
+import {Features} from "../Features.js";
+
+export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
+	constructor(viewer){
+		this.viewer = viewer;
+
+		this.edlMaterial = null;
+
+		//this.rtRegular;
+		this.rtEDLs = new Map
+
+		this.gl = viewer.renderer.getContext();
+        //反正也没用到,注释了:
+		//this.shadowMap = new PointCloudSM(this.viewer.pRenderer);
+        
+        viewer.addEventListener('resize',this.resize.bind(this))
+        this.initEDL(viewer) 
+	}
+
+	initEDL(viewer){
+		if (this.edlMaterial != null) {
+			return;
+		}
+
+		this.edlMaterial = new EyeDomeLightingMaterial();
+		this.edlMaterial.depthTest = true;
+		this.edlMaterial.depthWrite = true;
+		this.edlMaterial.transparent = true;
+        
+         
+
+		/* this.rtRegular = new THREE.WebGLRenderTarget(viewer.mainViewport.resolution2.x, viewer.mainViewport.resolution2.y, {
+			minFilter: THREE.NearestFilter,
+			magFilter: THREE.NearestFilter,
+			format: THREE.RGBAFormat,
+			depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
+		});
+         */
+        
+        
+        
+        
+       /*  let copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
+          
+        this.copyMaterial = new THREE.ShaderMaterial(	{
+            uniforms: copyUniforms,
+            vertexShader: copyShader.vertexShader,
+            fragmentShader: copyShader.fragmentShader,
+            //premultipliedAlpha: true,
+            transparent: true,
+            //blending: THREE.AdditiveBlending,
+            depthTest: false,
+            depthWrite: false
+        }); */
+        
+	};
+
+	resize(e ){
+        if(Features.EXT_DEPTH.isSupported()){  
+            let viewport = e.viewport
+             
+            this.getRtEDL(viewport).setSize(viewport.resolution2.x, viewport.resolution2.y);
+        }
+	}
+
+	
+
+	clearTargets(params={}){
+		const viewer = this.viewer;
+		const {renderer} = viewer;
+
+		const oldTarget = renderer.getRenderTarget();
+        
+        if(params.target){//add
+            renderer.setRenderTarget( params.target);
+            renderer.clear() 
+        }
+        
+        
+        
+        if(Features.EXT_DEPTH.isSupported()){ 
+            if(params.rtEDL){
+                renderer.setRenderTarget( params.rtEDL);
+                renderer.clear() 
+            }else{
+                var rtEDL = this.getRtEDL(params.viewport)
+                if(rtEDL){
+                    renderer.setRenderTarget( rtEDL );
+                    renderer.clear( true, true, true );
+                }
+                
+            }
+        }
+        
+        
+        
+		//renderer.setRenderTarget( this.rtRegular );
+		//renderer.clear( true, true, false );
+
+		renderer.setRenderTarget(oldTarget);
+	}
+ 
+ 
+    getRtEDL(viewport){//根据不同viewport返回rtEDL的texture
+        if(!viewport){
+            console.warn('getRtEDL没传viewport!!!! !!!!!!!!!!')
+            viewport = viewer.mainViewport
+        }
+        var rtEDL = this.rtEDLs.get(viewport)
+        if(!rtEDL){
+            if(Features.EXT_DEPTH.isSupported()){ 
+                rtEDL = new THREE.WebGLRenderTarget(viewport.resolution2.x, viewport.resolution2.y, {
+                    minFilter: THREE.NearestFilter,
+                    magFilter: THREE.NearestFilter,
+                    format: THREE.RGBAFormat,
+                    type: THREE.FloatType,
+                    depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
+                }); 
+                //注: 部分手机在resize时会崩溃,经检验去掉rtEDL的resize可以解决,所以更应该注释掉这个
+            
+                
+                this.rtEDLs.set(viewport, rtEDL)
+            }  
+        } 
+    
+        return rtEDL
+    }
+ 
+
+	renderShadowMap(visiblePointClouds, camera, lights){
+
+		const {viewer} = this;
+
+		const doShadows = lights.length > 0 && !(lights[0].disableShadowUpdates);
+		if(doShadows){
+			let light = lights[0];
+ 
+			this.shadowMap.setLight(light);
+
+			let originalAttributes = new Map();
+			for(let pointcloud of viewer.scene.pointclouds){
+				// TODO IMPORTANT !!! check
+				originalAttributes.set(pointcloud, pointcloud.material.activeAttributeName);
+				pointcloud.material.disableEvents();
+				pointcloud.material.activeAttributeName = "depth";
+				//pointcloud.material.pointColorType = PointColorType.DEPTH;
+			}
+
+			this.shadowMap.render(viewer.scene.scenePointCloud, camera);
+
+			for(let pointcloud of visiblePointClouds){
+				let originalAttribute = originalAttributes.get(pointcloud);
+				// TODO IMPORTANT !!! check
+				pointcloud.material.activeAttributeName = originalAttribute;
+				pointcloud.material.enableEvents();
+			}
+
+			viewer.shadowTestCam.updateMatrixWorld();
+			viewer.shadowTestCam.matrixWorldInverse.copy(viewer.shadowTestCam.matrixWorld).invert();
+			viewer.shadowTestCam.updateProjectionMatrix();
+		}
+
+	}
+
+	render(params={}){
+ 
+         
+        /* 
+            渲染顺序:
+                底层:背景 -> skybox(也可中间)
+                中间层(含有深度信息):1 点云、marker等mesh,
+                                        2 测量线(现在被做成借用depthTex
+                顶层:maginifier 
+            magnifier的贴图渲染不需要顶层、中间层只需要点云。
+         */
+        
+        const viewer = this.viewer; 
+		let camera = params.camera ? params.camera : viewer.scene.getActiveCamera();
+		const resolution = params.viewport ? params.viewport.resolution2 : this.viewer.renderer.getSize(new THREE.Vector2());
+		
+        
+        //viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
+	
+		       
+		let lights = [];
+		/* viewer.scene.scene.traverse(node => {
+			if(node.type === "SpotLight"){
+				lights.push(node);
+			}
+		}); */
+
+
+        viewer.renderer.setRenderTarget(params.target || null);
+        let background = params.background || viewer.background;
+        let backgroundOpacity = params.backgroundOpacity == void 0 ? viewer.backgroundOpacity : params.backgroundOpacity//如果想完全透明,只需要backgroundOpacity为0
+        if(backgroundOpacity != 0){//绘制背景
+            if(background === "skybox"){
+                viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation);
+                viewer.skybox.camera.fov = viewer.scene.cameraP.fov;
+                viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect;
+
+                viewer.skybox.parent.rotation.x = 0;
+                viewer.skybox.parent.updateMatrixWorld();
+
+                viewer.skybox.camera.updateProjectionMatrix();
+                
+                viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera);
+            }else if(background === 'gradient'){ 
+                viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
+                viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
+            }else if(background === 'overlayColor'){//在不clear的前提下加一层背景色 
+                viewer.scene.bg2.material.color.copy(params.backgroundColor)
+                viewer.scene.bg2.material.opacity = params.backgroundOpacity
+                viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg2);
+                viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
+            }  
+            
+        }
+        
+        //skybox  全景图
+        if(!params.magnifier){
+            viewer.setCameraLayers(camera, ['skybox'])
+            let useDepthTex
+            if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.pointcloud.hasDepthTex && Features.EXT_DEPTH.isSupported()){//渲染深度图
+                useDepthTex = true
+                viewer.renderer.setRenderTarget(params.rtEDL || this.getRtEDL(params.viewport)) //将带有深度图的skybox画在rtEDL一下,这样就不需要绘制后边的点云了
+                viewer.renderer.render(viewer.scene.scene, camera);
+                viewer.renderer.setRenderTarget(params.target || null);
+            } 
+            viewer.renderer.render(viewer.scene.scene, camera);
+            if(useDepthTex)return 
+        } 
+         
+        
+		const visiblePointClouds2 = viewer.scene.pointclouds.filter(pc => viewer.getObjVisiByReason(pc,'datasetSelection')  ); //需要绘制到rtEDL的
+		const showPointClouds = params.magnifier ? visiblePointClouds2.length>0 : viewer.scene.pointclouds.some(e=>e.visible) //是否有需要绘制到屏幕的
+         
+        visiblePointClouds2.forEach(e=>{//为了绘制到depthTexture,先显示(展示全景图时隐藏了点云,所以需要显示下。且放大镜需要绘制点云)
+            e.oldVisi = e.visible
+            e.visible = true; 
+        })
+
+        
+         
+        viewer.setCameraLayers(camera, ['pointcloud'])
+        camera.layers.set(Potree.config.renderLayers.pointcloud);
+		
+        //TODO adapt to multiple lights
+		//this.renderShadowMap(visiblePointClouds2, camera, lights);  //???????
+
+		{ 
+			for (let pointcloud of visiblePointClouds2) {
+                
+                let material = pointcloud.material; 
+                let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x;
+                material.fov = THREE.Math.degToRad(camera.fov) 
+                /* material.screenWidth = width;
+                material.screenHeight = height; */
+                material.resolution = resolution 
+               
+                
+                
+                material.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z);
+                material.near = camera.near;
+                material.far = camera.far;
+                material.uniforms.octreeSize.value = octreeSize
+	
+                if(viewer.useEDL && Potree.settings.displayMode != 'showPanos'){ 
+                    /* material.weighted = false;
+                    material.useLogarithmicDepthBuffer = false;  */ 
+                    material.useEDL = true;
+                    //material.fakeEDL = false; //add
+                }else{
+                    material.useEDL = false;
+                    //material.fakeEDL = true; //add 使也输出深度
+                }   
+                
+			}
+            
+            
+            if(Features.EXT_DEPTH.isSupported() && !params.dontRenderRtEDL){  
+                //借用rtEDL存储深度信息  
+                viewer.renderer.setRenderTarget(params.rtEDL || this.getRtEDL(params.viewport)/* this.rtEDL */);
+               
+               
+                if(visiblePointClouds2.length>0){ 
+                    //渲染scenePointCloud到rtEDL
+                    viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, params.rtEDL || this.getRtEDL(params.viewport), {
+                        shadowMaps:  lights.length > 0 ? [this.shadowMap] : null,
+                        clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
+                        transparent: false,
+                    });
+                } 
+                if(Potree.settings.intersectOnObjs){// model也要渲染到rtEDL
+                    camera.layers.set(Potree.config.renderLayers.model);
+                    viewer.objs.traverse(e=>{if(e.material)e._OlddepthWrite = e.material.depthWrite, e.material.depthWrite = true}) //否则半透明的mesh无法遮住测量线
+                    viewer.renderer.render(viewer.scene.scene, camera);
+                    viewer.objs.traverse(e=>{if(e.material)e.material.depthWrite = e._OlddepthWrite})
+                    //缺点:半透明的model 就算完全透明, 也会遮住测量线
+                }
+                //test
+                /* {
+                    viewer.objs.traverse((obj)=>{
+                        if(obj.material){
+                            obj.material = obj.depthMat
+                        }
+                    })
+                    viewer.setCameraLayers(camera, ['sceneObjects'])
+                    viewer.renderer.render(viewer.scene.scene, camera)
+                    viewer.objs.traverse((obj)=>{
+                        if(obj.material){
+                            obj.material = obj.standardMat
+                        }
+                    })
+                } */ 
+            } 
+		}
+        
+        
+        
+         
+        //渲染到rtEDL完毕
+		viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer/* , renderTarget: this.rtRegular */});
+		viewer.renderer.setRenderTarget(params.target || null);
+        
+        if(!params.magnifier)visiblePointClouds2.forEach(e=>{//放大镜显示点云
+            e.visible = e.oldVisi
+        })
+        
+         
+         
+        if(showPointClouds){ //绘制点云到画布
+            if(viewer.useEDL) {  //设置edlMaterial
+                //Features.EXT_DEPTH不支持的话不会到这一块
+                 
+                const uniforms = this.edlMaterial.uniforms;
+                //if(viewer.useEDL){
+                    /* uniforms.screenWidth.value = width;
+                    uniforms.screenHeight.value = height; */
+                    uniforms.resolution.value.copy(resolution)
+                    
+                    uniforms.edlStrength.value = viewer.edlStrength;
+                    uniforms.radius.value = viewer.edlRadius;
+                    uniforms.useEDL.value = 1;//add
+                /* }else{
+                    uniforms.useEDL.value = 0;//add
+                } */
+                
+                let proj = camera.projectionMatrix;
+                let projArray = new Float32Array(16);
+                projArray.set(proj.elements);
+                uniforms.uProj.value = projArray;
+             
+                uniforms.uEDLColor.value = (params.rtEDL || this.getRtEDL(params.viewport)).texture;
+                //uniforms.uEDLDepth.value = (params.rtEDL || this.getRtEDL(params.viewport)).depthTexture; //其实没用到
+                 
+                uniforms.opacity.value = viewer.edlOpacity; // HACK
+                 
+                
+                Utils.screenPass.render(viewer.renderer, this.edlMaterial, params.target);
+            }else{
+                //渲染点云 (直接用rtEDL上的会失去抗锯齿)
+                
+                let prop = {
+                    shadowMaps:  lights.length > 0 ? [this.shadowMap] : null,
+                    clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)) ,
+                    notAdditiveBlending: Potree.settings.notAdditiveBlending//add 否则透明的点云会挡住后面的模型。 加上这句后竟然透明不会叠加了!
+                }
+                 
+                viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, null , prop);  
+            }
+        }        
+          
+		//viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
+        visiblePointClouds2.forEach(e=>{
+            e.visible = e.oldVisi
+        })
+        
+	}
+}
+

+ 233 - 0
src/viewer/ExtendScene.js

@@ -0,0 +1,233 @@
+
+import * as THREE from "../../libs/three.js/build/three.module.js";
+  
+import {Utils} from "../utils.js"; 
+import Axis from '../custom/objects/Axis.js'
+import {Scene} from './Scene.js'
+
+class ExtendScene extends Scene{
+
+	constructor(){
+		super();
+
+		delete this.sceneBG;
+		
+         
+		this.cameraP = new THREE.PerspectiveCamera(this.fov, 1, Potree.config.view.near, Potree.config.view.near);
+		this.cameraO = new THREE.OrthographicCamera(-1, 1, 1, -1, Potree.config.view.near, Potree.settings.cameraFar);
+        this.cameraP.limitFar = true//add
+		this.initializeExtend();
+        
+        //-------------
+        this.axisArrow = new Axis();
+        this.scene.add(this.axisArrow)
+        if(!Potree.settings.isDebug)this.axisArrow.visible = false
+        viewer.setObjectLayers(this.axisArrow,  'bothMapAndScene' )
+        
+        
+        this.tags = new THREE.Object3D;
+        this.scene.add(this.tags)
+
+	}
+
+	estimateHeightAt (position) {
+		let height = null;
+		let fromSpacing = Infinity;
+
+		for (let pointcloud of this.pointclouds) {
+			if (pointcloud.root.geometryNode === undefined) {
+				continue;
+			}
+
+			let pHeight = null;
+			let pFromSpacing = Infinity;
+
+			let lpos = position.clone().sub(pointcloud.position);
+			lpos.z = 0;
+			let ray = new THREE.Ray(lpos, new THREE.Vector3(0, 0, 1));
+
+			let stack = [pointcloud.root];
+			while (stack.length > 0) {
+				let node = stack.pop();
+				let box = node.getBoundingBox();
+
+				let inside = ray.intersectBox(box);
+
+				if (!inside) {
+					continue;
+				}
+
+				let h = node.geometryNode.mean.z +
+					pointcloud.position.z +
+					node.geometryNode.boundingBox.min.z;
+
+				if (node.geometryNode.spacing <= pFromSpacing) {
+					pHeight = h;
+					pFromSpacing = node.geometryNode.spacing;
+				}
+
+				for (let index of Object.keys(node.children)) {
+					let child = node.children[index];
+					if (child.geometryNode) {
+						stack.push(node.children[index]);
+					}
+				}
+			}
+
+			if (height === null || pFromSpacing < fromSpacing) {
+				height = pHeight;
+				fromSpacing = pFromSpacing;
+			}
+		}
+
+		return height;
+	}
+	
+	 
+
+	 
+    //add:
+    removePointCloud (pointcloud) {
+        let index = this.pointclouds.indexOf(pointcloud);
+        if(index == -1)return
+        this.pointclouds.splice(index, 1)
+        this.scenePointCloud.remove(pointcloud);
+      
+        pointcloud.panos.forEach(pano=>{ 
+            pano.dispose()  
+        })
+        
+        
+    }
+    
+	 
+	removeCameraAnimation(animation){
+		let index = this.cameraAnimations.indexOf(animation);
+		if (index > -1) {
+			this.cameraAnimations.splice(index, 1);
+
+			this.dispatchEvent({
+				'type': 'camera_animation_removed',
+				'scene': this,
+				'animation': animation
+			});
+		}
+	};
+
+	 
+	initialize(){//不用旧的 因为还没创建完变量
+	}
+
+    initializeExtend(){//add  新的initialize
+		this.referenceFrame = new THREE.Object3D();
+		this.referenceFrame.matrixAutoUpdate = false;
+		this.scenePointCloud.add(this.referenceFrame);
+
+
+        if(window.axisYup){
+            
+
+        }else{
+            this.cameraP.up.set(0, 0, 1);
+            this.cameraO.up.set(0, 0, 1);
+            
+
+        }
+        this.cameraP.position.set(1000, 1000, 1000);
+        this.cameraO.position.set(1000, 1000, 1000);
+
+		//this.camera.rotation.y = -Math.PI / 4;
+		//this.camera.rotation.x = -Math.PI / 6;
+		this.cameraScreenSpace.lookAt(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1), new THREE.Vector3(0, 1, 0));
+		
+		this.directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
+		this.directionalLight.position.set( 10, 10, 10 );
+		this.directionalLight.lookAt( new THREE.Vector3(0, 0, 0));
+		this.scenePointCloud.add( this.directionalLight );
+		
+		let light = new THREE.AmbientLight( 0x555555 ); // soft white light 
+		this.scenePointCloud.add( light );
+        
+         
+        
+        //add:------给空间模型的box 或其他obj------
+        let light2 = new THREE.AmbientLight( 16777215, 1 );
+        viewer.setObjectLayers(light2, 'bothMapAndScene')
+        this.scene.add(light2)
+        let light3 = new THREE.DirectionalLight( 16777215, 1);  
+        light3.position.set( 10, 10, 10 );
+		light3.lookAt( new THREE.Vector3(0, 0, 0));
+        viewer.setObjectLayers(light3, 'bothMapAndScene')
+        this.scene.add(light3)  
+        //--------------------------------------------
+
+		{ // background
+			let texture = Utils.createBackgroundTexture(512, 512);
+
+			texture.minFilter = texture.magFilter = THREE.NearestFilter;
+			texture.minFilter = texture.magFilter = THREE.LinearFilter;
+			let bg = new THREE.Mesh(
+				new THREE.PlaneBufferGeometry(2, 2, 1),
+				new THREE.MeshBasicMaterial({
+					map: texture
+				})
+			);
+			bg.material.depthTest = false;
+			bg.material.depthWrite = false;
+            bg.name = 'bg'
+			//this.sceneBG.add(bg);
+            this.scene.add(bg)
+            bg.layers.set(Potree.config.renderLayers.bg)
+            
+		}
+
+        { // background color
+			  
+			let bg2 = new THREE.Mesh(
+				new THREE.PlaneBufferGeometry(2, 2, 1),
+				new THREE.MeshBasicMaterial({
+					transparent : true
+				})
+			);
+			bg2.material.depthTest = false;
+			bg2.material.depthWrite = false;
+            bg2.name = 'bg2' 
+            this.scene.add(bg2)
+            bg2.layers.set(Potree.config.renderLayers.bg2) 
+            this.bg2 = bg2
+		}
+
+
+		// { // lights
+		// 	{
+		// 		let light = new THREE.DirectionalLight(0xffffff);
+		// 		light.position.set(10, 10, 1);
+		// 		light.target.position.set(0, 0, 0);
+		// 		this.scene.add(light);
+		// 	}
+
+		// 	{
+		// 		let light = new THREE.DirectionalLight(0xffffff);
+		// 		light.position.set(-10, 10, 1);
+		// 		light.target.position.set(0, 0, 0);
+		// 		this.scene.add(light);
+		// 	}
+
+		// 	{
+		// 		let light = new THREE.DirectionalLight(0xffffff);
+		// 		light.position.set(0, -10, 20);
+		// 		light.target.position.set(0, 0, 0);
+		// 		this.scene.add(light);
+		// 	}
+		// }
+	} 
+     
+};
+
+
+
+Object.assign( ExtendScene.prototype, THREE.EventDispatcher.prototype );
+
+
+
+export {ExtendScene}

+ 330 - 0
src/viewer/ExtendView.js

@@ -0,0 +1,330 @@
+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
+        
+
+	}
+    //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 () {  
+        return Common.CloneClassObject(this)
+	}
+    
+    //----------  
+    
+    
+    
+ 
+ 
+
+	 
+	/* 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) {
+		super.translateWorld(x, y, 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 */); 
+
+    
+    
+        
+        
+    }
+    
+     
+    
+
+};
+
+Object.assign(ExtendView.prototype, THREE.EventDispatcher.prototype)
+
+export {ExtendView}

+ 21 - 48
src/viewer/HQSplatRenderer.js

@@ -4,15 +4,12 @@ import {NormalizationMaterial} from "../materials/NormalizationMaterial.js";
 import {NormalizationEDLMaterial} from "../materials/NormalizationEDLMaterial.js";
 import {PointCloudMaterial} from "../materials/PointCloudMaterial.js";
 import {PointShape} from "../defines.js";
-import {SphereVolume} from "../objects/tool/Volume.js";
+import {SphereVolume} from "../utils/Volume.js";
 import {Utils} from "../utils.js";
 
 
 export class HQSplatRenderer{
 	
-    
-  //rtAttribute  估计需要给magnifer复制一份 
-    
 	constructor(viewer){
 		this.viewer = viewer;
 
@@ -25,8 +22,6 @@ export class HQSplatRenderer{
 		this.gl = viewer.renderer.getContext();
 
 		this.initialized = false;
-        
-        viewer.addEventListener('resize',this.resize.bind(this))
 	}
 
 	init(){
@@ -63,22 +58,17 @@ export class HQSplatRenderer{
 		this.initialized = true;
 	};
 
-	resize(e/* width, height */){
-		this.rtDepth.setSize(e.canvasWidth, e.canvasHeight);
-		this.rtAttribute.setSize(e.canvasWidth, e.canvasHeight);
+	resize(width, height){
+		this.rtDepth.setSize(width, height);
+		this.rtAttribute.setSize(width, height);
 	}
 
-	clearTargets(params){
+	clearTargets(){
 		const viewer = this.viewer;
 		const {renderer} = viewer;
 
 		const oldTarget = renderer.getRenderTarget();
-        
-        
-        if(params.target){//add
-            renderer.setRenderTarget( params.target);
-            renderer.clear() 
-        }
+
 		renderer.setClearColor(0x000000, 0);
 
 		renderer.setRenderTarget( this.rtDepth );
@@ -91,7 +81,7 @@ export class HQSplatRenderer{
 	}
 
 
-	clear(params={}){
+	clear(){
 		this.init();
 
 		const {renderer, background} = this.viewer;
@@ -108,23 +98,21 @@ export class HQSplatRenderer{
 			renderer.setClearColor(0x000000, 0);
 		}
 
-		params.target || renderer.clear();
+		renderer.clear();
 
-		this.clearTargets(params);
+		this.clearTargets();
 	}
 
-	render(params={}) {
+	render (params) {
 		this.init();
 
 		const viewer = this.viewer;
-		const camera = params.camera ? params.camera : viewer.scene.getActiveCamera(); 
-		const {width, height} = params.width ? params : this.viewer.renderer.getSize(new THREE.Vector2());
-
-        viewer.renderer.setRenderTarget(params.target||null);
+		const camera = params.camera ? params.camera : viewer.scene.getActiveCamera();
+		const {width, height} = this.viewer.renderer.getSize(new THREE.Vector2());
 
 		viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
 
-		//params.target || this.resize(width, height);
+		this.resize(width, height);
 
 		const visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible);
 		const originalMaterials = new Map();
@@ -184,7 +172,7 @@ export class HQSplatRenderer{
 				pointcloud.material = depthMaterial;
 			}
 			
-			viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, (params.rtEDL || this.rtDepth), {
+			viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtDepth, {
 				clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
 			});
 		}
@@ -253,7 +241,7 @@ export class HQSplatRenderer{
 			
 			let gl = this.gl;
 
-			//viewer.renderer.setRenderTarget(null);
+			viewer.renderer.setRenderTarget(null);
 			viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtAttribute, {
 				clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
 				//material: this.attributeMaterial,
@@ -267,13 +255,7 @@ export class HQSplatRenderer{
 			pointcloud.material = material;
 		}
 
-
-        
-
-		
-        
-         
-        
+		viewer.renderer.setRenderTarget(null);
 		if(viewer.background === "skybox"){
 			viewer.renderer.setClearColor(0x000000, 0);
 			viewer.renderer.clear();
@@ -301,9 +283,6 @@ export class HQSplatRenderer{
 			viewer.renderer.clear();
 		}
 
-
-          
-
 		{ // NORMALIZATION PASS
 			let normalizationMaterial = this.useEDL ? this.normalizationEDLMaterial : this.normalizationMaterial;
 
@@ -312,7 +291,7 @@ export class HQSplatRenderer{
 				normalizationMaterial.uniforms.radius.value = viewer.edlRadius;
 				normalizationMaterial.uniforms.screenWidth.value = width;
 				normalizationMaterial.uniforms.screenHeight.value = height;
-				normalizationMaterial.uniforms.uEDLMap.value = (params.rtEDL || this.rtDepth).texture;
+				normalizationMaterial.uniforms.uEDLMap.value = this.rtDepth.texture;
 			}
 
 			normalizationMaterial.uniforms.uWeightMap.value = this.rtAttribute.texture;
@@ -325,18 +304,12 @@ export class HQSplatRenderer{
 
 		viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer});
 
-        viewer.renderer.render(viewer.scene.sceneOverlay, camera);// add   透明贴图层
-
-
 		viewer.renderer.clearDepth();
 
 		viewer.transformationTool.update();
-        if(!params.target){
-            
-            //测量线
-            viewer.dispatchEvent({type: "render.pass.perspective_overlay",viewer: viewer, camera});
-            viewer.renderer.render(viewer.overlay, camera);//从 viewer.renderDefault搬过来,为了reticule不遮住测量线
-        }
+
+		viewer.dispatchEvent({type: "render.pass.perspective_overlay",viewer: viewer});
+
 		viewer.renderer.render(viewer.controls.sceneControls, camera);
 		viewer.renderer.render(viewer.clippingTool.sceneVolume, camera);
 		viewer.renderer.render(viewer.transformationTool.scene, camera);
@@ -348,7 +321,7 @@ export class HQSplatRenderer{
 		viewer.renderer.setViewport(0, 0, width, height);
 		
 		viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
-        viewer.renderer.setRenderTarget(null) 
+
 	}
 
 }

+ 3 - 3
src/viewer/PropertyPanels/DistancePanel.js

@@ -1,7 +1,7 @@
 
 
 import {MeasurePanel} from "./MeasurePanel.js";
-import {Profile} from "./../../objects/tool/Profile.js";
+import {Profile} from "./../../utils/Profile.js";
 
 export class DistancePanel extends MeasurePanel{
 	constructor(viewer, measurement, propertiesPanel){
@@ -56,9 +56,9 @@ export class DistancePanel extends MeasurePanel{
 	update(){
 		let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
 		elCoordiantesContainer.empty();
-		elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points));
+		elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points.map(p => p.position)));
 
-		let positions = this.measurement.points;
+		let positions = this.measurement.points.map(p => p.position);
 		let distances = [];
 		for (let i = 0; i < positions.length - 1; i++) {
 			let d = positions[i].distanceTo(positions[i + 1]);

+ 2 - 2
src/viewer/Scene.js

@@ -2,7 +2,7 @@
 import * as THREE from "../../libs/three.js/build/three.module.js";
 import {Annotation} from "../Annotation.js";
 import {CameraMode} from "../defines.js";
-import {View} from "./View.js";
+import {ExtendView} from "./ExtendView.js";
 import {Utils} from "../utils.js";
 import {EventDispatcher} from "../EventDispatcher.js";
 
@@ -43,7 +43,7 @@ export class Scene extends EventDispatcher{
 		this.deviceControls = null;
 		this.inputHandler = null;
 
-		this.view = new View();
+		this.view = new ExtendView();
 
 		this.directionalLight = null;
 

+ 80 - 285
src/viewer/View.js

@@ -1,78 +1,28 @@
 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' 
-import { EventDispatcher } from "../EventDispatcher.js";
-
-let sid = 0
- 
-export class View extends EventDispatcher{
+
+export class View{
 	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.yaw = Math.PI / 4;
+		this._pitch = -Math.PI / 4;
 		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();
+		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)
+		return c;
 	}
-    
-    //----------  
-    
-    
-    
+
 	get pitch () {
 		return this._pitch;
 	}
@@ -105,7 +55,7 @@ export class View extends EventDispatcher{
 		
 	}
 
-	lookAt(t){//setPivot
+	lookAt(t){
 		let V;
 		if(arguments.length === 1){
 			V = new THREE.Vector3().subVectors(t, this.position);
@@ -131,7 +81,7 @@ export class View extends EventDispatcher{
 		return side;
 	}
 
-	/* pan (x, y) {
+	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);
@@ -147,250 +97,95 @@ export class View extends EventDispatcher{
 
 		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 ) {
-        //相机方向
+
+	translate (x, y, z) {
 		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);//水平角度
+		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);  //垂直于相机当前水平朝向的 左右方向
+		side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
+
+		let up = side.clone().cross(dir);
 
-		let up = side.clone().cross(dir); //垂直于相机当前水平朝向的 向上方向
+		let t = side.multiplyScalar(x)
+			.add(dir.multiplyScalar(y))
+			.add(up.multiplyScalar(z));
 
-		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()
 	}
 
+	setView(position, target, duration = 0, callback = null){
 
-    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;
+		let endPosition = null;
+		if(position instanceof Array){
+			endPosition = new THREE.Vector3(...position);
+		}else if(position.x != null){
+			endPosition = position.clone();
 		}
-	}
-    
-    
-    
-    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){
+
+		let endTarget = null;
+		if(target instanceof Array){
+			endTarget = new THREE.Vector3(...target);
+		}else if(target.x != null){
+			endTarget = target.clone();
+		}
+		
+		const startPosition = this.position.clone();
+		const startTarget = this.getPivot();
+
+		//const endPosition = position.clone();
+		//const endTarget = target.clone();
+
+		let easing = TWEEN.Easing.Quartic.Out;
+
+		if(duration === 0){
 			this.position.copy(endPosition);
-            this.restrictPos()
-			
-            info.onUpdate && info.onUpdate(1)
-            done()
-            
+			this.lookAt(endTarget);
 		}else{
+			let value = {x: 0};
+			let tween = new TWEEN.Tween(value).to({x: 1}, duration);
+			tween.easing(easing);
+			//this.tweens.push(tween);
+
+			tween.onUpdate(() => {
+				let t = value.x;
+
+				//console.log(t);
+
+				const pos = new THREE.Vector3(
+					(1 - t) * startPosition.x + t * endPosition.x,
+					(1 - t) * startPosition.y + t * endPosition.y,
+					(1 - t) * startPosition.z + t * endPosition.z,
+				);
+
+				const target = new THREE.Vector3(
+					(1 - t) * startTarget.x + t * endTarget.x,
+					(1 - t) * startTarget.y + t * endTarget.y,
+					(1 - t) * startTarget.z + t * endTarget.z,
+				);
 
-            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 */); 
-
-    
-    
-        
-        
-    }
-    
-     
-    
+				this.position.copy(pos);
+				this.lookAt(target);
+
+			});
+
+			tween.start();
+
+			tween.onComplete(() => {
+				if(callback){
+					callback();
+				}
+			});
+		}
+
+	}
 
 };

+ 0 - 778
src/viewer/map/Map.js

@@ -1,778 +0,0 @@
-import * as THREE from "../../../libs/three.js/build/three.module.js";
-import { ExtendEventDispatcher } from "../../custom/ExtendEventDispatcher.js";
-
-
-
-let texLoader = new THREE.TextureLoader() 
-    texLoader.crossOrigin = "anonymous" 
-
-
-let createErrorMaterial = function() {
-   var t = new THREE.MeshBasicMaterial({
-       transparent: !0,
-       depthWrite: !1,
-       depthTest: !0,
-       opacity: 1,
-       side: THREE.DoubleSide
-   });
-   return t.color = new THREE.Color(3355443),
-   t
-}
-let tempVector = new THREE.Vector3,  //sharedata
-    face1 = new THREE.Face3(0,1,2),
-    face2 = new THREE.Face3(2,3,0),
-    errorMaterial = createErrorMaterial(),
-    uv00 = new THREE.Vector2(0,0),
-    uv01 = new THREE.Vector2(0,1),
-    uv10 = new THREE.Vector2(1,0),
-    uv11 = new THREE.Vector2(1,1),
-    face1UV = [uv00, uv10, uv11],
-    face2UV = [uv11, uv01, uv00]
-
-
-
-const HALF_WORLD_SIZE = 21e6 
-const MAX_VERTICAL_DIST = 2 
-const MAX_VERTICAL_DIST_TO_BEST = 1  
- 
-
-
-
-
-//高德坐标拾取工具 : https://lbs.amap.com/tools/picker
-
-
-export class MapLayer extends ExtendEventDispatcher{ // 包括了 MapLayerBase SceneLayer
-    constructor(viewer_, viewport){
-        super()
-        this.sceneGroup = new THREE.Object3D;
-        this.sceneGroup.name = "MapLayer" 
-         
-        
-        this.loadingInProgress = 0
-        this.maps = [] 
-        this.frustum = new THREE.Frustum 
-        this.frustumMatrix = new THREE.Matrix4 
-        this.tileColor = /* i && i.tileColor ? i.tileColor :  */new THREE.Color(16777215)
-        this.viewport = viewport
-        this.changeViewer(viewer_) 
-        //添加地图
-        var map = new TiledMapOpenStreetMap(this, this.tileColor )
-        this.addMap(map) 
-        
-
-        //map.setEnable(false)
-        
-        
-        
-        
-        /* this.on('needUpdate',()=>{ 
-            this.mapLayer.update()
-        }) 
-          */
-    }
-    
-    addMapEntity(data, datasetId){
-         
-        if(!data || !data[0]){ 
-            Potree.Log('平面图无数据','red')
-            return
-        }
-        
-        var floorplan = new TiledMapFromEntity(this, this.tileColor, data[0]    )//[0]?
-
-        if(floorplan){
-            floorplan.name += "_"+ datasetId  
-
-            this.addMap(floorplan)
-            floorplan.updateProjection()
-            floorplan.updateObjectGroup()
-             
-            let visible = false
-
-            if(datasetId in Potree.settings.floorplanEnables){
-                visible = Potree.settings.floorplanEnables[datasetId]
-            }else{
-                visible = Potree.settings.floorplanEnable
-            }
-
-            if(visible){
-                this.needUpdate = true
-            }else{
-                floorplan.setEnable(false)
-            }
-            
-            this.dispatchEvent({type:'floorplanLoaded', floorplan})
-        }
-        return floorplan
-    }
-    
-    
-    getFloorplan(datasetId){
-        return this.maps.find(e=>e.name == 'floorplan'+"_"+ datasetId  )
-    }
-    
-    
-    addMap(t){ 
-        this.maps.push(t)
-        //this.view.invalidateScene()
-        this.needUpdate = true
-    }
-     
-    removeMap(t){
-        var e = this.maps.indexOf(t);
-        if(e >= 0){
-            t.removeFromSceneGroup(this.sceneGroup) 
-            this.maps.splice(e, 1) 
-        }
-         
-        /* this.view.invalidateScene() */ 
-        this.needUpdate = true
-        this.viewer.dispatchEvent({
-            type:'content_changed'
-        }) 
-    }
-    
-     
-    
-    
-    changeViewer(viewer_){//add 
-        this.viewer = viewer_ 
-    }
-    
-    initProjection(){
-        this.maps.forEach(map=>{
-            map.updateProjection()
-            map.updateObjectGroup() 
-        })
-    }
-    
-    visibilityChanged(){
-        if (!this.visible)
-            for (var t = 0, e = this.maps; t < e.length; t++){
-                e[t].removeFromSceneGroup(this.sceneGroup)
-            }
-    }
-    
-    onAfterRenderViewport(e){
-        var n = this;
-        
-        /* this.isVisibleInViewport(e) && (this.updateTimer || this.loadingInProgress || (this.updateTimer = window.setTimeout((function(){
-            n.update(e).then((function  (t){
-                t && n.loadComplete.emit(!0)
-            }
-            )).catch(u.handleWarning)
-        }
-        ), 100))) */
-    }
-     
-     
-     
-    update(){
-        this.needUpdate = false
-        if(this.disabled || !this.maps.find(e=>!e.disabled) || !this.maps.find(e=>e.objectGroup.visible)   )return  //add
-        
-          
-        var e, n, i, r, o;
-        
-        this.updateTimer = void 0,
-        e = this.viewport.camera,
-        n = e.projectionMatrix.clone(),
-        
-        n.elements[0] /= 1.5,
-        n.elements[5] /= 1.5,
-        this.frustumMatrix.multiplyMatrices(n, e.matrixWorldInverse),
-        this.frustum.setFromProjectionMatrix(this.frustumMatrix),
-        this.frustum.planes[4].setComponents(0, 0, 0, 0),
-        this.frustum.planes[5].setComponents(0, 0, 0, 0),
-        i = !0 
-        
-       
-        for (r = 0; r < this.maps.length; r++){
-            var map = this.maps[r] 
-            i = map.update(this.frustum, this.sceneGroup) && i;
-        }
-            
-        return [2, i]
-        
-                 
-    }
-     
-    getAttributions(){
-        for (var t = {}, e = 0, n = this.maps; e < n.length; e++){
-            n[e].fillAttributions(t)
-        }
-        return t
-    }
-     
-    updateProjection(){
-        for (var t = 0, e = this.maps; t < e.length; t++){
-            var n = e[t];
-            n.clearProjection(),
-            n.updateObjectGroup()
-        }
-    }
-}
- 
-    
-    
-    
-    
-    
-    
-    
-    
-   
- 
-
-export class TiledMapBase extends THREE.EventDispatcher{
-    constructor(/* t,  */name, mapLayer, tileColor, projection){
-        super();
-        this.name = name
-        //this.TransformService = t,
-        this.mapLayer = mapLayer,
-        this.tileColor = tileColor,
-        this.bias = 0 
-        this.zIndex = -1  
-        this.objectGroup = new THREE.Object3D;
-        this.objectGroup.name = name
-        this.objectGroupAdded = !1,
-        this.baseTile = new MapTile(this,/*   this.mapLayer, */this.objectGroup,this.tileColor),
-        this.isTileVisibleBox = new THREE.Box3,
-        this.isTileVisibleVec = new THREE.Vector3
-        
-        
-        this.projection = projection
-        this._zoomLevel = 0;//1-20
-        
-    }
-    
-    get zoomLevel(){
-        return this._zoomLevel
-    }
-    set zoomLevel(zoomLevel){
-        if(this._zoomLevel != zoomLevel){
-            this._zoomLevel = zoomLevel
-            //this.dispatchEvent('zoomLevelChange',zoomLevel)
-             
-            //if(this.name == 'map')console.log(zoomLevel,viewer.mapViewer.camera.zoom)
-        }
-    }
-    
-    
-    updateObjectGroup(){
-        this.position && this.objectGroup.position.copy(this.position),
-        this.quaternion && this.objectGroup.quaternion.copy(this.quaternion),
-        this.objectGroup.updateMatrixWorld(!0)
-    }
-    
-    updateProjection(){
-        //this.transformMapToLocal || (this.transformMapToLocal = this.TransformService.getTransform(this.projection, this.TransformService.crsLocal))
-        if(!this.transformMapToLocal){
-            if(proj4.defs("NAVVIS:TMERC")){
-                if(this.projection == "EPSG:4550"){
-                    this.transformMapToLocal = {
-                            forward:(e)=>{
-                                var a = viewer.transform.lonlatTo4550.inverse(e)
-                                return viewer.transform.lonlatToLocal.forward(a)
-                            },
-                        }
-                        
-                   
-                }else{
-                    this.transformMapToLocal = proj4(this.projection, "NAVVIS:TMERC") 
-                }
-                    //this.transformMapToLocal = proj4(this.projection, "NAVVIS:TMERC") 
-             
-               
-            }
-        } 
-    
-    } 
-    
-    setEnable(enable){//add
-        if(!this.disabled == enable)return
-        if(enable){
-            console.log('setEnable',true)
-        }
-        this.disabled = !enable
-     
-        viewer.updateVisible(this.objectGroup, 'setEnable', enable)
-        
-        if(!enable){
-            this.baseTile.remove()
-        }else{
-            this.mapLayer.needUpdate = true
-        }
-         
-        this.mapLayer.viewer.dispatchEvent({
-            type:'content_changed'
-        })
-        
-        
-    }
-    
-    
-    
-    /* clearProjection(){
-        this.transformMapToLocal = void 0,
-        this.projection.name !== this.TransformService.NAVVIS_LOCAL && this.baseTile.remove()
-    } */
-    
-    update(e, n){
-        
-        var unavailable = (this.disabled || !this.objectGroup.visible)//地图即使不显示也要获得zoomlevel
-        if(this.name != 'map' && unavailable)return 
-        
-        this.updateProjection()
-        
-        if(!this.transformMapToLocal)return
-        
-        if (!this.isTileVisible(new THREE.Vector3(0,0,0), this.mapSizeM, e))
-            return this.removeFromSceneGroup(n), !0;
-        
-        let viewport = this.mapLayer.viewport
-        
-        
-        var i = new THREE.Vector3(-.5 * this.mapSizeM,0,0);
-        i.applyMatrix4(this.objectGroup.matrixWorld),
-        i.project(viewport.camera);
-        var o = new THREE.Vector3(.5 * this.mapSizeM,0,0);
-        o.applyMatrix4(this.objectGroup.matrixWorld),
-        o.project(viewport.camera);
-        var a = viewport.resolution.x 
-          , s = viewport.resolution.y 
-        if (a <= 0 || s <= 0 || isNaN(i.x) || isNaN(o.x))  return !1;
-        i.sub(o),
-        i.x *= a / 2,
-        i.y *= s / 2;
-        
-        var c = this.tileSizePx / i.length()
-          , level = Math.ceil(-Math.log(c) / Math.log(2) - this.bias);
-        level = Math.max(level, 0) 
-        level = Math.min(level, void 0 === this.maxDepth ? 1 / 0 : this.maxDepth) 
-        this.zoomLevel = level//add
-        //console.log(level)
-        if(!unavailable){
-            this.addToSceneGroup(n) 
-            return this.baseTile.update(this, e, level, this.mapSizeM, 0, 0, "")
-        }
-    }
-     
-     
-     
-     
-    isTileVisible(e, n, i){
-        if (n > HALF_WORLD_SIZE) return !0;
-        var r = .5 * n;
-        this.transformMapToLocal.forward(e) 
-        this.isTileVisibleBox.makeEmpty() 
-        this.isTileVisibleVec.set(e.x - r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld)
-        this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec)
-        this.isTileVisibleVec.set(e.x - r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld)
-        this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec)
-        this.isTileVisibleVec.set(e.x + r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld)
-        this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec)
-        this.isTileVisibleVec.set(e.x + r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld)
-        this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec)
-        return i.intersectsBox(this.isTileVisibleBox)
-    }
-     
-    addToSceneGroup(t){
-        this.objectGroupAdded || (t.add(this.objectGroup),
-        this.objectGroupAdded = !0)
-    }
-     
-    removeFromSceneGroup(t){
-        this.baseTile.remove(),
-        this.objectGroupAdded && (t.remove(this.objectGroup),
-        this.objectGroupAdded = !1)
-    }
-    
-    
-}
-
-export class MapTile{
-    constructor(map,/*  t, */ e, n){
-        this.map = map;
-        //this.mapLayer = t,
-        this.objectGroup = e,
-        this.tileColor = n,
-        this.meshAdded = !1,
-        this.textureLoaded = !1,
-        this.children = []
-    }
-    update(e, n, i, r, o, a, s){
-        return !!this.doesNotContainTilesToBeDisplayed(e) || (0 === i ? this.updateTile(e, r, o, a) : this.updateSubTiles(e, n, i, r, o, a, s))
-    }
-    
-    doesNotContainTilesToBeDisplayed(t){
-        return t.tilePresenceMap && t.tilePresenceMap.empty
-    }
-    
-    updateTile(t, e, n, i){ 
-        if(!this.mesh){
-            this.createTileObject(t, e, n, i)
-        }
-        if(!this.meshAdded){
-            this.objectGroup.add(this.mesh) 
-            this.meshAdded = !0
-        }
-        if(this.textureLoaded){
-            this.removeChildren() 
-        }
-        
-        return this.textureLoaded
-    }
-    
-    updateSubTiles(entity, n, level, o, a, s, c){
-        for (var l = !0, u = [-.25 * o, .25 * o, -.25 * o, .25 * o], d = [.25 * o, .25 * o, -.25 * o, -.25 * o], p = 0; p < 4; ++p){
-            var h = c + p.toString(10);
-            //一级(512):0 1 2 3分别为左上、右上、左下、右下。二级(1024)就是把一级的每一块分裂,如00 01 02 03分别是0的左上、右上、左下、右下……
-            /* if(entity.name == 'floorplan'){
-                console.log(1)
-            } */
-            
-            
-            if (!entity.tilePresenceMap || entity.tilePresenceMap[h]){
-                //去掉判断,直接显示
-                var f = a + u[p]
-                  , m = s + d[p];
-                tempVector.set(f, m, 0);
-                if (entity.isTileVisible(tempVector, .5 * o, n)){
-                     
-                    this.children[p] || (this.children[p] = new MapTile(this.map, this.objectGroup,this.tileColor))
-                    l = this.children[p].update(entity, n, level - 1, .5 * o, f, m, h) && l
-                } else {
-                    if (this.children[p]){
-                        this.children[p].remove()
-                        delete this.children[p]
-                    }
-
-                }
-                 
-            }
-        }
-        return l && this.removeObject3D(),
-        l
-
-    }
-    
-    createTileObject(t, e, n, a){
-        var s = this;
-        this.mesh = this.createMesh(t.transformMapToLocal, e, n, a),
-        this.textureLoaded = !1;
-        var c = t.mapSizeM / e
-          , l = Math.log(c) / Math.log(2)
-          , u = n / e + .5 * (c - 1)
-          , d = -a / e + .5 * (c - 1)
-          , p = t.getTileUrl(Math.round(l), Math.round(u), Math.round(d));
-        viewer.setObjectLayers(this.mesh, 'map' )
-        this.mesh.renderOrder = -(1e6 - l - 100 * (t.zIndex || 0));
-        var h = this.mesh.material;
-        
-        
-        var loadDone = ()=>{
-            this.map.mapLayer.loadingInProgress--
-            if(this.map.mapLayer.loadingInProgress == 0){
-                this.map.mapLayer.dispatchEvent('loadDone') 
-            }
-        }
-        
-        h.map = texLoader.load(p, (tex)=>{
-            if(this.mesh){//如果还要显示的话
-                this.textureLoaded = true
-                this.mesh.material.opacity = 1
-                //this.mapLayer.view.invalidateScene()
-                
-                this.map.mapLayer.viewer.dispatchEvent({
-                    type:'content_changed'
-                })
-                this.map.mapLayer.needUpdate = true //表示还要继续update(以removeChildren)
-            }else{
-                tex.dispose()   
-            } 
-            loadDone()
-        } , void 0, (()=>{//error
-            this.textureLoaded = !0
-            if(this.mesh){ 
-                this.mesh.material.dispose() //o.disposeMeshMaterial(this.mesh) 
-                this.mesh.material =  errorMaterial
-                //this.map.mapLayer.view.invalidateScene())
-                this.map.mapLayer.viewer.dispatchEvent({
-                    type:'content_changed'
-                }) 
-            } 
-            loadDone()
-        })) 
-        
-        h.map.anisotropy = 0,
-        h.map.generateMipmaps = !1,
-        h.map.minFilter = THREE.LinearFilter,
-        h.map.magFilter = THREE.LinearFilter,
-        this.map.mapLayer.loadingInProgress++
-    }
-    
-    createMesh(t, e, n, o){
-        var a = new THREE.Geometry;
-        return tempVector.set(n - e / 2, o - e / 2, 0),
-        a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))),
-        tempVector.set(n + e / 2, o - e / 2, 0),
-        a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))),
-        tempVector.set(n + e / 2, o + e / 2, 0),
-        a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))),
-        tempVector.set(n - e / 2, o + e / 2, 0),
-        a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))),
-        a.faces.push(face1),
-        a.faces.push(face2),
-        a.faceVertexUvs[0].push(face1UV),
-        a.faceVertexUvs[0].push(face2UV),
-        new THREE.Mesh(a,this.createMaterial())
-    }
-    
-    createMaterial(){
-        var t = new THREE.MeshBasicMaterial({
-            transparent: !0,
-            depthWrite: !1,
-            depthTest: !0,
-            opacity: 0,
-            side: THREE.DoubleSide
-        });
-        return t.color = this.tileColor ? this.tileColor : new THREE.Color(16777215),
-        t
-    }
-    
-    remove(){
-        this.removeObject3D(),
-        this.removeChildren()
-    }
-    
-    removeObject3D(){
-        if (this.mesh){
-            if (this.objectGroup.remove(this.mesh),
-            this.textureLoaded){
-                var t = this.mesh.material.map;
-                t && t.dispose()
-            } 
-            this.mesh.material.dispose() //o.disposeMeshMaterial(this.mesh),
-            this.mesh.geometry.dispose() 
-            this.mesh = void 0
-        }
-        this.meshAdded = !1,
-        this.textureLoaded = !1
-        
-    }
-    
-    removeChildren(){
-        for (var t = 0, e = this.children; t < e.length; t++){
-            var n = e[t];
-            n && (n.removeObject3D(),
-            n.removeChildren())
-        }
-        this.children.length = 0
-    }
-  
-}
-
-
-
-
-proj4.defs("EPSG:3857", "+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs")
-//这里地图世界的中心是不是lon:0,lat:0     
-
-export class TiledMapOpenStreetMap extends TiledMapBase{
-    constructor(mapLayer, tileColor){
-        super('map', mapLayer, tileColor, /* "EPSG:4550" */  "EPSG:3857"  ) //EPSG projection
-        //this.baseUrl = "https://wprd03.is.autonavi.com/appmaptile?style=7&x=${x}&y=${y}&z=${z}",
-        //this.baseUrl = "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x=${x}&y=${y}&z=${z}" //最高只到18 level
-        this.baseUrl = "https://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&style=7&x=${x}&y=${y}&z=${z}"    //最高只到19
-        
-        this.attribution = "© PopSmart,  © 高德地图",
-        this.tileSizePx = 256
-        this.mapSizeM = 40075017
-        this.maxDepth = 19//20
-        this.bias = 0.5 
-         
-  
-            
-    }
-    
-    getTileUrl(t, e, n){ 
-        return this.baseUrl.replace(/\${z}/, t.toString(10)).replace(/\${x}/, e.toString(10)).replace(/\${y}/, n.toString(10))
-    }
-     
-    fillAttributions(t){
-        t[this.attribution] = {
-            score: 50
-        }
-    }
-    
-    
-    
-}
-
-
-
-
-export class TiledMapFromEntity extends TiledMapBase{
-    constructor(mapLayer, tileColor, data){ 
-        super('floorplan', mapLayer, tileColor,  "NAVVIS:TMERC"   /* "EPSG:3857"  *//* "WGS84" */)  //直接就是本地坐标,没有projec 
-        
-         
-        let entity = this.tiledMapEntity = this.fillFromData(data)  
-        let time = entity.updateTime || entity.createTime  
-          
-        this.tileSizePx = entity.tileSizePx,
-        this.mapSizeM = entity.mapSizeM,
-        this.maxDepth = entity.maxDepth;
-        this.postStamp = time ? time.replace(/[^0-9]/ig,'') : (new Date).getTime()   
-        //this.projection = n.crsLocal,
-        this.zIndex = 0, 
-        this.tilePresenceMap = this.decodeBitStream(this.tiledMapEntity.quadtree) //包含tile分裂信息,如果写错了会造成tile显示不全
-         
-    }
-  
-    fillFromData(e){ 
-        let data = {}
-        
-        data.id = e.id
-        data.globalLocation = Potree.Utils.VectorFactory.fromArray3(e.location),
-        data.orientation = Potree.Utils.QuaternionFactory.fromArray(e.orientation) 
-        //if(Potree.fileServer){
-            data.filePath = `${Potree.settings.urls.prefix1}${e.file_path}`
-        //}else{
-        //    data.filePath = `${Potree.settings.urls.prefix}/data/${Potree.settings.number}/${e.file_path}`
-        //}
-         
-        //if(!data.filePath.includes('building_1'))data.filePath = data.filePath.replace('building','building_1')//暂时
-        data.fileName = '$DEPTH/$X/$Y.png' //e.file_name,
-        data.type = e.type,
-        data.mapSizeM = e.map_size_m,
-        data.tileSizePx = e.tile_size_px,
-        data.maxDepth = e.max_depth,
-        data.quadtree = e.quadtree,
-        data.floorId = e.floor_id,
-        data.bundleId = e.bundle_id  
-        //this.computeLocalCoordinates()
-        return data
-        
-    }
-    
-    
-    
-    computeLocalCoordinates(){
-        if(proj4.defs("NAVVIS:TMERC")){
-            this.tiledMapEntity.location = new THREE.Vector3().copy(viewer.transform.lonlatToLocal.forward(this.tiledMapEntity.globalLocation))
-        } 
-    }
-     
-    updateProjection() {
-        super.updateProjection()
-        if(!this.position){
-            this.computeLocalCoordinates()
-        } 
-        /* this.projection = this.TransformService.crsLocal,
-        t.prototype.updateProjection.call(this) */
-    } 
-    
-    
-    get position(){
-        return this.tiledMapEntity.location
-       /* enumerable: !0,
-       configurable: !0 */
-    }
-    get quaternion(){
-        return this.tiledMapEntity.orientation
-        /* enumerable: !0,
-        configurable: !0 */
-    }
-   
-    getTileUrl(t, e, n) {
-        var i = (this.tiledMapEntity.filePath + "/" + this.tiledMapEntity.fileName).replace(/\$DEPTH/g, t.toString(10)).replace(/\$X/g, e.toString(10)).replace(/\$Y/g, n.toString(10));
-        return i += "?t=" + this.postStamp  
-        //this.RestService.addAuthorizationQueryParameter(i) //????
-    }
-    
-    fillAttributions(t) {
-        t.NavVis = {
-            score: 100
-        }
-    }
-   
-    
-    
-    
-    decodeBitStream(t) {
-        if (!t)
-            return {
-                empty: !0
-            };
-        for (var e = {}, n = [e], i = 0; i < t.length; i++) {
-            var r = n.shift()
-              , o = parseInt(t.substr(i, 1), 16);
-            if (1 & o) {
-                var a = {};
-                r[0] = a,
-                n.push(a)
-            }
-            2 & o && (a = {},
-            r[1] = a,
-            n.push(a)),
-            4 & o && (a = {},
-            r[2] = a,
-            n.push(a)),
-            8 & o && (a = {},
-            r[3] = a,
-            n.push(a))
-        }
-        var s = {
-            empty: !0
-        };
-        return this.computeHashes(s, e, ""),
-        s
-    }
-   
-    computeHashes(t, e, n) {
-        for (var i = 0; i < 4; i++)
-            e[i] && (t[n + i.toString(10)] = !0,
-            t.empty = !1,
-            this.computeHashes(t, e[i], n + i.toString(10)))
-    }
-    
- 
-
-}
-
-
-/* { 
-    "bundle_id": 1,                         //t-CwfhfqJ
-    "file_name": "$DEPTH/$X/$Y.png",
-    "file_path": "data/bundle_t-CwfhfqJ/building_1/map_tiles/11",
-    "floor_id": 11,
-    "id": 1,
-    "location": [
-        113.5957510575092,
-        22.366605927999239,
-        0.0
-    ],
-    "map_size_m": 61.44,
-    "max_depth": 3,
-    "orientation": [
-        0.7071067811865476,
-        0.0,
-        0.0,
-        0.7071067811865475
-    ],
-    "quadtree": "fe5f7c7fcffff7f53",
-    "sceneCode": "t-CwfhfqJ",
-    "tile_size_px": 256,
-    "type": "TILED_PYRAMID"
-
-
-}
- */
-
-
-
- 
-

+ 0 - 646
src/viewer/map/MapViewer.js

@@ -1,646 +0,0 @@
-
-import * as THREE from "../../../libs/three.js/build/three.module.js";
-import {MapLayer} from './Map.js'
-import {FirstPersonControls} from '../../navigation/FirstPersonControls.js'
-import {View} from "../View.js";
-import Viewport from "../Viewport.js"; 
-import {InputHandler} from "../../navigation/InputHandler.js";
-import {ViewerBase} from "../viewerBase.js"
-import math from "../../utils/math.js";
-//import CursorDeal from '../utils/CursorDeal.js' 
-import {Images360} from '../../modules/Images360/Images360.js'
-import Common from '../../utils/Common.js' 
-import {transitions, easing, lerp} from "../../utils/transitions.js";
-import {config } from "../../settings.js";
-/* var centerCross = new THREE.Mesh(new THREE.SphereGeometry(1, 4, 4),new THREE.MeshBasicMaterial({
-    transparent:true,  color:"#ff0000", opacity:0.5
-})); */
-
-
-/* const mapHeight = -1000;//要比点云低。最低
-const cameraHeight = 1000;  //最高 */
-const panosHeight = config.map.mapHeight + 100 //要比点云低  (marker)
-const cursorHeight = 0;//比地图高就行  
-const routeLayerHeight = config.map.mapHeight + 105
-
-const texLoader = new THREE.TextureLoader()
-const planeGeo = new THREE.PlaneBufferGeometry(1,1)
-
-const markerSize = 1;
-
-
-var initCameraFeildWidth = 50
-var panoMarkerMats  
- 
-
-export class MapViewer extends ViewerBase{
-    constructor(dom){
-        super(dom, {
-            clearColor: Potree.config.mapBG,
-            name: 'mapViewer'
-        })
-        this.visible = true
-        this.initScene()
-         
-         
-        this.mapLayer = new MapLayer(this, this.viewports[0])
-        this.scene.add(this.mapLayer.sceneGroup)
-        this.mapLayer.sceneGroup.position.setZ(Potree.config.map.mapHeight)
-        this.mapRatio = 0.5
-        this.splitDir = 'leftRight'
-        
-        
-        //this.scene.add(centerCross)
-        
-     
-        viewer.addEventListener("camera_changed", (e)=>{
-            if(e.viewport == viewer.mainViewport) this.updateCursor()
-            else this.updateWhenAtViewer() 
-        })
-        
-         
-        //viewer.addEventListener("global_mousemove", this.updateWhenAtViewer.bind(this))  //鼠标移动时reticule也动,所以直接就needRender
-        viewer.reticule.addEventListener('update',(e)=>{
-            if(this.attachedToViewer)this.needRender = true 
-        })
-         
-        
-        viewer.scene.addEventListener("360_images_added", this.addPanos.bind(this))
-        
-        
-        viewer.addEventListener("loadPointCloudDone",  this.initProjection.bind(this))
-        
-        this.addEventListener('global_click',(e)=>{
-            if(!e.isTouch && e.button != THREE.MOUSE.LEFT)return
-            this.updateClosestPano(e.intersect)
-        })
-        
-        
-        
-        this.addEventListener('add',(e)=>{//添加其他mesh
-            this.scene.add(e.object)
-            if(e.name == 'route'){
-                e.object.position.z = routeLayerHeight 
-            } 
-            viewer.setObjectLayers(e.object, 'mapObjects' )
-        })
-        
-        
-        
-        
-        
-        if(!Potree.settings.isOfficial){
-            let domRoot = viewer.renderer.domElement.parentElement; 
-            let elAttach = $("<input type='button' value='map绑定'></input>");
-            elAttach.css({
-                position : "absolute",
-                right : '80%',
-                bottom: '20px',
-                zIndex: "10000",
-                fontSize:'1em', color:"black",
-                background:'rgba(255,255,255,0.8)',
-            })
-            let state = false
-            elAttach.on("click", () => {
-                state = !state
-                this.attachToMainViewer(state, 'measure')
-                elAttach.val(state ? 'map分离':'map绑定')
-            }); 
-            domRoot.appendChild(elAttach[0]);
-            
-            
-        }
-        
-    }
-    
-    
-    
-    
-    
-    waitLoadDone(callback){//确保加载完后执行
-        if(this.mapLayer.loadingInProgress == 0){
-            callback()
-        }else{
-            var f = ()=>{
-                callback()
-                this.mapLayer.removeEventListener('loadDone', f)
-            } 
-            this.mapLayer.addEventListener('loadDone', f)  
-        }
-        
-    }
-    
-    addListener(images360){
-        images360.addEventListener('flyToPano',e=>{
-            let toPano = e.toPano
-            if(toPano.dontMoveMap) return
-            
-            /* transitions.start(lerp.vector(this.view.position, toPano.pano.position.clone().setZ(cameraHeight), 
-            
-            (pos, progress)=>{
-                  
-            }), toPano.duration, null, 0, easing[toPano.easeName]  );  */ 
-            let boundSize// = new THREE.Vector2(10,10)
-            
-            this.moveTo(toPano.pano.position.clone().setZ(Potree.config.map.cameraHeight),  boundSize, toPano.duration, toPano.easeName) 
-        })
-        
-        
-    }
-    
-    
-    
-    initProjection(){
-        this.started = true
-        this.mapLayer.initProjection()
-        
-    }
-     
-    initScene(){
-        let w = initCameraFeildWidth
-        let width = this.renderArea.clientWidth;
-		let height = this.renderArea.clientHeight; 
-        //let aspect = width / height
-        this.camera = new THREE.OrthographicCamera(-width/2,width/2,height/2,-height/2/* -w/2, w/2, w/2/aspect, -w/2/aspect */, 0.01, 10000);
-        this.camera.zoom = width / w  //zoom越大视野越小
-        //this.camera.position.set(0,0,10);
-        this.camera.up.set(0,0,1)
-        //this.camera.lookAt(new THREE.Vector3())
-        //this.camera.updateMatrixWorld()
-         
-        this.view = new View();
-        this.view.position.set(0,0,Potree.config.map.cameraHeight);
-        this.view.lookAt(0,0,0)
-        this.setViewLimit('standard')
-            
-        let viewport = new Viewport( this.view, this.camera, {
-            left:0, bottom:0, width:1, height: 1, name:'mapViewport'  
-        })
-        viewport.axis = ["x","y"]
-        viewport.axisSign = [1,1]
-        
-        viewport.noPointcloud = true; //不要渲染点云
-        viewport.render = this.render.bind(this)//标志给mainView渲染
-        //viewport.unableDepth = true //depthBasicMaterial等在此viewport中不开启depth
-        
-        
-        
-        this.viewports = [viewport]
-        
-               
-                
-                
-        this.controls = new FirstPersonControls(this, this.viewports[0]);
-        this.controls.setEnable(true)
-        this.scene = new THREE.Scene();  
-  
-        
-        
-        this.inputHandler = new InputHandler(this, this.scene);
-        this.inputHandler.name = 'mapInputHandler'
-        //this.inputHandler.addInputListener(this.controls);
-        this.inputHandler.registerInteractiveScene(this.scene);//interactiveScenes
-       
-        this.viewports[0].interactiveScenes = this.inputHandler.interactiveScenes;//供viewer的inputHandler使用
-         
-        var cursor = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
-            transparent:true,
-            opacity:0.9,
-            depthTest : false, //防止透明冲突
-            map: texLoader.load(Potree.resourcePath+'/textures/pic_location128.png' )
-        }))
-            cursor.position.set(0,0,cursorHeight);
-            
-            
-        this.cursor = cursor
-       
-        this.scene.add(cursor)
-        viewer.setObjectLayers(this.scene, 'mapObjects' )
-    }
-    
-    setViewLimit(state){//设置边界,当编辑空间模型等时要解禁
-        let setting = Potree.config.OrthoCameraLimit[state]
-        if(setting){
-            this.view.limitBound = new THREE.Box3().copy(setting.posBound)
-            this.camera.zoomLimit = $.extend({},setting.zoom);
-        }else{  
-            this.view.limitBound = null
-            this.camera.zoomLimit  = null
-        }
-    } 
-    
-    updateCursor(){
-         
-        var scale = math.getScaleForConstantSize( {//规定下最小最大像素
-            minSize : 80,  maxSize : 200,   nearBound : initCameraFeildWidth*0.1 , farBound : initCameraFeildWidth*2,
-            camera:this.camera , position: this.cursor.getWorldPosition(new THREE.Vector3()),
-            resolution: this.viewports[0].resolution//2
-        })
-        this.cursor.scale.set(scale, scale, scale);//当地图缩放时
-        this.cursor.position.copy(viewer.mainViewport.camera.position).setZ(cursorHeight)//当场景镜头旋转移动时
-        this.cursor.rotation.z = viewer.mainViewport.view.yaw
-        this.needRender = true
-    }
-    
-    addPanos(e){
-        var panosGroup = new THREE.Object3D;
-        panoMarkerMats = {
-            default: new THREE.MeshBasicMaterial({
-                transparent:true,
-                opacity: 0.5,
-                map: texLoader.load(Potree.resourcePath+'/textures/map_marker.png' ),
-            }),
-            selected: new THREE.MeshBasicMaterial({
-                transparent:true,
-                opacity: 1,
-                map: texLoader.load(Potree.resourcePath+'/textures/map_marker.png' ),
-            })
-        }
-        
-         
-        e.images.panos.forEach(pano=>{
-            pano.mapMarker = new THREE.Mesh(planeGeo, panoMarkerMats.default);
-            pano.mapMarker.position.copy(pano.position).setZ(0)   
-            pano.mapMarker.scale.set(markerSize,markerSize,markerSize)
-            pano.mapMarker.name = 'mapMarker'
-            panosGroup.add(pano.mapMarker); 
-            
-            
-            let mouseover = (e)=>{ 
-                if(!e.byMap){
-                    pano.mapMarker.material = panoMarkerMats.selected
-                    if(!e.byMainView) pano.dispatchEvent({type: "hoverOn", byMap:true})
-                    this.needRender = true    
-                }
-            }
-            
-            let mouseleave = (e)=>{
-                if(!e.byMap){
-                    pano.mapMarker.material = panoMarkerMats.default
-                    if(!e.byMainView) pano.dispatchEvent({type: "hoverOff", byMap:true})
-                    this.needRender = true
-                }
-            }
-             
-            
-            pano.mapMarker.addEventListener('mouseover', mouseover);  
-			pano.mapMarker.addEventListener('mouseleave', mouseleave);
-            
-            pano.addEventListener('hoverOn', mouseover)
-            pano.addEventListener('hoverOff', mouseleave)
-            
-            let onclick = (e)=>{
-                viewer.images360.flyToPano(pano)
-            }
-            pano.mapMarker.addEventListener('click', onclick); 
-            
-            pano.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)   
-                //console.log('panoMarker isVisible', pano.id, e.visible)
-                viewer.updateVisible(pano.mapMarker, 'panoVisible', e.visible )
-                this.needRender = true
-                
-            })
-            pano.addEventListener('rePos',(e)=>{
-                pano.mapMarker.position.copy(pano.position).setZ(0)
-            })
-        })
-        this.scene.add(panosGroup)
-        panosGroup.position.z = panosHeight
-        this.panosGroup = panosGroup
-        viewer.setObjectLayers(panosGroup, 'mapObjects' )
-        
-        
-        /* e.images.on('markersDisplayChange', (show)=>{
-            panosGroup.visible = show
-            this.needRender = true
-        }) */
-        
-        //-------
-        
-        //this.fitPanosToViewport()
-
-
-        this.initFitView()
-
-    }
-    
-    
-    
-    updateClosestPano(intersect){
-        if(viewer.images360.flying)return; 
-        intersect = intersect && intersect.orthoIntersect
-        if(!intersect)return
-        
-        intersect = intersect.clone().setZ(0)
-        
-        const minDis = 20 //距离鼠标不能太远
-
-        
-        var filterFuncs = [
-            (pano)=>{
-                return pano.position.clone().setZ(0).distanceTo(intersect) < minDis 
-            },
-            Images360.filters.isEnabled(),
-            
-            Images360.filters.isVisible(),//只走显示的点,否则会走到别的层 
-        ]; 
-      
-        
-         
-    
-        
-        
-        var pano = Common.find(viewer.images360.panos,  filterFuncs, [Images360.sortFunctions.floorDisSquaredToPoint(intersect)]);
-        if (pano && pano != viewer.images360.currentPano ) {
-           viewer.images360.flyToPano(pano)
-            
-        }   
-          
-    }
-    
-    
-    fitPanosToViewport(){//使所有漫游点占满viewport 
-        
-        //var w = viewer.bound.boundSize.x; 
-        var boundSize = viewer.images360.bound.size.clone().multiplyScalar(1.1);
-        boundSize.max(new THREE.Vector3(4,4,4))
-        let endPosition = viewer.images360.bound.center.clone()
-        this.moveTo(endPosition, boundSize, 0) 
-    }
-
-    fitToPointcloud(pointcloud, duration=400){
-        var boundSize = pointcloud.bound.getSize(new THREE.Vector3)/* .multiplyScalar(1.1); */
-            boundSize.max(new THREE.Vector3(4,4,4))
-        let endPosition = pointcloud.bound.getCenter(new THREE.Vector3)
-        this.moveTo(endPosition, boundSize, duration) //给点duration去变化 否则地图放大后所占的还是很小
-    }
-
-    initFitView(){ 
-        let dis = 5 , px = 70 //地图上px像素长度代表的距离为dis  //px是手动缩放到5m后发现是这个长度
-        let zoom = px / dis;
-        this.camera.zoom = zoom
-        this.moveTo(viewer.images360.position/* viewer.images360.bound.center */) 
-        this.camera.updateProjectionMatrix()
-
-    }
-    
-    fitToDatasets(datasets){
-        let bound = new THREE.Box3; 
-        datasets.forEach(e=>bound.union(e.bound))
-        let center = bound.getCenter(new THREE.Vector3)
-        let size = bound.getSize(new THREE.Vector3)
-        
-        this.moveTo(center, size, 200 ) //给duration是为了顺应视口大小改变,缓冲
-    }
-    
-    moveTo(endPosition, boundSize, duration=0, easeName){//前两个参数有xy即可
-        endPosition = new THREE.Vector3(endPosition.x,endPosition.y,Potree.config.map.cameraHeight)
-        this.view.moveOrthoCamera(this.viewports[0],  {endPosition, boundSize },   duration,  easeName)
-        
-       
-        
-        
-        /* let endZoom, startZoom = this.camera.zoom 
-        
-        //修改相机为bound中心,这样能看到全部(宽度范围内)
-        
-        this.view.setView({ position:endPosition,  duration, 
-            callback:()=>{//done
-                 
-            },
-            onUpdate:(progress)=>{ 
-                if(boundSize){ 
-                    let aspect = boundSize.x / boundSize.y
-                    let w, h; 
-                    
-                    if(this.camera.aspect > aspect){//视野更宽则用bound的纵向来决定
-                        h = boundSize.y
-                        //w = h * this.camera.aspect
-                        endZoom = this.viewports[0].resolution.y / h
-                    }else{
-                        w = boundSize.x; 
-                        //h = w / this.camera.aspect
-                        endZoom = this.viewports[0].resolution.x / w
-                    }  
-                    //onUpdate时更新endzoom是因为画布大小可能更改
-                    
-                    
-                    this.camera.zoom = endZoom * progress + startZoom * (1 - progress)
-                    this.camera.updateProjectionMatrix() 
-                } 
-            },
-            
-            Easing:easeName
-        
-        }) */
-          
-            
-    }
-    
-    
-    
-    
-    updateWhenAtViewer(e){
-        if(this.attachedToViewer){ 
-            if(this.started) this.mapLayer.update()  
-            
-            this.updateCursor() 
-            this.needRender = true
-        } 
-    }
-    
-    update(delta, areaSize ){
-        if(!this.visible && !this.attachedToViewer )return
-        
-        
-        if(this.attachedToViewer){
-            if(this.mapLayer.needUpdate){//必须更新。(较少触发)
-                this.updateWhenAtViewer()
-            }
-            return
-        }            
-         
-         
-        this.updateScreenSize()
-         
-        this.controls.update(delta);
-        this.view.applyToCamera(this.camera) 
-        
-            
-        let changed = this.cameraChanged() 
-        
-        
-        if(this.started && (changed || this.mapLayer.needUpdate))this.mapLayer.update()
-        
-        if(changed /*||   || this.needRender */){ 
-            this.dispatchEvent({
-                type: "camera_changed", 
-                camera: this.camera,
-                viewport : this.viewports[0]
-            })
-                 
-            this.needRender = true
-            this.updateCursor()//更改大小
-        }
-        this.render()
-        
-    }
-    
-    attachToMainViewer(state, desc, mapRatio=0.5, options={}){//转移到viewer中。测量时展示or截图需要
-        
-        if(!Potree.settings.isOfficial)this.renderArea.style.display = state ? 'none':'block'
-        
-        if(state){
-            this.enabledOld = this.enabled
-            this.enabled = true
-            
-            if(mapRatio != 'dontSet'){
-                this.changeSplitScreenDir(options.dir, mapRatio)
-                 
-                if(this.attachedToViewer){ 
-                    //this.fitPanosToViewport() 
-                    viewer.updateScreenSize({forceUpdateSize:true})        
-                    return
-                }                
-                viewer.viewports = [viewer.mainViewport, viewer.mapViewer.viewports[0] ];//因为mainViewer的相机变化会触发map的变化,所以先渲染mainViewer
-            }
-              
-            
-            if(desc == 'measure') this.inputHandler.registerInteractiveScene(viewer.measuringTool.scene);//虽然用的是viewer的inputHandler,但借用了this.inputHandler的interactiveScenes
-            else if(desc == 'split4Screens') {
-                this.inputHandler.registerInteractiveScene(viewer.scene.scene);
-            }
-            
-        }else{
-            if(!this.attachedToViewer)return
-             
-            viewer.mainViewport.left = 0;
-            viewer.mainViewport.bottom = 0;
-            viewer.mainViewport.width = 1;
-            viewer.mainViewport.height = 1;
-             
-            this.viewports[0].width = 1;
-            this.viewports[0].bottom = 0;
-            this.viewports[0].height = 1;
-            this.viewports[0].left = 0;
-            
-            this.inputHandler.unregisterInteractiveScene(viewer.measuringTool.scene);
-            this.inputHandler.unregisterInteractiveScene(viewer.scene.scene);
-            viewer.viewports = [viewer.mainViewport]
-            this.updateScreenSize({forceUpdateSize:true}) //更新相机projectionMatrix
-        }
-        
-        //if(desc == 'measure')this.renderMeasure = state 
-        this.attachedToViewer = state
-        
-        
-        
-        
-         
-        viewer.updateScreenSize({forceUpdateSize:true})
-        
-        
-        //mapRatio != 'dontSet' && !options.dontFit && this.moveTo(...)//要写在updateScreenSize后面,因为要根据视图大小来fit
-        if(options.moveToCurrentPos){
-            let boundSize = new THREE.Vector2(10,10)
-            let duration = 1000
-            this.moveTo(viewer.images360.position.clone(), boundSize, duration)
-        }    
-        this.needRender = true
-    }
-    
-    changeSplitScreenDir(dir, mapRatio){//左右 | 上下
-        //if(!dir || dir == this.dir)return
-        if(dir )this.splitDir = dir
-        this.updateSplitSize(mapRatio)
-        /* if(this.attachedToViewer){ //如果已经分屏了,中途修改方向的话……
-            this.updateSplitSize()
-        } */ 
-    }
-    updateSplitSize(mapRatio){
-        if(mapRatio != void 0) this.mapRatio = mapRatio
-        
-        //console.log(this.mapRatio)
-        
-        if(this.splitDir == 'leftRight'){//地图在左方
-            viewer.mainViewport.left = this.mapRatio
-            viewer.mainViewport.width = 1-this.mapRatio
-            this.viewports[0].width = this.mapRatio; 
-            
-            viewer.mainViewport.bottom = this.viewports[0].bottom = 0
-            viewer.mainViewport.height = this.viewports[0].height = 1
-             
-        }else if(this.splitDir == 'upDown'){ //地图在下方
-            viewer.mainViewport.bottom = this.mapRatio
-            viewer.mainViewport.height = 1-this.mapRatio
-            this.viewports[0].height = this.mapRatio;  
-            
-            viewer.mainViewport.left = this.viewports[0].left = 0
-            viewer.mainViewport.width = this.viewports[0].width = 1
-            
-        }
-        if(this.attachedToViewer){ 
-            viewer.updateScreenSize({forceUpdateSize:true})
-        }
-    }
-    
-    render(params={}){
-       
-        if(!this.visible && !this.attachedToViewer || !this.needRender && !params.force)return
-        
-        let renderer = params.renderer || this.renderer
-        
-        if(params.target){
-            renderer.setRenderTarget(params.target)
-        } 
-        /* if(params.resize){
-            this.emitResizeMsg(new THREE.Vector2(params.width,params.height, viewport:params.viewport))
-        }  */
-        params.clear ? params.clear() : renderer.clear(); 
-        
-        if(!this.attachedToViewer){
-            viewer.dispatchEvent({type: "render.begin",  viewer: this, viewport:this.viewports[0], params }); 
-        }
-        
-        viewer.setCameraLayers(this.camera, ['map','mapObjects'  , 'bothMapAndScene'  ])
-        
-        if(this.mapGradientBG){//渲染背景
-            viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
-            renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
-        }
-        
-        renderer.render(this.scene, this.camera);
-        renderer.render(viewer.scene.scene, this.camera);
-        
-        //测量线等
-        //params.renderOverlay && params.renderOverlay({camera:this.camera, isMap:true, viewport: this.viewports[0] })//其余如reticule 等场景层
-        params.renderOverlay && params.renderOverlay( $.extend({}, params, { isMap:true }))
-        
-        
-        renderer.setRenderTarget(null)
-        
-        this.needRender = false
-        
-    }
-    
-    
-    /* render(){
-        
-        let camera =  viewer.scene.getActiveCamera();
-
-        viewer.scene.view.position.x += 0.01    
-
-        viewer.setCameraLayers(camera, ['sceneObjects','marker','reticule','skybox' ]) 
-        this.renderer.render(viewer.scene.scene, camera);  
-
-
-    } */
-    
-    
-    
-}
-
-
-
-//本地调试地图白屏是因为代码自动更新了 但没刷新
-

+ 34 - 430
src/viewer/sidebar.js

@@ -2,25 +2,22 @@
 import * as THREE from "../../libs/three.js/build/three.module.js";
 import {GeoJSONExporter} from "../exporter/GeoJSONExporter.js"
 import {DXFExporter} from "../exporter/DXFExporter.js"
-import {Volume, SphereVolume} from "../objects/tool/Volume.js"
-import {PolygonClipVolume} from "../objects/tool/PolygonClipVolume.js"
+import {Volume, SphereVolume} from "../utils/Volume.js"
+import {PolygonClipVolume} from "../utils/PolygonClipVolume.js"
 import {PropertiesPanel} from "./PropertyPanels/PropertiesPanel.js"
 import {PointCloudTree} from "../PointCloudTree.js"
-import {Profile} from "../objects/tool/Profile.js"
-import {Measure} from "../objects/tool/Measure.js"
+import {Profile} from "../utils/Profile.js"
+import {Measure} from "../utils/Measure.js"
 import {Annotation} from "../Annotation.js"
 import {CameraMode, ClipTask, ClipMethod} from "../defines.js"
-import {ScreenBoxSelectTool} from "../objects/tool/ScreenBoxSelectTool.js"
+import {ScreenBoxSelectTool} from "../utils/ScreenBoxSelectTool.js"
 import {Utils} from "../utils.js"
 import {CameraAnimation} from "../modules/CameraAnimation/CameraAnimation.js"
 import {HierarchicalSlider} from "./HierarchicalSlider.js"
 import {OrientedImage} from "../modules/OrientedImages/OrientedImages.js";
 import {Images360} from "../modules/Images360/Images360.js";
- 
-import JSON5 from "../../libs/json5-2.1.3/json5.mjs";
- 
-
 
+import JSON5 from "../../libs/json5-2.1.3/json5.mjs";
 
 export class Sidebar{
 
@@ -48,278 +45,21 @@ export class Sidebar{
 	}
 
 	init(){
-        if(Potree.settings.editType == 'merge'){
-            this.initMergeBar() 
-            this.initToolbar();
-            this.initScene();
-            this.initNavigation();
-        }else{
-            this.initAccordion();
-            this.initAppearance();
-            this.initToolbar();
-            this.initScene();
-            this.initNavigation();
-            this.initFilters();
-            this.initClippingTool();
-            this.initSettings();
-            
-            if(Potree.settings.editType != 'pano'){
-                this.initAlignment();
-                this.initClipModel();
-                this.initSiteModel()
-                this.initParitcle()
-                 
-            }else{
-                this.initPanosEdit()
-            }
-            
-        }
-         
-        
-        
-        
-        
-        
+
+		this.initAccordion();
+		this.initAppearance();
+		this.initToolbar();
+		this.initScene();
+		this.initNavigation();
+		this.initFilters();
+		this.initClippingTool();
+		this.initSettings();
+		
 		$('#potree_version_number').html(Potree.version.major + "." + Potree.version.minor + Potree.version.suffix);
 	}
 
-    
-    initAlignment(){
-        let Alignment = viewer.modules.Alignment
-        var pannel = $('#alignment');
-        var buttons = pannel.find('[name="transform"] button')   
-        var applyToPointcloud = (fun, value)=>{
-            return function(){
-                var selected = $('#alignment li[name="selectPointCloud"] input:checked' )
-                Array.from(selected).forEach(e=>{
-                    var pointcloud = viewer.scene.pointclouds.find(p=>p.name == e.name) 
-                    fun(pointcloud, value)
-                })
-                
-                
-            } 
-        }
-        //逆时针是正数
-        buttons.eq(0).on('click', applyToPointcloud(Alignment.rotate, 10)) 
-        
-        //viewer.scene.pointclouds[0].rotation.z += THREE.Math.degToRad(10)
-         
-        buttons.eq(1).on('click' ,applyToPointcloud(Alignment.rotate, 1))
-        buttons.eq(2).on('click', applyToPointcloud(Alignment.rotate, 0.1)) 
-       
-        buttons.eq(3).on('click', applyToPointcloud(Alignment.rotate, -10))
-        buttons.eq(4).on('click',applyToPointcloud(Alignment.rotate, -1))
-        buttons.eq(5).on('click',applyToPointcloud(Alignment.rotate, -0.1))
-        
-        
-        buttons.eq(6).on('click', applyToPointcloud(Alignment.translate, new THREE.Vector3(-1,0,0)))
-        buttons.eq(7).on('click', applyToPointcloud(Alignment.translate, new THREE.Vector3(1,0,0)))
-        buttons.eq(8).on('click', applyToPointcloud(Alignment.translate, new THREE.Vector3(0,-1,0)))
-        buttons.eq(9).on('click', applyToPointcloud(Alignment.translate, new THREE.Vector3(0,1,0)))
-        buttons.eq(10).on('click', applyToPointcloud(Alignment.translate, new THREE.Vector3(0,0,-1)))
-        buttons.eq(11).on('click', applyToPointcloud(Alignment.translate, new THREE.Vector3(0,0,1)))
-        pannel.find('#startAlignment').on('click', ()=>{
-            Alignment.enter()
-        })
-        pannel.find('#exitAlignment').on('click', ()=>{
-            Alignment.save()
-            Alignment.leave()
-        })
-        pannel.find('#rotTool').on('click', ()=>{
-            Alignment.switchHandle('rotate')
-        })
-        pannel.find('#moveTool').on('click', ()=>{
-            Alignment.switchHandle('translate')
-        })
-        
-        
-        
-    }
-    
-    
-    initMergeBar(){//多元融合模块
-        var pannel = $('#mergeModel');
-        var buttons = pannel.find('button')
-        let MergeEditor = viewer.modules.MergeEditor
-        let loading = false;
-        
-        pannel.find('ul[name="model"] li button').on('click',(e)=>{
-            if(loading)return console.log('还在加载', loading)
-            let $elem = $(e.target)
-            
-                
-            let parent = $elem.parent()
-            let name = parent.attr('name') 
-            
-            if($elem.attr('name') == 'select'){ 
-                return Potree.selectModel(name)
-            }
-            
-            if($elem.text() == '添加'){
-                let startTime = Date.now()
-                Potree.addModel(name,()=>{
-                    loading = false 
-                    $elem.text('删除')
-                    let now = Date.now()
-                    console.log('加载完毕', name, '用时', (now-startTime)/1000, 's')
-                })
-                loading = name
-                
-            }else{
-                Potree.removeModel(name)
-                $elem.text('添加')
-            }
-            
-            
-        })
-        
-        pannel.find('li button[name="splitScreen"]').on('click',(e)=>{
-            let $elem = $(e.target)
-            if($elem.text() == '分屏'){
-                $elem.text('恢复') 
-                MergeEditor.enterSplit()
-            }else{
-                $elem.text('分屏') 
-                MergeEditor.leaveSplit()
-            }
-        
-        })
-        
-        let addingTag = false
-        pannel.find('li button[name="tag"]').on('click',(e)=>{
-            let $elem = $(e.target)
-             
-            viewer.tagTool.startInsertion()
-        })
-        
-    }
-    
-    
-    
-    
-    addAlignmentButton(pointcloud){
-        var pannel = $('#alignment li[name="selectPointCloud"]>div');
-        var option = $(` <input name="${pointcloud.name}" class="editCheckbox" type="checkbox" >
-                          
-                         <label for="showingLabels">${pointcloud.name}</label>`) 
-                         
-        pannel.append(option)  
-        /* option.find("input").on('change',function(){ 
-        })
-         */
-    }
-    initClipModel(){
-        let Clip = viewer.modules.Clip
-        var pannel = $('#clipModel');
-        var buttons = pannel.find('button')
-        buttons.eq(0).on('click', Clip.enter.bind(Clip))
-        buttons.eq(1).on('click', Clip.download.bind(Clip))
-        buttons.eq(2).on('click', Clip.leave.bind(Clip))
-    }
-    initSiteModel(){
-        let SiteModel = viewer.modules.SiteModel
-        var pannel = $('#siteModel');
-        pannel.find('button[name="start"] ').on('click', SiteModel.enter.bind(SiteModel))
-        pannel.find('button[name="exit"] ').on('click', SiteModel.leave.bind(SiteModel))
-        pannel.find('button[name="building"] ').on('click', SiteModel.startInsertion.bind(SiteModel,'building'))
-        pannel.find('button[name="floor"] ').on('click', ()=>{
-            SiteModel.addFloor(SiteModel.buildings[0], 'top')
-        } )  
-        pannel.find('button[name="room"] ').on('click', ()=>{
-            SiteModel.startInsertion('room', SiteModel.buildings[0].buildChildren[0])
-        })
-        pannel.find('button[name="digHole"] ').on('click', ()=>{
-            SiteModel.selected && SiteModel.startInsertion('hole', SiteModel.selected)
-        })
-        
-        
-        
-        pannel.find('button[name="selectBuilding"] ').on('click', ()=>{
-            SiteModel.selectEntity(SiteModel.buildings[0] )
-        } )
-        pannel.find('button[name="selectFloor"] ').on('click', ()=>{
-            SiteModel.selectEntity(SiteModel.buildings[0].buildChildren[0])
-        } )
-        pannel.find('button[name="selectRoom"] ').on('click', ()=>{
-            SiteModel.selectEntity(SiteModel.buildings[0].buildChildren[0].buildChildren[0])
-        })
-        
-        
-        pannel.find('button[name="removeFirstBuilding"] ').on('click', ()=>{
-            SiteModel.removeEntity(SiteModel.buildings[0])
-        })
-        pannel.find('button[name="removeFirstFloor"] ').on('click', ()=>{
-            SiteModel.removeEntity(SiteModel.buildings[0].buildChildren[0])
-        })
-        pannel.find('button[name="removeFirstRoom"] ').on('click', ()=>{
-            SiteModel.removeEntity(SiteModel.buildings[0].buildChildren[0].buildChildren[0])
-        })
-        pannel.find('button[name="removeFirstHole"] ').on('click', ()=>{
-            SiteModel.selected.removeHole(SiteModel.selected.holes[0])   
-        })
-        
-        
-        
-        pannel.find('button[name="removeFirstMarker"] ').on('click', ()=>{
-            //SiteModel.removeMarker(SiteModel.selected.markers[0])
-            SiteModel.selected.removeMarker(0)
-        }) 
-        
-    }
-    initParitcle(){
-        let ParticleEditor = viewer.modules.ParticleEditor
-        var pannel = $('#particle');
-        pannel.find('button[name="addFire"] ').on('click', ()=>{ 
-            ParticleEditor.startInsertion('fire+smoke')
-        })
-        pannel.find('button[name="addExplode"] ').on('click', ()=>{ 
-            ParticleEditor.startInsertion('explode')
-        })
-  
-    }
-    
-    initPanosEdit(){
-        let PanoEditor = viewer.modules.PanoEditor
-        let Alignment = viewer.modules.Alignment
-        
-        var pannel = $('#panos');
-        pannel.find('button[name="save"] ').on('click', ()=>{ 
-            console.log('saveData',PanoEditor.exportSavingData())
-        })
-        pannel.find('button[name="translate"] ').on('click', ()=>{ 
-            Alignment.switchHandle('translate')
-        })
-        pannel.find('button[name="rotate"] ').on('click', ()=>{ 
-            Alignment.switchHandle('rotate')
-        })
-        pannel.find('button[name="topView"] ').on('click', ()=>{ 
-            PanoEditor.switchView('top') 
-        })
-        pannel.find('button[name="sideView"] ').on('click', ()=>{ 
-            PanoEditor.switchView('right') 
-        })
-        pannel.find('button[name="3DView"] ').on('click', ()=>{ 
-            PanoEditor.switchView('mainView') 
-        })
-        
-        pannel.find('button[name="addLink"] ').on('click', ()=>{ 
-            PanoEditor.setLinkOperateState('addLink', true)
-        })
-        pannel.find('button[name="removeLink"] ').on('click', ()=>{ 
-            PanoEditor.setLinkOperateState('removeLink', true)
-        })
-        
-        pannel.find('button[name="getCloser"] ').on('click', ()=>{ 
-            PanoEditor.setZoomInState(true)
-        })
-        
-        
-    }
-    
-    
-    
-    
-    
+		
+
 	initToolbar(){
 
 		// ANGLE
@@ -335,8 +75,7 @@ export class Sidebar{
 					showArea: false,
 					closed: true,
 					maxMarkers: 3,
-                    minMarkers:3,
-					measureType: 'Angle'});
+					name: 'Angle'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -355,12 +94,10 @@ export class Sidebar{
 					showDistances: false,
 					showAngles: false,
 					showCoordinates: true,
-                    showEdges:false,
 					showArea: false,
 					closed: true,
 					maxMarkers: 1,
-                    minMarkers:1,
-					measureType: 'Point'});
+					name: 'Point'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -379,10 +116,7 @@ export class Sidebar{
 					showDistances: true,
 					showArea: false,
 					closed: false,
-                    minMarkers:2,
-                    maxMarkers: 2,
-					measureType: 'Distance'
-                });
+					name: 'Distance'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -398,15 +132,12 @@ export class Sidebar{
 			() => {
 				$('#menu_measurements').next().slideDown();
 				let measurement = this.measuringTool.startInsertion({
-					showDistances: true,//false,
+					showDistances: false,
 					showHeight: true,
 					showArea: false,
 					closed: false,
 					maxMarkers: 2,
-                    minMarkers:2, 
-                    //showGuideLine: true: true
-                    measureType: 'Ver Distance',
-                });
+					name: 'Height'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -429,9 +160,7 @@ export class Sidebar{
 					showEdges: false,
 					closed: false,
 					maxMarkers: 3,
-                    minMarkers:3,
-					measureType: 'Circle'
-                });
+					name: 'Circle'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -455,8 +184,7 @@ export class Sidebar{
 					showAzimuth: true,
 					closed: false,
 					maxMarkers: 2,
-                    minMarkers:2,
-					measureType: 'Azimuth'});
+					name: 'Azimuth'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -475,30 +203,7 @@ export class Sidebar{
 					showDistances: true,
 					showArea: true,
 					closed: true,
-                    minMarkers:3,
-                    //showGuideLine: true: true,
-					measureType: 'Area'});
-
-				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
-				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
-				$.jstree.reference(jsonNode.id).deselect_all();
-				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
-			}
-		));
-        
-        
-        //Hor AREA
-		elToolbar.append(this.createToolIcon(
-			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.Hor Area',
-			() => {
-				$('#menu_measurements').next().slideDown();
-				let measurement = this.measuringTool.startInsertion({
-					showDistances: true,
-					showArea: true,
-					closed: true,
-                    minMarkers:3, 
-					measureType: 'Hor Area'});
+					name: 'Area'});
 
 				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
 				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
@@ -506,100 +211,6 @@ export class Sidebar{
 				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
 			}
 		));
-        
-        
-        // Ver Area
-		elToolbar.append(this.createToolIcon(
-			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.Ver Area',
-			() => {
-				$('#menu_measurements').next().slideDown();
-				let measurement = this.measuringTool.startInsertion({
-					showDistances: true,
-					showArea: true,
-					closed: true,
-                    minMarkers:3, 
-					measureType: 'Ver Area'});
-
-				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
-				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
-				$.jstree.reference(jsonNode.id).deselect_all();
-				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
-			}
-		));
-        
-        // rect area freedom direction
-        elToolbar.append(this.createToolIcon(
-			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.area_freedom_rect',
-			() => {
-				$('#menu_measurements').next().slideDown();
-				let measurement = this.measuringTool.startInsertion({
-					showDistances: true,
-					showArea: true,
-					closed: true,
-                    minMarkers:4,
-                    maxMarkers:4,
-                    //showGuideLine: true: true,
-                    isRect:true,
-					measureType: 'Rect Area'});
-
-				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
-				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
-				$.jstree.reference(jsonNode.id).deselect_all();
-				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
-			}
-		));
-        // rect area horizontal
-		elToolbar.append(this.createToolIcon(
-			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.area_horizontal_rect',
-			() => {
-				$('#menu_measurements').next().slideDown();
-				let measurement = this.measuringTool.startInsertion({
-					showDistances: true,
-					showArea: true,
-					closed: true,
-                    minMarkers:4,
-                    maxMarkers:4,
-                    //showGuideLine: true: true,
-                    isRect:true,
-                    faceDirection:"horizontal", 
-					measureType: 'Hor Rect Area'});
-
-				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
-				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
-				$.jstree.reference(jsonNode.id).deselect_all();
-				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
-			}
-		));
-
-        // rect area vertical
-        elToolbar.append(this.createToolIcon(
-			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.area_vertical_rect',
-			() => {
-				$('#menu_measurements').next().slideDown();
-				let measurement = this.measuringTool.startInsertion({
-					showDistances: true,
-					showArea: true,
-					closed: true,
-                    minMarkers:4,
-                    maxMarkers:4,
-                    //showGuideLine: true: true,
-                    isRect:true,
-                    faceDirection:"vertical",
-					measureType: 'Ver Rect Area'});
-
-				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
-				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
-				$.jstree.reference(jsonNode.id).deselect_all();
-				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
-			}
-		));       
-                
-                
-                
 
 		// VOLUME
 		elToolbar.append(this.createToolIcon(
@@ -657,7 +268,7 @@ export class Sidebar{
 				$.jstree.reference(jsonNode.id).deselect_all();
 				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
 			}
-		)); 
+		));
 
 		// REMOVE ALL
 		elToolbar.append(this.createToolIcon(
@@ -715,7 +326,7 @@ export class Sidebar{
 					let url = window.URL.createObjectURL(new Blob([geoJson], {type: 'data:application/octet-stream'}));
 					elDownloadJSON.attr('href', url);
 				}else{
-					this.viewer.postError("nothing to export");
+					this.viewer.postError("no measurements to export");
 					event.preventDefault();
 				}
 			});
@@ -848,16 +459,11 @@ export class Sidebar{
 			}else if(object instanceof Measure){
 				let points = object.points.map(p => p.position);
 				let box = new THREE.Box3().setFromPoints(points);
-                
-				if(box.getSize(new THREE.Vector3()).length() == 0){
-                    box.min = box.max.clone();//禁止相同
-                    box.expandByVector(new THREE.Vector3(1,1,1));
-                } 
-                
-                let node = new THREE.Object3D();
-                node.boundingBox = box;
-                this.viewer.zoomTo(node, 2, 500);
-				  
+				if(box.getSize(new THREE.Vector3()).length() > 0){
+					let node = new THREE.Object3D();
+					node.boundingBox = box;
+					this.viewer.zoomTo(node, 2, 500);
+				}
 			}else if(object instanceof Profile){
 				let points = object.points;
 				let box = new THREE.Box3().setFromPoints(points);
@@ -1750,9 +1356,7 @@ export class Sidebar{
 			});
 
 			let currentBackground = this.viewer.getBackground();
-            try{
-                $(`input[name=background_options][value=${currentBackground}]`).trigger("click");
-            }catch(e){}
+			$(`input[name=background_options][value=${currentBackground}]`).trigger("click");
 		}
 
 		$('#chkEDLEnabled').click( () => {

+ 2 - 2
src/viewer/viewer.js

@@ -24,8 +24,8 @@ import {VolumeTool} from "../utils/VolumeTool.js";
 import {InputHandler} from "../navigation/InputHandler.js";
 import {NavigationCube} from "./NavigationCube.js";
 import {Compass} from "../utils/Compass.js";
-import {OrbitControls} from "../navigation/OrbitControls.js";
-import {FirstPersonControls} from "../navigation/FirstPersonControls.js";
+import {OrbitControls} from "../navigation/OrbitControlsNew.js";
+import {FirstPersonControls} from "../navigation/FirstPersonControlsNew.js";
 import {EarthControls} from "../navigation/EarthControls.js";
 import {DeviceOrientationControls} from "../navigation/DeviceOrientationControls.js";
 import {VRControls} from "../navigation/VRControls.js";