Browse Source

fix: 漫游点过渡遮挡 + 热点遮挡

xzw 2 years ago
parent
commit
ecac4f90c8

+ 2 - 59
examples/features_sorvilier.html

@@ -85,65 +85,8 @@
 
 		{ // MEASUREMENTS
 			let scene = viewer.scene;
-
-			{ // DISTANCE MEASURE
-				let measure = new Potree.Measure();
-				measure.closed = false;
-				measure.addMarker(new THREE.Vector3(589803.18, 231357.35, 745.38));
-				measure.addMarker(new THREE.Vector3(589795.74, 231323.42, 746.21));		
-				measure.addMarker(new THREE.Vector3(589822.50, 231315.90, 744.45));
-				
-				scene.addMeasurement(measure);
-			}
-			
-			{ // ANGLE MEASURE
-				let measure = new Potree.Measure();
-				measure.name = "Angle Sample";
-				measure.closed = true;
-				measure.showAngles = true;
-				measure.showDistances = false;
-				measure.addMarker(new THREE.Vector3(589866.11, 231372.25, 737.41));
-				measure.addMarker(new THREE.Vector3(589842.15, 231366.82, 743.61));		
-				measure.addMarker(new THREE.Vector3(589860.61, 231348.01, 740.33));
-				
-				scene.addMeasurement(measure);
-			}
-			
-			{ // SINGLE POINT MEASURE
-				let measure = new Potree.Measure();
-				measure.name = "Canopy";
-				measure.showDistances = false;
-				measure.showCoordinates = true;
-				measure.maxMarkers = 1;
-				measure.addMarker(new THREE.Vector3(589853.73, 231300.24, 775.48));
-				
-				scene.addMeasurement(measure);
-			}
-			
-			{ // HEIGHT MEASURE
-				let measure = new Potree.Measure();
-				measure.name = "Tree Height";
-				measure.closed = false;
-				measure.showDistances = false;
-				measure.showHeight = true;
-				measure.addMarker(new THREE.Vector3(589849.69, 231327.26, 766.32));
-				measure.addMarker(new THREE.Vector3(589840.96, 231329.53, 744.52));		
-				
-				scene.addMeasurement(measure);
-			}
-			
-			{ // AREA MEASURE
-				let measure = new Potree.Measure();
-				measure.name = "Area";
-				measure.closed = true;
-				measure.showArea = true;
-				measure.addMarker(new THREE.Vector3(589899.37, 231300.16, 750.25));
-				measure.addMarker(new THREE.Vector3(589874.60, 231326.06, 743.40));
-				measure.addMarker(new THREE.Vector3(589911.61, 231352.57, 743.58));
-				measure.addMarker(new THREE.Vector3(589943.50, 231300.08, 754.62));
-				
-				scene.addMeasurement(measure);
-			}
+ 
+			 
 
 			{ // PROFILE
 				let profile = new Potree.Profile();

+ 114 - 21
libs/three.js/3dtiles/three-loader-3dtiles.esm.js

@@ -22,6 +22,17 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 PERFORMANCE OF THIS SOFTWARE.
 ***************************************************************************** */
 
+
+window.visiVertexCount = 0
+window.visiGeoCount = 0
+const maxVertexVisi = 5e6
+const maxTexVisi = 500
+
+
+
+
+
+
 function __awaiter(thisArg, _arguments, P, generator) {
     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
     return new (P || (P = Promise))(function (resolve, reject) {
@@ -8466,6 +8477,7 @@ function createBoundingVolume(boundingVolumeHeader, transform, result) {
 
 function createBox(box, transform, result) {
   const center = new Vector3(box[0], box[1], box[2]);
+  result && (result.oriCenter = center)//add
   transform.transform(center, center);
   let origin = [];
 
@@ -9781,6 +9793,10 @@ const TILES_UNLOADED = 'Tiles Unloaded';
 const TILES_LOAD_FAILED = 'Failed Tile Loads';
 const POINTS_COUNT = 'Points';
 const TILES_GPU_MEMORY = 'Tile Memory Use';
+
+
+let tilesetSid = 0//add
+
 class Tileset3D extends EventDispatcher{//xzw add EventDispatcher
   constructor(json, options) {
       super()
@@ -9967,7 +9983,9 @@ class Tileset3D extends EventDispatcher{//xzw add EventDispatcher
 
   getTileUrl(tilePath) {
     const isDataUrl = tilePath.startsWith('data:');
-
+    
+    tilePath = Potree.Common.dealURL(tilePath) //add 去除'+'
+    
     if (isDataUrl) {
       return tilePath;
     }
@@ -10185,7 +10203,8 @@ class Tileset3D extends EventDispatcher{//xzw add EventDispatcher
   }
 
   _initializeTileHeaders(tilesetJson, parentTileHeader) {
-    const rootTile = new TileHeader(this, tilesetJson.root, parentTileHeader);
+       
+    const rootTile = new TileHeader(this, tilesetJson.root, parentTileHeader, parentTileHeader == void 0 && 'root_'+tilesetSid++);
 
     if (parentTileHeader) {
       parentTileHeader.children.push(rootTile);
@@ -16932,8 +16951,12 @@ function loadersBoundingBoxToMesh(tile) {
     const { boundingVolume } = tile;
     let redColor = 0;
     if (tile.content) {
-        redColor = Math.min(tile.content.byteLength / 500000, 1.0);
-    }
+        //redColor = Math.min((tile.content.byteLength != void 0 ? tile.content.byteLength : 0) / 500000, 1.0);
+        redColor = Math.min( tile.depth / 10 , 1.0); //改
+    } 
+     
+
+     
     const boxColor = new Color(redColor, 1.0, 0.0);
     const boxGeometry = new BoxGeometry(1, 1, 1);
     const boxTransform = new Matrix4$1();
@@ -16943,10 +16966,34 @@ function loadersBoundingBoxToMesh(tile) {
     else if (boundingVolume.radius) {
         boxGeometry.scale(boundingVolume.radius * 2, boundingVolume.radius * 2, boundingVolume.radius * 2);
     }
+    
+    boxTransform.premultiply((new Matrix4$1()).setPosition(...boundingVolume.center))  //add 
+    
+    
     boxGeometry.applyMatrix4(boxTransform);
+    
+    
     const edges = new EdgesGeometry(boxGeometry);
-    const dispPlane = new LineSegments(edges, new LineBasicMaterial({ color: boxColor }));
-    dispPlane.position.copy(new Vector3$1(...boundingVolume.center));
+    const dispPlane = new LineSegments(edges, new LineBasicMaterial({ color: boxColor, transparent:true }));
+    //dispPlane.position.copy(new Vector3$1(...boundingVolume.center));
+    dispPlane.matrixAutoUpdate = false //add
+    
+    
+    /* if(tile.content.byteLength == void 0){
+        let oldUpdate = dispPlane.updateMatrixWorld.bind(dispPlane)
+        dispPlane.updateMatrixWorld = (a,b)=>{
+            oldUpdate(a,b)
+        }
+        
+        let oldU = dispPlane.updateMatrix.bind(dispPlane)
+        dispPlane.updateMatrix = (a,b)=>{
+            oldU(a,b)
+        }
+    
+    } */
+    
+    
+    
     return dispPlane;
 }
 function getMatrix4FromHalfAxes(halfAxes) {
@@ -17268,8 +17315,11 @@ class Loader3DTiles {
             const unloadQueue = [];
             const root = new Group();
             const tileBoxes = new Group();
+            tileBoxes.matrixAutoUpdate = false//add
             if (!options.debug) {
                 tileBoxes.visible = false;
+            }else{
+                options.parent.add(tileBoxes) //add
             }
             const pointcloudUniforms = {
                 pointSize: { type: 'f', value: options.pointSize },
@@ -17339,7 +17389,7 @@ class Loader3DTiles {
                         if (options.debug) {
                             const box = loadersBoundingBoxToMesh(tile);
                             tileBoxes.add(box);
-                            boxMap[tile.id] = box;
+                            boxMap[tile.id] = box; 
                         }
                         //xzw :
                         tileset.dispatchEvent({type:'tileLoaded',tileContent}) //每一个tile加载完要更改透明度等
@@ -17498,14 +17548,32 @@ class Loader3DTiles {
                         if (!renderMap[tile.id]) {
                             console.error('TILE SELECTED BUT NOT LOADED!!', tile.id);
                         }
-                        else {
-                            // Make sure it's visible
-                            renderMap[tile.id].visible = true;
+                        else { 
+                            
+                            // Make sure it's visible 
+                            if(!renderMap[tile.id].visible){
+                                if(visiVertexCount<maxVertexVisi){
+                                    renderMap[tile.id].visible = true;
+                                    visiVertexCount += renderMap[tile.id].vertexCount  //xzw add
+                                    options.debug && (boxMap[tile.id].material.opacity = 1)
+                                }else{
+                                    console.log('超出', visiVertexCount)
+                                }
+                            }   
+                            /* if(!renderMap[tile.id].realVisible()){
+                                console.error('!realVisible')
+                            } */
+                            
                         }
                     }
                     else {
                         if (renderMap[tile.id]) {
-                            renderMap[tile.id].visible = false;
+                            if(renderMap[tile.id].visible){
+                                renderMap[tile.id].visible = false;
+                                options.debug && (boxMap[tile.id].material.opacity = 0.1)
+                                
+                                visiVertexCount -= renderMap[tile.id].vertexCount  //xzw add
+                            } 
                         }
                     }
                 }
@@ -17513,6 +17581,7 @@ class Loader3DTiles {
                 while (unloadQueue.length > 0) {
                     const tile = unloadQueue.pop();
                     if (renderMap[tile.id] && tile.contentState == TILE_CONTENT_STATE.UNLOADED) {
+                        //console.log('removevisi', renderMap[tile.id].visible) 如果是true,visiVertexCount要减
                         root.remove(renderMap[tile.id]);
                         disposeNode(renderMap[tile.id]);
                         delete renderMap[tile.id];
@@ -17627,25 +17696,36 @@ class Loader3DTiles {
                                 pointcloudUniforms.rootNormal.value.copy(new Vector3$1(0, 0, 1).applyMatrix4(lastRootTransform).normalize());
                                 rootTransformInverse.copy(lastRootTransform).invert();
                                 if (options.debug) {
-                                    boxMap[tileset.root.id].matrixWorld.copy(threeMat);
-                                    boxMap[tileset.root.id].applyMatrix4(lastRootTransform);
+                                   /*  console.log('move', tileset.root.id, boxMap[tileset.root.id].matrix.elements,  boxMap[tileset.root.id].matrixWorld.elements)
+                                    boxMap[tileset.root.id].matrix.copy(root.matrixWorld); 
+                                    boxMap[tileset.root.id].matrixWorld.copy(root.matrixWorld);  */
+                                    
+                                    
+                                    //boxMap[tileset.root.id].applyMatrix4(threeMat);
+                                    tileBoxes.matrix.copy(root.matrixWorld);
+                                    
+                                    //boxMap[tileset.root.id].matrixWorld.copy(threeMat);
+                                    //boxMap[tileset.root.id].applyMatrix4(lastRootTransform);//boxMap[tileset.root.id].applyMatrix4(lastRootTransform);
+                                    //boxMap[tileset.root.id].updateWorldMatrix() 
                                 }
                             }
-                            if (lastCameraTransform == null) {
-                                lastCameraTransform = new Matrix4$1().copy(camera.matrixWorld);
+                            if (this.lastCameraTransform == null) { 
+                                this.lastCameraTransform = new Matrix4$1().copy(camera.matrixWorld);
                             }
                             else {
-                                const cameraChanged = !camera.matrixWorld.equals(lastCameraTransform) ||
+                                const cameraChanged = !camera.matrixWorld.equals(this.lastCameraTransform) ||
                                     !(camera.aspect == lastCameraAspect);
                                 if (cameraChanged) {
                                     timer = 0;
                                     tileset._frameNumber++;
                                     camera.getWorldPosition(lastCameraPosition);
-                                    lastCameraTransform.copy(camera.matrixWorld);
+                                    this.lastCameraTransform.copy(camera.matrixWorld);
                                     tilesetUpdate(tileset, renderMap, renderer, camera);
                                 }
                             }
-                        }
+                        }/* else{
+                            console.log('11')
+                        } */
                     },
                     dispose: function () {
                         disposeFlag = true;
@@ -17675,13 +17755,18 @@ class Loader3DTiles {
     }
 }
 function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransformInverse) {
+      
     return __awaiter(this, void 0, void 0, function* () {
         return new Promise((resolve, reject) => {
             var _a;
             const rotateX = new Matrix4$1().makeRotationAxis(new Vector3$1(1, 0, 0), Math.PI / 2);
             const shouldRotate = ((_a = tile.tileset.asset) === null || _a === void 0 ? void 0 : _a.gltfUpAxis) !== "Z";
             // The computed trasnform already contains the root's transform, so we have to invert it
-            const contentTransform = new Matrix4$1().fromArray(tile.computedTransform).premultiply(rootTransformInverse);
+            const contentTransform = new Matrix4$1().fromArray(tile.computedTransform).premultiply(rootTransformInverse); //xzw 删。原先的会造成移动后tiles错乱
+            
+            //不知为何在刚开始加载时迅速移动模型,tiles位置会参差不齐。应该和contentTransform有关,也就是tile.computedTransform 和rootTransformInverse的问题。但即使每次_updateTransform后重新计算也不对
+            
+            
             if (shouldRotate) {
                 contentTransform.multiply(rotateX); // convert from GLTF Y-up to Z-up
             }
@@ -17691,7 +17776,7 @@ function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransform
                 (gltf) => {
                     const tileContent = gltf.scenes[0];
                     //tileContent.applyMatrix4(contentTransform);
-                     
+                    tileContent.vertexCount = 0   //xzw add
                     tileContent.traverse((object) => {
                         if (object.type == "Mesh") {
                             const mesh = object;
@@ -17706,8 +17791,10 @@ function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransform
                                 mesh.material = unlitMaterial.clone();
                                 originalMaterial.dispose();
                             }
+                            
+                            
                             if (options.shading != Shading.ShadedNoTexture) {
-                                if (mesh.material.type == "ShaderMaterial") {
+                                if (mesh.material.type == "ShaderMaterial" && !(mesh.material instanceof MeshBasicMaterial)) {//改 
                                     mesh.material.uniforms.map = { value: originalMap };
                                 }
                                 else {
@@ -17727,6 +17814,10 @@ function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransform
                             if (options.computeNormals) {
                                 mesh.geometry.computeVertexNormals();
                             } 
+                            //xzw add:
+                            tileContent.vertexCount += mesh.geometry.attributes.position.count
+                             
+                            
                             
                             //------------------add on 2023.1.16----zeg
                             
@@ -17741,6 +17832,8 @@ function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransform
                              
                         }
                     });
+                    
+                    
                     resolve(tileContent);
                 }, 
                 (e) => {

+ 13 - 6
src/custom/materials/BasicMaterial.js

@@ -1,11 +1,16 @@
 
 import * as THREE from "../../../libs/three.js/build/three.module.js";
 import {Shaders} from "../../../build/shaders/shaders.js";
+
+/* let sid = 0
+let getName = ()=>{
+    return sid ++
+} */
  
 
 class BasicMaterial  extends THREE.ShaderMaterial{ 
     constructor(o={}){
-        
+       
        super( Object.assign({},{ 
             uniforms:{
                 color:  {type:'v3',   value: o.color || new THREE.Color("#FFF")} ,
@@ -14,17 +19,19 @@ class BasicMaterial  extends THREE.ShaderMaterial{
             },
             vertexShader: Shaders['basicTextured.vs'],   
             fragmentShader: Shaders['basicTextured.fs'], 
-            defines:{HasColor:''}
+            defines:{HasColor:'' }
         },o))
-            
+        //this.name111 = getName()   
     } 
      
     
     copy(source){
         super.copy(source)
+        //console.log('copy',  source.name111, this.name111, !!source.map )
+         
         
         this.map = source.map
-         
+          
         return this
     }
     
@@ -43,8 +50,8 @@ class BasicMaterial  extends THREE.ShaderMaterial{
         }else{
             delete this.defines.HasMap
         }
-        //console.log('hasMap', !!o, this.name )
-    }
+        //console.log('hasMap', !!o, this.name111 )
+    } 
     get map(){
         return this.uniforms.map.value  
     }

+ 14 - 12
src/custom/mergeStartTest.js

@@ -7,7 +7,7 @@ import cameraLight from './utils/cameraLight.js'
 import './three.shim.js'  
 import "./potree.shim.js"
 
-
+ 
 
 
 
@@ -24,21 +24,21 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
     
     
     Potree.settings.number = number || 't-o5YMR13'// 't-iksBApb'// 写在viewer前 
-    Potree.fileServer = fileServer 
+    Potree.fileServer = fileServer  
     webSite && (Potree.settings.webSite = webSite)
     
     
     let viewer = new Potree.Viewer(dom , mapDom);
     
     let Alignment = viewer.modules.Alignment
-     
+       
 	viewer.setEDLEnabled(false);
     viewer.setFOV(config.view.fov); 
     viewer.loadSettingsFromURL(); 
     
     {
         
-        
+         
         
         viewer.mainViewport.view.position.set(100,100,200)
         viewer.mainViewport.view.lookAt(0,0,0)
@@ -338,12 +338,14 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                 }) 
                 model.addEventListener("rotation_changed", maintainBtmZAndCenter )
                 model.addEventListener("scale_changed", maintainBtmZAndCenter )
-                
+                model.addEventListener('transformChanged', ()=>{
+                    MergeEditor.modelTransformCallback(model)
+                })
             
             }
             model.updateMatrixWorld()
             viewer.updateModelBound()
-            
+            model.lastMatrixWorld = model.matrixWorld.clone();
             MergeEditor.getBoundCenter(model) //初始化
             
             
@@ -525,17 +527,17 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                  
                 viewer.loadModel({ 
                     fileType:'3dTiles',
-                    /*  tilesUrl: 'https://4dkk.4dage.com/scene_view_data/SS-Ds19qsmuFA/images/3dtiles/tileset.json',
+                     url: 'https://4dkk.4dage.com/fusion/test/b3dm/tileset.json',
                     transform : { 
                         rotation : [Math.PI/2,  0,   0],
                         position : [0,0,0]  
-                    }  */    
-   
-                    url: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
+                    }          
+       
+                    /* url: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
                     transform : { 
-                        rotation : [/* Math.PI/2,   */0,  0,   0],
+                        rotation : [ 0,  0,   0],
                         position : [0,0,0]   
-                    }   
+                    }  */  
                     
                     
                 },callback,onprogress)

+ 6 - 10
src/custom/modules/Particles/ParticleEditor.js

@@ -227,16 +227,12 @@ let ParticleEditor = {
             particles.push(explode)
         }    
 
-        var geoNeedsUpdate 
-        curve.addEventListener('dragCurvePoint',()=>{ 
-            geoNeedsUpdate = true 
-            Common.intervalTool.isWaiting('particlePointChange', ()=>{ //延时update,防止卡顿  
-                if(geoNeedsUpdate){ 
-                    particles.forEach(e=>e.updateGeometry())  
-                    geoNeedsUpdate = false 
-                    curve.dispatchEvent('sendUpdatePoints')
-                    return true  
-                }
+      
+        curve.addEventListener('dragCurvePoint',()=>{  
+            Common.intervalTool.isWaiting('particlePointChange', ()=>{ //延时update,防止卡顿   
+                particles.forEach(e=>e.updateGeometry())  
+                geoNeedsUpdate = false 
+                curve.dispatchEvent('sendUpdatePoints') 
             }, 400)  
         })
         

+ 7 - 6
src/custom/modules/clipModel/Clip.js

@@ -15,17 +15,18 @@ var Clip = {
     changeCallback(force){ 
         viewer.controls.setTarget(this.box.position)//绕其旋转
         
-        if(Potree.settings.isOfficial){  
-            Common.intervalTool.isWaiting('clipSelectedDatasets', ()=>{ //延时update,防止卡顿
+        if(Potree.settings.isOfficial){
+            let fun = ()=>{ 
                 let pointclouds = this.getIntersectPointcloud() 
-                if(force || Common.getDifferenceSet(pointclouds,this.selectedDatasets).length){  
+                if( Common.getDifferenceSet(pointclouds,this.selectedDatasets).length){  
                     this.selectedDatasets = pointclouds 
                     //console.error('clipSelectedDatasets',selectedDatasets)
                     this.bus.dispatchEvent({type:'updateSelectedDatasets', selectedDatasets:pointclouds.map(e=>e.dataset_id) })
-                    force = false
-                    return true 
+                    
                 } 
-            },  300)  
+            }
+            if(force)fun()        
+            else Common.intervalTool.isWaiting('clipSelectedDatasets', fun ,  300)  
         }  
     },
 

+ 5 - 16
src/custom/modules/panoEdit/panoEditor.js

@@ -323,16 +323,10 @@ class PanoEditor extends THREE.EventDispatcher{
                     this.setTranMode('translate') 
                 }
             }) */      
-            {
-                let changed
-                viewer.addEventListener('camera_changed', (e)=>{
-                    changed = true
+            { 
+                viewer.addEventListener('camera_changed', (e)=>{ 
                     Common.intervalTool.isWaiting('updatePointLevels', ()=>{  
-                        if(changed){
-                            changed = false
-                            this.updatePointLevels()
-                            return true
-                        }
+                        this.updatePointLevels()
                     }, 1050)
                 })
 
@@ -734,15 +728,10 @@ class PanoEditor extends THREE.EventDispatcher{
          
         informBy2d || this.dispatchEvent({type:"switchPanoVisible", pano, v})
         
-        {
-            this.updatedPL_ = true
+        { 
             setTimeout(()=>{
                 Common.intervalTool.isWaiting('updatePointLevels2', ()=>{  
-                    if(this.updatedPL_){
-                        this.updatedPL_ = false
-                        this.updatePointLevels()
-                        return true
-                    }
+                    this.updatePointLevels() 
                 }, 50)
             },1)//等update过visibleNodes
         }

+ 98 - 20
src/custom/modules/panos/DepthImageSampler.js

@@ -1,14 +1,18 @@
 
-
+import * as THREE from "../../../../libs/three.js/build/three.module.js"; 
 import math from "../../utils/math.js";
+import browser from "../../utils/browser.js";
+
+
 
-class DepthImageSampler {
+class DepthImageSampler extends THREE.EventDispatcher{
     
     constructor(){ 
+        super()
         var canvas = document.createElement("canvas");
         this.canvas = canvas
         this.context = canvas.getContext("2d")  
-        
+        this.imgDatas = []
         
         /* document.getElementsByTagName('body')[0].appendChild(canvas);
         canvas.style.position = 'fixed';
@@ -16,51 +20,116 @@ class DepthImageSampler {
         canvas.style.top = canvas.style.left = 0
         canvas.style['z-index'] = 100
          */
-        
+        this.maxDataCount = browser.isMobile() ? 6 : 20;  //手机会崩溃. 平均每张图为8M数据量(以200个点的园区为例,加载时间久一些后,总内存=700 + 每张图的8M * maxDataCount)
+        this.maxNeighCount = browser.isMobile() ? 3 : 14;  //包含在maxDataCount内的nearPanos最大个数.至少比maxDataCount少3个,留出空位给最近更新的pano
+        this.nearPanos = []
     }
  
-     
-    changeImg(img){
-        if(this.img == img)return
+    updateNearPanos(panos){
+        this.nearPanos = panos.slice(0,this.maxNeighCount)
+    }
+    
+    changeImg(img, pano){
+        
+        this.pano = pano
+        let item = this.imgDatas.find(p=>p.pano == pano) 
+        if(/* this.img == img ||  */item){
+            //最新的在末尾,所以换到末尾
+            
+            let index = this.imgDatas.indexOf(item)
+            this.imgDatas.splice(index,1)
+            this.imgDatas.push(item) 
+            //console.log('重复使用',item.pano.id)
+            return
+        }
+        
+        viewer.addTimeMark('depthSampler','start')
         this.canvas.width = img.width 
         this.canvas.height = img.height 
-        this.context.drawImage(img, 0, 0)
-        this.img = img
+        this.context.drawImage(img, 0, 0) 
+        let data = this.context.getImageData(0, 0, img.width , img.height ).data;     //getImageData 1px时 : pc chrome 耗时0.01毫秒左右(排除第一次的50) , 但firefox: 4。但换贴图之后就多达5甚至几十  
+        //console.log('changeImg',pano.id ) 
+        //this.img = img
+         
+         
+        if(this.imgDatas.length >= this.maxDataCount){
+            let old = this.imgDatas.find(e=>!this.nearPanos.includes(e.pano))   
+            //console.log('推出',old.pano.id)
+            this.imgDatas.splice(this.imgDatas.indexOf(old), 1)//推出使用时间最早的一个非nearPano
+        } 
+        this.imgDatas.push({pano, data})
+        
+        
+        
+        this.dispatchEvent({type:'changeImg',pano})
+        viewer.addTimeMark('depthSampler','end') //耗时chrome 25ms,firefox: 38ms, iphoneX:33ms
+        
+        
+        
+         /* pano.depthData = {}
+        for(let h=0; h<img.height; h++){
+            for(let w=0; w<img.width; w++){
+                let blockIndex = img.width * h + w
+                let r = data.slice(blockIndex*4, (blockIndex+1)*4)
+                let depth = r[1] + r[0] / 256 
+                if(depth>0) pano.depthData[h+'|'+w] = depth
+            }
+        } */ 
+        
     } 
     
      
-    getDepth(UVx, UVy) {//根据图片像素获取深度值 
+    getDepth( UVx, UVy) {//根据图片像素获取深度值 
         var x = Math.round(UVx * (this.canvas.width - 1))
           , y = Math.round(UVy * (this.canvas.height - 1));
+          
+        
         if (!(x < 0 || y < 0 || x >= this.canvas.width || y >= this.canvas.height)) { 
          
-            viewer.addTimeMark('depthSampler','start')
+            /* viewer.addTimeMark('depthSampler','start')
             
-            var r = this.context.getImageData(x, y, 1, 1).data;     //pc chrome 耗时0.01毫秒左右(排除第一次的50) , 但firefox: 4。  
+            var r = this.context.getImageData(x, y, 1, 1).data;     //pc chrome 耗时0.01毫秒左右(排除第一次的50) , 但firefox: 4。但换贴图之后就多达5甚至几十  
             
             viewer.addTimeMark('depthSampler','end')
             
              
             //console.log('color', r, x,y)
-            return r[1] + r[0] / 256
+            return r[1] + r[0] / 256 */
+            let imgData = this.imgDatas.find(p=>p.pano == this.pano)
+            let blockIndex = this.canvas.width * y + x
+            let color = imgData.data.slice(blockIndex*4, (blockIndex+1)*4)
+            return color[1] + color[0] / 256
+            
         }
-    }   
+    } 
+
+
+    
     
     
     sample( intersect, currentPano, onlyPos ) {//通过和skybox的intersect得到真实的intersect的位置
         if(!intersect)return
         
-          
+        
         
         let location = new THREE.Vector3
         let normal
         currentPano = currentPano || viewer.images360.currentPano
+        
+        //let markName 
          
+        
         if(currentPano != this.currentPano){
             if(!currentPano.depthTex/*  || !currentPano.depthTex.image  */) return //未加载
-            this.changeImg(currentPano.depthTex.image)
+             
+            /* markName = 'depthSampleChangeTex'
+            viewer.addTimeMark(markName,'start') */
+            this.changeImg(currentPano.depthTex.image, currentPano)
             this.currentPano = currentPano
-        }
+        }/* else{
+            markName = 'depthSampleSame'
+            viewer.addTimeMark(markName,'start')
+        } */
         
         let origin = currentPano.position
         let dir = intersect.dir || new THREE.Vector3().subVectors(intersect.point, origin).normalize()
@@ -70,8 +139,11 @@ class DepthImageSampler {
         let dirInPano = dir.clone().applyMatrix4(currentPano.panoMatrix2Inverse).normalize(); //转化为考虑漫游点旋转的方向
         let uv = math.getUVfromDir(dirInPano)//转化为uv
         
-        let distance = this.getDepth(uv.x, uv.y);
-        //console.log('depth', depth,  uv.y)
+        let distance = this.getDepth(  uv.x, uv.y);
+        //viewer.addTimeMark(markName,'end')
+        
+        
+        
         if (!distance){
             const margin =  0.1
             if(uv.y > 1-Potree.config.depthTexUVyLimit){//漫游点底部识别不到的区域,给一个地板高度
@@ -94,7 +166,13 @@ class DepthImageSampler {
             //console.log('无穷远')
             return !1;  //应该是天空或模型外 , 因为很少有漫游点的地方还拍不到地板
         }  
-         
+        
+        
+        
+        //console.log('distance', distance,  dirInPano.clone().multiplyScalar(distance)) 
+        
+        
+        
         location.copy(dir).multiplyScalar(distance).add(origin);
         
         if(!onlyPos){

File diff suppressed because it is too large
+ 496 - 198
src/custom/modules/panos/Images360.js


+ 15 - 2
src/custom/modules/panos/Panorama.js

@@ -184,7 +184,7 @@ class Panorama extends THREE.EventDispatcher{
             //let xy = this.transform.forward([this.longitude, this.latitude]);  
             //this.file = `https://4dkk.4dage.com/images/images${Potree.settings.number}/pan/high/${this.id}.jpg`
             
-             
+             this.neighbours = [];
             
         }
         this.rotation = new THREE.Euler().setFromQuaternion(this.quaternion) 
@@ -223,9 +223,21 @@ class Panorama extends THREE.EventDispatcher{
                 this.hoverOff(e) 
             } 
         })
+        
+        
 	}
     
-
+    get noNeighbour(){//是否绝对到不到的孤立点
+         
+        for(let i=0,j=this.images360.panos.length; i<j; i++){
+            if(this.images360.neighbourMap[this.id][i] !== false ){
+                return false
+            }
+        }
+        return true 
+        
+        //return this.neighbours.length == 0
+    }
 
     setEnable(enable){//是否可以走
         Potree.Utils.updateVisible(this, 'isEnabled', enable) //令所有marker不可见
@@ -240,6 +252,7 @@ class Panorama extends THREE.EventDispatcher{
         this.depthTexLoading = true
         let src = //Potree.settings.number == 'SS-t-7DUfWAUZ3V' ?  `${Potree.scriptPath}/data/${Potree.settings.number}/depthMap/${this.originID}.png` :
                  `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/depthmap/${this.originID}.png`
+        //console.log('开始下载depthImg', this.id)
         let texture = texLoader.load( src, ()=>{
             this.depthTex = texture
             this.images360.dispatchEvent({type:'loadedDepthImg', pano:this, loaded:true})

+ 10 - 11
src/custom/modules/panos/tile/TileDownloader.js

@@ -107,23 +107,22 @@ class TileDownloader extends THREE.EventDispatcher{
         if(this.downloadCubeTex){ //可以下载贴图
             var e = this.forceQueue.length > 0;
             this.processQueueForDownloading(this.forceQueue);
-            if (this.processPriorityQueue) { 
-                this.needPrioritize = true
+            if (this.processPriorityQueue) {  
                 Potree.Common.intervalTool.isWaiting('processPriorityQueue', ()=>{ //延时update,防止崩溃 , 未到时间就拦截(第一次直接执行)
-                    if(this.needPrioritize && this.downloadCubeTex){
-                        this.needPrioritize = false
-                        this.queuePrioritizedTilesForPanos(this.panos)   //这句比较耗时 降四倍时大概1-2毫秒
-                        return true  
-                    }
+                    this.queuePrioritizedTilesForPanos(this.panos)   //这句比较耗时 降四倍时大概1-2毫秒
                 }, 66)
                 
                 this.priorityQueue.length > 0 && (e = !0);
                 this.processQueueForDownloading(this.priorityQueue);
             }
-            return e 
-        }else{//仅下载depthTex
-            this.tilePrioritizer.filterDepthTex(this.panos)
-        }
+            //return e 
+        } 
+        Potree.Common.intervalTool.isWaiting('filterDepthTex', ()=>{ 
+            this.tilePrioritizer.filterDepthTex(this.panos)//下载深度图
+        }, 77)
+        
+        
+         
         
     }
 

+ 28 - 9
src/custom/modules/panos/tile/TilePrioritizer.js

@@ -7,6 +7,11 @@ import math from '../../../utils/math.js'
 import Common from '../../../utils/Common.js' 
 import * as THREE from "../../../../../libs/three.js/build/three.module.js";
 
+import browser from '../../../utils/browser.js'
+
+
+
+
 
 let {DownloadStatus} = Potree.defines
 
@@ -80,12 +85,18 @@ export default class TilePrioritizer {//优先级处理序列
         return i
     } */
 
-    populateScoredPanos(e, t, i, dirs, a) {
+    populateScoredPanos(e, t, i, dirs, a, dontFilterDir) {
         i = i || [],
             i.length = 0;
-        var s = [Images360.filters.inPanoDirection(e.position, dirs, TilePrioritizer.DIRECTION_SCORE_STRICTNESS), Images360.filters.not(e)],
+            
+            
+        var s = [Images360.filters.not(e)],
             l = [Images360.scoreFunctions.distanceSquared(e), Images360.scoreFunctions.direction(e.position, dirs)],
             c = Common.sortByScore(t, s, l);  
+        if(!dontFilterDir){
+            s.push(Images360.filters.inPanoDirection(e.position, dirs, TilePrioritizer.DIRECTION_SCORE_STRICTNESS),)
+        }    
+            
         if (c)
             for (var h = 0; h < c.length && h < a; h++) {
                 var u = c[h].item;
@@ -203,15 +214,23 @@ TilePrioritizer.insertSortedPanoTile = function (e, t, pano, dir) {
 
 
 
-TilePrioritizer.prototype.filterDepthTex = function (panos ) {//下载depthTex
-    if(!Potree.settings.useDepthTex || !this.priorityCriteria.pano)return
+TilePrioritizer.prototype.filterDepthTex = function (panos ) {// 下载depthTex
+    if(!Potree.settings.useDepthTex || !this.priorityCriteria.pano || viewer.mainViewport.view.isFlying())return
     
     let cameraDirLocals = this.priorityCriteria.cameraDirs.vectorForward
-    let t = [] 
-    //获得视野范围内的邻近点位序列t
-    this.populateScoredPanos(this.priorityCriteria.pano, panos, t, cameraDirLocals , TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER);
-    t.filter(p=>!p.depthTex).slice(0, Potree.config.depTexDlCount).forEach(p=>p.loadDepthImg()) //add
+    let nearPanos = [] //用于获得邻近点位序列 
+    
+    this.populateScoredPanos(this.priorityCriteria.pano, panos, nearPanos, cameraDirLocals , Infinity, true);
+    
     
+     
+    let depTexDlCount = browser.isMobile() ? 1 : 2; 
+    
+    let loadingCount = panos.filter(p=>p.depthTexLoading).length
+    if(loadingCount<depTexDlCount){
+        nearPanos.filter(p=>!p.depthTex).slice(0, depTexDlCount-loadingCount).forEach(p=>p.loadDepthImg())  
+    }
+    this.nearPanos = nearPanos; 
 }
 
 
@@ -240,7 +259,7 @@ TilePrioritizer.prototype.filterAndPrioritize = function () {//挑选出优先
         this.populateScoredPanos(this.priorityCriteria.pano, panos, t, cameraDirLocals , TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER);
         
         
-        t.filter(p=>!p.depthTex).slice(0, Potree.config.depTexDlCount).forEach(p=>p.loadDepthImg()) //add
+        //t.filter(p=>!p.depthTex).slice(0, Potree.config.depTexDlCount).forEach(p=>p.loadDepthImg()) //add
         
         var s = this.baseSize //512
             ,

+ 13 - 20
src/custom/modules/route/RouteGuider.js

@@ -37,29 +37,22 @@ export class RouteGuider extends THREE.EventDispatcher{
         viewer.mapViewer.addEventListener('camera_changed', e => {
             if(!this.routeStart || !this.routeEnd) return   
             var camera = e.viewport.camera
-           
-            Common.intervalTool.isWaiting('routeCameraInterval', ()=>{ //延时update,防止卡顿
-                if(camera.zoom != zoom || !resolution.equals(e.viewport.resolution)){ 
-                    //console.log('updateMapArrows')
-                    this.updateMapArrows(true)
-                    zoom = camera.zoom; resolution.copy(e.viewport.resolution)  
-                    return true 
-                } 
-            }, browser.isMobile()?500:200)
+            if(camera.zoom != zoom || !resolution.equals(e.viewport.resolution)){
+                Common.intervalTool.isWaiting('routeCameraInterval', ()=>{ //延时update,防止卡顿
+                    if(camera.zoom != zoom || !resolution.equals(e.viewport.resolution)){ 
+                        //console.log('updateMapArrows')
+                        this.updateMapArrows(true)
+                        zoom = camera.zoom; resolution.copy(e.viewport.resolution)   
+                    } 
+                }, browser.isMobile()?500:200)
+            }
         })
          
-        let lastPos = new THREE.Vector3 
+       
         viewer.addEventListener('camera_changed', e => {
-            if(!this.routeStart || !this.routeEnd || !e.changeInfo.positionChanged) return
-            Common.intervalTool.isWaiting('routeCameraInterval2', ()=>{ //延时update,防止卡顿
-                let currPos = viewer.scene.getActiveCamera().position
-             
-                if(!currPos.equals(lastPos)){
-                    lastPos.copy(currPos)
-                    this.updateArrowDisplay() 
-                     
-                    return true 
-                }
+            if(!this.routeStart || !this.routeEnd || !e.changeInfo.positionChanged  ) return
+            Common.intervalTool.isWaiting('routeCameraInterval2', ()=>{ //延时update,防止卡顿  
+                this.updateArrowDisplay()  
             }, 1000)          
         })
         

+ 8 - 4
src/custom/modules/siteModel/BuildingBox.js

@@ -159,11 +159,15 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
         }
         if(this.buildParent)this.dontDragFloorHeight = this.buildParent.dontDragFloorHeight
         
+        
+        if(this.buildType != 'hole'){
+            this.box = this.createBox() //无论是否edit都绘制的原因:为了将在外的点移到在内,需要用mesh来获取intersect
+            this.add(this.box)
+            this.box.visible = !!this.ifDraw
+        }
+        
         if(this.ifDraw){ //只存储空间模型信息,不绘制
-            if(this.buildType != 'hole'){
-                this.box = this.createBox()
-                this.add(this.box)
-            }
+            
             {
                 this.lineMesh = LineDraw.createLine([],{color})
                 this.lineMesh.name = 'buildingLines'

+ 4 - 9
src/custom/modules/siteModel/SiteModel.js

@@ -90,14 +90,9 @@ var SiteModel = {
         
         {
             let updated = false
-            this.bus.addEventListener('updated',()=>{ 
-                updated = true
-                Common.intervalTool.isWaiting('siteModelUpdated', ()=>{ 
-                    if(updated){
-                        updated = false
-                        this.changedCallback() 
-                        return true
-                    }
+            this.bus.addEventListener('updated',()=>{  
+                Common.intervalTool.isWaiting('siteModelUpdated', ()=>{  
+                    this.changedCallback()  
                 },500)
             })
         }
@@ -141,7 +136,7 @@ var SiteModel = {
                     
                 }
                 force = false
-                return true 
+                 
             //}
         }
         

+ 2 - 2
src/custom/objects/TextSprite.js

@@ -79,8 +79,8 @@ export class TextSprite extends THREE.Object3D{
     update(){
         this.sprite.update()
     }
-    setVisible(v){
-        this.visible = v
+    setVisible(v){ 
+        Potree.Utils.updateVisible(this, 'setVisible', v)
     }
     setUniforms(name,value){
         this.sprite.setUniforms(name,value)

+ 4 - 2
src/custom/objects/tool/Measure.js

@@ -398,7 +398,9 @@ export class Measure extends ctrlPolygon{
             this.editStateTimer = setTimeout(()=>{
                 if(!this.isEditing){
                     this.dispatchEvent({type:'editStateChange',state:false})   
-                    this.setEdgesDisplay(false) 
+                    this.setEdgesDisplay(false)
+                    this.areaPlane && Potree.Utils.updateVisible(this.areaPlane, 'intersectLastLine',  true)
+                    this.areaLabel && Potree.Utils.updateVisible(this.areaLabel, 'intersectLastLine',  true) 
                 }
             },100)
         }else{
@@ -694,7 +696,7 @@ export class Measure extends ctrlPolygon{
             this.isNew || viewer.focusOnObject(this, 'measure')
         })
         Potree.Utils.setObjectLayers(areaLabel, 'measure' )
-        areaLabel.visible = false
+        areaLabel.setVisible(false) 
         
         return areaLabel;
         

+ 23 - 13
src/custom/objects/tool/MeasuringTool.js

@@ -321,7 +321,7 @@ export class MeasuringTool extends THREE.EventDispatcher{
         }
         
         if(state){
-            viewer.dispatchEvent({type:"measureMovePoint"})
+            viewer.dispatchEvent({type:"measureMovePoint"})//重新激活reticule状态
         }else{
             viewer.dispatchEvent({type:"endMeasureMove"})
         }
@@ -372,28 +372,29 @@ export class MeasuringTool extends THREE.EventDispatcher{
                     measure.edges[length-1].visible = true 
                     
                     measure.markers[length-1].visible = true;
-                    
-                    measure.editStateChange(true) //重新激活reticule状态
-                    
+                     
                     marker.isDragging = true 
                     measure.continueDrag(marker, e)    
                 } 
 				 
 			} else if (e.button === THREE.MOUSE.RIGHT ) { //触屏怎么取消?
-				if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话
-                else measure.continueDrag(null, e)     
+				if(e.pressDistance < Potree.config.clickMaxDragDis){//非拖拽的话
+                    var isIntersectSelf = measure.atPlane && measure.closed && !measure.isRect && measure.point2dInfo && measure.intersectSelf(measure.point2dInfo.points2d.slice(0,measure.point2dInfo.points2d.length-1))//检测除了最后一个点的相交情况
+                    if(!isIntersectSelf)end(e);
+                    else measure.continueDrag(null, e) 
+                }else measure.continueDrag(null, e)     
                  
 			}
 		};
 
 		let end = (e={}) => {//确定、结束 
             if(!measure.isNew )return
-              
+             
             if(args.minMarkers != void 0 ){
                 
                 if(!e.finish && measure.markers.length<=args.minMarkers ){//右键  当个数不够时取消
                     //this.viewer.scene.removeMeasurement(measure)
-                    //cancelFun && cancelFun()
+                    
                     //重新开始画
                     if(measure.markers.length>0){
                         measure.markers[0].removeEventListener('mousedown',end)
@@ -411,7 +412,9 @@ export class MeasuringTool extends THREE.EventDispatcher{
                     } */
                 } 
             }
-             
+            
+            
+            
             
             if (/* !e.finish&& */ measure.markers.length > args.minMarkers) {
                 measure.removeMarker(measure.points.length - 1); 
@@ -448,6 +451,13 @@ export class MeasuringTool extends THREE.EventDispatcher{
             
             viewer.inputHandler.dispatchEvent({type:'isMeasuring',  v:false, cause:'stopInsertion'}  ) 
             
+            
+            //var isIntersectSelf = measure.atPlane && measure.closed && !measure.isRect && measure.point2dInfo && measure.intersectSelf(measure.point2dInfo.points2d.slice(0,measure.point2dInfo.points2d.length-1))//检测除了最后一个点的相交情况
+            var isIntersectSelf = measure.atPlane && measure.closed && !measure.isRect && measure.point2dInfo && measure.intersectSelf(measure.point2dInfo.points2d)
+            if(isIntersectSelf) return cancelFun && cancelFun() //请求删除,不重建 
+            
+            
+            
             e.remove || callback && callback()  
             /* this.viewer.dispatchEvent({
                 type: 'finish_inserting_measurement',
@@ -460,17 +470,17 @@ export class MeasuringTool extends THREE.EventDispatcher{
         
         let Exit = (e)=>{//强制退出
         
-            if(e.measure && e.measure != measure){
+            if(e.measure && e.measure != measure || !viewer.scene.measurements.includes(measure) || !measure.isNew){
                 return;//若指定了退出的measure但和该measure不一致,就返回
-            }
-        
+            } 
             if(e.remove){
                 viewer.scene.removeMeasurement(measure)  
             }
             
+             
             measure.editStateChange(false)
             measure.cannotConfirmNormal = false  //一些dropMarker中的句子
-            measure.guideLine &&(measure.guideLine.visible = false)
+            measure.guideLine && (measure.guideLine.visible = false)
             /*
             if(this.viewer.inputHandler.drag && !e.remove ){//还未触发drop的话   
                   this.viewer.inputHandler.drag.object.dispatchEvent({  //这句会导致又增一个marker

+ 31 - 26
src/custom/objects/tool/ctrlPolygon.js

@@ -340,29 +340,30 @@ export class ctrlPolygon extends THREE.Object3D {
                     points2d = points.map(e=>e.clone().applyQuaternion(qua))  
                 } */
                 this.getPoint2dInfo(points)
-                
-                var isIntersectSelf = this.atPlane && this.closed && !this.isRect && this.intersectSelf(this.point2dInfo.points2d)//检测相交
-                if(isIntersectSelf){
-                    //not-allowed
-                    viewer.dispatchEvent({
-                        type : "CursorChange", action : "add",  name:"polygon_isIntersectSelf"
-                    })
-                    this.isIntersectSelf = true
-                    return
-                }else{
-                    this.isIntersectSelf = false
+                 
+                var isIntersectSelf = this.atPlane && this.closed && !this.isRect && this.point2dInfo && this.intersectSelf(this.point2dInfo.points2d)//检测相交
+                this.isIntersectSelf = isIntersectSelf
+                if(isIntersectSelf){//not-allowed
+                    if(!this.isNew && isIntersectSelf == 'lastLine') this.isIntersectSelf = 'all'  //已经画好了就不用特别对待最后一条线
+                    if(this.isIntersectSelf == 'lastLine'){ 
+                        Potree.Utils.updateVisible(this.areaPlane, 'intersectLastLine',  false)
+                        Potree.Utils.updateVisible(this.areaLabel, 'intersectLastLine',  false)
+                    }else{ 
+                        viewer.dispatchEvent({
+                            type : "CursorChange", action : "add",  name:"polygon_isIntersectSelf"
+                        }) 
+                        return  
+                    } 
+                } 
+                if(!this.isIntersectSelf){
+                    this.areaPlane && Potree.Utils.updateVisible(this.areaPlane, 'intersectLastLine',  true)
+                    this.areaLabel && Potree.Utils.updateVisible(this.areaLabel, 'intersectLastLine',  true) 
+                } 
+                if(!this.isIntersectSelf || this.isIntersectSelf == 'lastLine' ){
                     viewer.dispatchEvent({
                         type : "CursorChange", action : "remove",  name:"polygon_isIntersectSelf"
                     })
-                    /* this.facePlane && (this.point2dInfo = {
-                        originPoint0 ,
-                        points2d,
-                        quaInverse : qua.clone().invert()
-                    }) */
                 }
-                
-                
-                 
             }
             
             var showGuideLine = len>1 && (this.faceDirection || len > 3)
@@ -433,7 +434,7 @@ export class ctrlPolygon extends THREE.Object3D {
          
         
         if (e.button != THREE.MOUSE.RIGHT && (//右键click的话继续执行,因为会停止
-                this.isIntersectSelf && this.isNew //有线相交了
+                this.isIntersectSelf == 'all' && this.isNew //有线相交了
                 || this.isAtWrongPlace && this.isNew
                 || !e.isAtDomElement && this.isNew//如果是刚添加时在其他dom点击, 不要响应
                 ||  e.hoverViewport != viewer.mainViewport && this.unableDragAtMap //垂直的测量线不允许在地图上放点
@@ -528,13 +529,17 @@ export class ctrlPolygon extends THREE.Object3D {
                 var p3 =  points2d[j]
                 var p4 =  points2d[(j+1)%len] 
                 if(p1.equals(p2) || p3.equals(p4) || p1.equals(p3) || p2.equals(p3) || p1.equals(p4) || p2.equals(p4))continue 
-             
-                
+              
                 var line1 = [p1,p2]
                 var line2 = [p3,p4]
                 var intersect = math.isLineIntersect(line1, line2, false, 0.001)
                 if(intersect){
-                    return true
+                    if(i==len-1 || j==len-1){//最后一条线。如果还没绘制完,最后的线还没定下,可被允许继续绘制,但无法显示面积。
+                        return 'lastLine'
+                    }else{
+                        return 'all'
+                    }
+                     
                     break
                 } 
             }
@@ -676,13 +681,12 @@ export class ctrlPolygon extends THREE.Object3D {
             })
             viewer.dispatchEvent({type:'reticule_forbit', v:false})
             
-            this.markers.forEach(e=>e.isDragging = false )
+            this.markers.forEach(e=>e.isDragging = false ) 
         }
     }
     
     transformData(prop){
-        
-        
+         
         const pick = (defaul, alternative) => {
 			if(defaul != null){
 				return defaul;
@@ -714,6 +718,7 @@ export class ctrlPolygon extends THREE.Object3D {
     continueDrag(marker, e){
         let object = marker || e.drag.object
         object.isDragging = true 
+        this.editStateChange(true)
         var timer = setTimeout(()=>{//等 drag=null之后 //右键拖拽结束后需要重新得到drag 
             if(this.parent && object.isDragging){
                 //console.log('continueDrag')        

+ 5 - 3
src/custom/potree.shim.js

@@ -136,6 +136,7 @@ var texLoader = new THREE.TextureLoader()
     let gl_
     Features.EXT_DEPTH = { 
         isSupported: function (gl) { 
+        
             gl = gl || gl_
             gl_ = gl
             if(browser.detectIOS()){
@@ -719,8 +720,9 @@ Utils.isIntersectBox = function(object,  boxMatrix){//object是否有在box中
 
 Utils.getIntersect = function (camera, meshes, pointer, raycaster) {
     //获取鼠标和meshes交点
-    camera.updateMatrixWorld()
+    
     if(!raycaster){//getMouseIntersect
+        camera.updateMatrixWorld()
         raycaster = new THREE.Raycaster()
         var origin = new THREE.Vector3(pointer.x, pointer.y, -1).unproject(camera),
         end = new THREE.Vector3(pointer.x, pointer.y, 1).unproject(camera)
@@ -729,7 +731,7 @@ Utils.getIntersect = function (camera, meshes, pointer, raycaster) {
     } 
     
     meshes.forEach(e=>{ 
-        raycaster.layers.enable(math.getBaseLog(e.layers.mask,2)) 
+        raycaster.layers.enable(math.getBaseLog(2,e.layers.mask)) 
     }) 
     var n = raycaster.intersectObjects(meshes)
     if (0 === n.length) return null
@@ -1907,7 +1909,7 @@ LRU.prototype.freeMemory = function(){
     if (this.elements <= 1) {
         return;
     } 
-    let memoryRatio = browser.isMobile ? 2 : 5;
+    let memoryRatio = browser.isMobile() ? 2 : 5;
     //改成navvis的,使用pointBudget,否则四屏点云闪烁。 (似乎要比updateVisiblede的node时限制要宽些,作为缓存继续存着。否则会闪烁)
     let max =  viewer.viewports.length * memoryRatio * Potree.pointBudget 
        

+ 6 - 6
src/custom/settings.js

@@ -76,9 +76,9 @@ const config = {//配置参数   不可修改
     },
     
     transitionsTime:{
-        flyMinTime : 500 /* * 3 */,  // 毫秒/米
-        flytimeDistanceMultiplier: 150/* * 3  */,
-        panoToPanoMax: 2000/*  * 3 */, 
+        flyMinTime : 400  ,  // 毫秒/米
+        flytimeDistanceMultiplier: 130 ,
+        panoToPanoMax: 1800 , 
         flyIn:1000,
         flyOut:1000
     }
@@ -279,7 +279,7 @@ const config = {//配置参数   不可修改
     ,
     axis : {   'x':{color:'#ea3f3f'/* '#d0021b' */  /* 'red' */}, 'y':{ color:'#86c215' /* '#86c542'  *//* 'green' */},  'z': {color:'#3396f8'/* '#3399c8' */ /* 'blue' */}, 'xyz':{color:'#ccc',}},  
     
-    
+    shelterMargin:0.1,
     highQualityMaxZoom: 2,
     ultraHighQualityMaxZoom: 3,
     panoFieldRadius : 10, //当前位置多远范围内可以切全景模式
@@ -309,7 +309,7 @@ const config = {//配置参数   不可修改
         "deep orange": [ 255,61,0],
          
     },
-    depTexDlCount : browser.isMobile() ? 1 : 3,
+    
     depthTexUVyLimit: 0.141, // 在这个范围内是没有深度的,从图片算的0.14003, 设置为稍大于这个数值
 }
 
@@ -398,7 +398,7 @@ let settings = {//设置   可修改
     loadPointsWhenUnfocus:true, //页面unfocus时也仍在加载点云 */
    
     //initialShowPano:true
-    drawEntityData: false,
+    drawEntityData: false,  //包括marker、线
     
     zoomFromPointert:{//定点缩放(包括点云模式、全景模式、地图)
         whenPanos:true,

+ 29 - 4
src/custom/start.js

@@ -8,7 +8,6 @@ import math from './utils/math.js'
 import browser from './utils/browser.js' 
 import './three.shim.js'  
 import "./potree.shim.js"
-
  
 
 export function start(dom, mapDom, number ){ //t-Zvd3w0m
@@ -297,7 +296,7 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
     
     
     
-    Potree.loadDatasets(Potree.loadDatasetsCallback) 
+    number && Potree.loadDatasets(Potree.loadDatasetsCallback) 
      
      
 
@@ -347,6 +346,31 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
     }  
     
     
+    
+    
+    
+    //--------------------------------
+    if(!number){
+        Potree.settings.boundAddObjs = true
+        Potree.settings.intersectOnObjs = true
+        
+        // Load untextured bunny from ply
+        viewer.loadModel({
+            fileType:'ply',
+            url:Potree.resourcePath + "/models/indoor.ply",
+            name:'test',
+            },
+            (object)=>{
+                object.isModel = true
+                viewer.updateModelBound()
+            }
+        )  
+  
+    }
+    
+    
+    
+    
 }
  
  
@@ -685,7 +709,8 @@ export function mergeEditStart(dom){
                 model.position.copy(prop.position)
             }
             if(prop.rotation){
-                model.rotation.setFromVector3(prop.rotation) 
+                //model.rotation.setFromVector3(prop.rotation) 
+                model.rotation.copy(prop.rotation) 
             }
             if(prop.scale){
                 model.scale.set(prop.scale,prop.scale,prop.scale)
@@ -818,7 +843,7 @@ export function mergeEditStart(dom){
             
             
             
-        }else if(prop.type == 'obsg'){  //3d tiles  
+        }else if(prop.type == 'obsg' || prop.type == 'b3dm'){  //3d tiles  
         
             let callback = (object)=>{
                  

+ 88 - 4
src/custom/three.shim.js

@@ -211,11 +211,12 @@ THREE.EventDispatcher.prototype.traverse = function(callback){
     }
          
     const children = this.children;
+    if(children){
+        for ( let i = 0, l = children.length; i < l; i ++ ) {
 
-    for ( let i = 0, l = children.length; i < l; i ++ ) {
-
-        children[ i ].traverse( callback );
+            children[ i ].traverse( callback );
 
+        }
     }
 }
 
@@ -287,6 +288,89 @@ THREE.Material.prototype.setValues = function ( values ) {
 
 } 
 
+function ascSort( a, b ) {
+
+	return a.distance - b.distance;
+
+}
+
+
+function intersectObject( object, raycaster, intersects, recursive,  ignoreUnvisible ) {
+    
+    if(ignoreUnvisible && !object.visible)return  //add
+    
+	if ( object.layers.test( raycaster.layers ) ) {
+
+		object.raycast( raycaster, intersects );
+
+	}
+
+	if ( recursive === true ) {
+
+		const children = object.children;
+
+		for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+			intersectObject( children[ i ], raycaster, intersects, true, ignoreUnvisible);
+
+		}
+
+	}
+
+}
+
+THREE.Raycaster.prototype.intersectObject = function ( object, recursive, optionalTarget, ignoreUnvisible ) {
+
+    const intersects = optionalTarget || [];
+ 
+    intersectObject( object, this, intersects, recursive, ignoreUnvisible );
 
+    intersects.sort( ascSort );
 
- 
+    return intersects;
+
+} 
+
+THREE.Raycaster.prototype.intersectObjects = function ( objects, recursive, optionalTarget, ignoreUnvisible ) {//add ignoreUnvisible 跳过不可见
+
+    const intersects = optionalTarget || [];
+
+    if ( Array.isArray( objects ) === false ) {
+
+        console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
+        return intersects;
+
+    }
+
+    for ( let i = 0, l = objects.length; i < l; i ++ ) {
+
+        intersectObject( objects[ i ], this, intersects, recursive, ignoreUnvisible );
+
+    }
+
+    intersects.sort( ascSort );
+
+    return intersects;
+
+}
+
+
+
+THREE.Object3D.prototype.realVisible = function(){
+    let v = true 
+    let parent = this 
+    let lastParent
+    while(parent){
+        if(parent.visible === false){
+            v = false
+            break; 
+        }
+        lastParent = parent
+        parent = parent.parent
+    }
+     
+    if(v && !(lastParent instanceof THREE.Scene)){//已被删除
+        v = false
+    }   
+    return v                                
+}

+ 79 - 38
src/custom/utils/Common.js

@@ -4,30 +4,20 @@ import * as THREE from "../../../libs/three.js/build/three.module.js";
 import math from './math.js'
 
 var Common = {
-    /* sortByScore: function(list, request, rank){
-        var i = request ? Common.filterAll(list, request) : list
-        return 0 === i.length ? null : i = i.map(function(e) {
-            return {
-                item: e,
-                score: rank.reduce(function(t, i) {
-                    return t + i(e)
-                }, 0)
-            }
-        }).sort(function(e, t) {
-            return t.score - e.score;
-        })
-    }  */
-    
+     
     
     sortByScore: function(list, request, rank){
         var i = request ? Common.filterAll(list, request) : list
-        return 0 === i.length ? null : i = i.map(function(e) {
-            let scores = rank.map(function(f){return f(e)}) //add
-            
+        return 0 === i.length ? [] : i = i.map(function(e) {
+            let results = rank.map(function(f){return f(e)}) 
+            let scores = results.map(e=>e.score != void 0 ? e.score : e)
+            let logs = results.map(e=>e.log)
+              
             return {
                 item: e,
                 scores,
-                score: scores.reduce(function(t, i) {
+                logs,
+                score: scores.reduce(function(t, i) {//总分
                     return t + i
                 }, 0)
             }
@@ -52,7 +42,7 @@ var Common = {
     find : function(list, request, rank, sortByScore  ) { 
         if(sortByScore){
             var r = this.sortByScore(list, request, rank)
-            return  r && r[0] && r[0].item   
+            return r[0] && r[0].item   
         }else{
             var i = request ? Common.filterAll(list, request) : list
             return 0 === i.length ? null : (rank && rank.forEach(function(e) {
@@ -233,14 +223,14 @@ var Common = {
             console.log('isSame出现例外')
         } 
         
-    }
+    } 
     ,
     replaceAll : function (str, f, e) {
         //f全部替换成e
         var reg = new RegExp(f, "g"); //创建正则RegExp对象  
         return str.replace(reg, e);
     } 
-    ,
+    , 
     downloadFile : function(data, filename, cb) {
         var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
         save_link.href = data;
@@ -250,11 +240,34 @@ var Common = {
         save_link.dispatchEvent(event);
         cb && cb();
     }, 
+      
+    dealURL(url){
+        return this.replaceAll(url, "\\+", "%2B");// 浏览器似乎不支持访问带+的地址
+    },
+    
+    
+    getNameFromURL(url){
+        let get = (e)=>{
+            return e.split('/').pop()
+        }
+        if(url instanceof Array){
+            return url.map(e=>get(e))
+        }
+        return get(url)
+    },
+     
+    
+    
+    
+    
+    
+    //---------------------------
+    
     
     intervalTool:{  //延时update,防止卡顿
         list:[],
         
-        isWaiting:function(name, func, delayTime){
+        /* isWaiting:function(name, func, delayTime){
             if(!this.list.includes(name)){  //如果没有该项, 则开始判断
                 var needWait = func(); //触发了改变,则等待一段时间后再自动判断
                 if(needWait){
@@ -266,13 +279,31 @@ var Common = {
                     },delayTime)
                 } 
             }
-        },
-        /* wait:function(name, delayTime){
-            this.list.push(name);
-            setTimeout(()=>{
-                
-            },delayTime)
         }, */
+        
+        
+        
+        isWaiting:function(name, func, delayTime/* , autoCycle */){   
+            let item = this.list.find(e=>e.name == name)
+            if(!item){  //如果没有该项, 则加入循环
+                let ifContinue = func() 
+                item = {name} 
+                this.list.push(item);
+                    setTimeout(()=>{ 
+                        var a = this.list.indexOf(item);
+                        this.list.splice(a,1);
+                        if(item.requestUpdate || ifContinue )  this.isWaiting(name, func, delayTime) //循环
+                    },delayTime)
+                 
+            }else{//如果有该项,说明现在请求下一次继续更新
+                /* if(delayTime == 0){//想立刻更新一次
+                    func()
+                }else{ */
+                    item.requestUpdate = true
+                //}
+                
+            }
+        },
     }
     ,
     pushToGroupAuto : function(items, groups, recognizeFunction){//自动分组。 items是将分到一起的组合。items.length = 1 or 2. 
@@ -302,9 +333,9 @@ var Common = {
         }
     },
      
-
-
-    getBestCount : function(name, minCount=1,maxCount=6, durBound1 = 1.2, durBound2 = 8, ifLog){
+   
+ 
+    getBestCount : function(name, minCount=1,maxCount=6, durBound1 = 1.2, durBound2 = 10, ifLog){
        
        
         let timeStamp = performance.getEntriesByName("loop-start");
@@ -315,7 +346,7 @@ var Common = {
             count = Math.round(math.linearClamp(dur, durBound1,durBound2,   maxCount,  minCount))
              
             if(ifLog){//注意,console.log本身用时挺高, 降4倍时可能占用0.5毫秒
-               name && console.log(name,   count , ' ,dur:', dur.toFixed(3))
+               name   && count  && console.log(name,   count , ' ,dur:', dur.toFixed(3))
             } 
         }else{
             count = maxCount  //  ? 
@@ -331,14 +362,21 @@ var Common = {
         
         lists:[],
          
-        getSlice : function(name, items , {stopWhenAllUsed,  min=5,max=100, durBound1 , durBound2 }){
+        getSlice : function(name, items , {stopWhenAllUsed,  min=5,max=100, durBound1 , durBound2, useEquals , maxUseCount}){
+            if(items.length == 0 || 
+                ((maxUseCount = maxUseCount == void 0 ? Common.getBestCount(name, min,max , durBound1, durBound2 /*  , true  */  ) : maxUseCount), !maxUseCount)   //本次最多可以使用的个数
+            ){
+                return {list:[]}
+            }
+            
+             
             if(!this.lists[name]) this.lists[name] = {list:[] }
             //更新列表项目,但不变原来的顺序
-            let list = this.lists[name].list.filter(a=>items.some(item=>a.item == item))//去掉已经不在items里的项目
+            let list = this.lists[name].list.filter(a=>items.some(item=> useEquals ? a.item.equals(item) : a.item == item))//去掉已经不在items里的项目
             this.lists[name].list = list
               
             items.forEach(item=>{//增加新的项目。
-                if(!list.some(a=>a.item == item)){
+                if(!list.some(a=>useEquals ? a.item.equals(item) : a.item == item )){
                     list.push({item, count:0})
                 }
             })
@@ -346,7 +384,6 @@ var Common = {
             
             
             
-            const maxUseCount = Common.getBestCount(name, min,max , durBound1, durBound2 /*  , true  */  )  ; //本次最多可以使用的个数
              
             let unUsed = list.filter(e=>e.count == 0)//未使用的项目(count为0)优先
             let result = []
@@ -371,8 +408,12 @@ var Common = {
                 list.forEach(e=>e.count--) //复原,等待新的循环
             }
             
-             
-            
+            /* result.forEach((e,i)=>{//有重复的
+               if( result.slice(0,i).some(a=>a.equals(e)) || result.slice(i+1).some(a=>a.equals(e))  ) {
+                   console.log(e)
+               } 
+            })  */
+                        
             
             return {list:result  }
             

+ 9 - 6
src/custom/utils/transitions.js

@@ -120,7 +120,6 @@ easing.easeInSine = function(currentTime, startY, wholeY, duration) {
 }
 ,
 easing.easeOutSine = function(currentTime, startY, wholeY, duration) {// y' = S * PI / 2 / dur * cos(PI/2/dur * x)
-    console.log('easeOutSine')
     return wholeY * Math.sin(currentTime / duration * (Math.PI / 2)) + startY
 }
 ,
@@ -287,9 +286,9 @@ var lerp = {
     vector: function(t, i, f) {//xzw change, add f
         var n = t.clone();
         return i = i.clone(),
-        function(e) {
+        function(e, delta) {
             t.set(n.x * (1 - e) + i.x * e, n.y * (1 - e) + i.y * e, n.z * (1 - e) + i.z * e)
-            f && f(t,e);
+            f && f(t,e, delta);
         }
     },
     quaternion: function(t, i, f) {//xzw change, add f
@@ -347,7 +346,7 @@ var transitions = {
     funcs: [],
     counter: 0,
     uniqueID: 0,
-    start: function(func, duration, done, delay, ease, name, id, cancelFun) {
+    start: function(func, duration, done, delay, ease, name, id, cancelFun, ignoreFirstFrame=true) {
         return delay = delay || 0,
         this.funcs.push({
             func: func,
@@ -362,6 +361,8 @@ var transitions = {
             id: void 0 === id ? this.counter : id,
             paused: !1,
 			cancelFun : cancelFun,   //取消时执行的函数
+            updateCount:0,
+            ignoreFirstFrame,
         }),
         func(0, 16),
         this.counter += 1,
@@ -395,8 +396,8 @@ var transitions = {
     },
     update: function(e) {
         this.funcs.forEach(function(t) {
-            if (!(t.paused || (t.current += 1e3 * e,
-            t.current < 0)))
+            if(t.updateCount++ == 0 && t.ignoreFirstFrame) return //add start可能发生在一帧中任意时刻,而每次update的是在一帧中的固定时刻,所以从start到第一次update的时间并不是所传入的delta,该delta 是上一帧的update到这一帧的update的耗时。 故去掉了第一次的update,相当于延迟一帧再update.
+            if (!(t.paused || (t.current += 1e3 * e, t.current < 0))){
                 if (t.current >= t.duration && !t.cycling) {
                     var u = t.easing(1, 0, 1, 1);
                     t.func(u, 1e3 * e),
@@ -408,6 +409,8 @@ var transitions = {
                     r && (t.done && t.done(),
                     t.running = !1)
                 }
+                
+            }
         });
         var t = this.funcs.length;
         this.funcs = this.funcs.filter(function(e) {

+ 371 - 94
src/custom/viewer/ViewerNew.js

@@ -58,7 +58,7 @@ import {OBJLoader} from "../../../libs/three.js/loaders/OBJLoader.js";
 import {MTLLoader} from "../../../libs/three.js/loaders/MTLLoader.js";
 import {GLTFLoader} from  "../../../libs/three.js/loaders/GLTFLoader.js";
 import {Loader3DTiles} from '../../../libs/three.js/3dtiles/three-loader-3dtiles.esm.js';
-   
+import {PLYLoader} from "../../../libs/three.js/loaders/PLYLoader.js";
     
    
 import SSAARenderPass from "../materials/postprocessing/SSAARenderPass.js"
@@ -82,7 +82,7 @@ const manager = new THREE.LoadingManager();
 let loaders = {}
 
 let mapArea; 
-let blockedByIntersectHistory = new Map();
+let shelterHistory = []
 
 
 
@@ -124,12 +124,20 @@ export class Viewer extends ViewerBase{
         }
         {
             Potree.timeCollect = {
-                depthSampler : {minCount:20, median:0.5}, //median预置一个中等值,以防止cpu过低的设备首次卡顿  
-            }
-            for(let i in Potree.timeCollect){
-                Potree.timeCollect[i].measures = [];
-                Potree.timeCollect[i].sum = 0;
+                depthSampler : {minCount:400, median: 25}, //median预置一个中等值,以防止cpu过低的设备首次卡顿  
+                 
             }
+            
+            setTimeout(()=>{
+                for(let i in Potree.timeCollect){
+                    Potree.timeCollect[i].measures = [];
+                    Potree.timeCollect[i].sum = 0;
+                    Potree.timeCollect[i].start = true
+                }
+                /* setTimeout(()=>{
+                    console.log('timeCollect', Potree.timeCollect.depthSampler.median, Potree.timeCollect.depthSampler.ave, Potree.timeCollect.depthSampler.measures.length)
+                },10000) */
+            },2000)
         }
          
   
@@ -535,8 +543,8 @@ export class Viewer extends ViewerBase{
             loaders = {
                 objLoader : new OBJLoader( manager ),
                 mtlLoader : new MTLLoader( manager ),
-                glbLoader : new GLTFLoader(undefined, this.renderer, Potree.settings.libsUrl )
-             
+                glbLoader : new GLTFLoader(undefined, this.renderer, Potree.settings.libsUrl ),
+                plyLoader : new PLYLoader( manager ),
             } 
             //add test
             /* const environment = new RoomEnvironment();
@@ -713,24 +721,16 @@ export class Viewer extends ViewerBase{
             this.addEventListener('camera_changed', e => {
                 
                 if(e.viewport == this.mainViewport && (e.changeInfo.positionChanged || zoomLevel != this.images360.zoomLevel)){ 
-                    zoomLevel = this.images360.zoomLevel
-                    blockedByIntersectHistory.clear() //清空
-                    this.updateDatasetAt()  //更新所在数据集  
+                    zoomLevel = this.images360.zoomLevel  //对updateMarkerVisibles有影响
+                    //e.changeInfo.positionChanged && shelterHistory.clear() //清空
+                    e.changeInfo.positionChanged && this.updateDatasetAt()  //更新所在数据集  
                     
                     if(Potree.settings.ifShowMarker && Potree.settings.editType != 'merge'){
-                        updated = true
-                        
-                        Common.intervalTool.isWaiting('updateMarkerVisibles', ()=>{
-                            
-                            if(!updated)return
-                             
+                       
+                        Common.intervalTool.isWaiting('updateMarkerVisibles', ()=>{  
                             if(!this.mainViewport.view.isFlying() ){ 
-                                this.updateMarkerVisibles() 
-                                updated = false
-                            } 
-                            
-                            return true    
-                             
+                                this.updateMarkerVisibles()  
+                            }    
                         },500)
                     }
                 }
@@ -741,6 +741,25 @@ export class Viewer extends ViewerBase{
             })
             
             
+            
+            /* if(!Potree.Features.EXT_DEPTH.isSupported()){
+            
+                this.images360.addEventListener('endChangeMode',(e)=>{  
+                    if(e.mode == 'showPanos'){ 
+                        this.updateMarkerVisibles()
+                    } 
+                }) */
+                this.images360.addEventListener('getNeighbourAuto',(e)=>{  
+                    if(/* Potree.settings.displayMode == 'showPanos' &&  */e.panos.includes(this.images360.currentPano)){ 
+                        Common.intervalTool.isWaiting('updateMarkerVisibles', ()=>{  
+                            this.updateMarkerVisibles()   
+                        },500) 
+                    }
+                })
+                    
+            /* } */
+            
+            
         } 
         
         
@@ -748,17 +767,208 @@ export class Viewer extends ViewerBase{
 	}
     
     
-    ifPointBlockedByIntersect(pos3d, object){//点是否被遮挡
+     
+    
+    ifPointBlockedByIntersect(pos3d , panoId,  soon ){//点是否被遮挡
         let ifShelter
-        let shelter = blockedByIntersectHistory.get(object || pos3d);
-        if(shelter){
-            ifShelter = shelter.ifShelter
-        }else{
-            ifShelter = viewer.inputHandler.ifBlockedByIntersect(pos3d, 0.3, true)//由于热点都是使用点云得到的位置,所以判断遮挡时也用点云
-            blockedByIntersectHistory.set(object || pos3d, {ifShelter})
+        let now = Date.now()
+        let extraPanoId = panoId != void 0 
+        if(!this.shelterCount)return
+        
+        let history = shelterHistory.find(e=>e.pos3d.equals(pos3d)) 
+        let cameraPos = this.mainViewport.view.position.clone()
+        if(panoId == void 0){
+            if(this.images360.isAtPano(0.05)){
+                panoId = this.images360.currentPano.id
+            }
+        }
+        
+        
+        
+        if(history){ 
+            if(panoId != void 0){
+                ifShelter = history.panos[panoId];  
+            }else{
+                if(history.notAtPano.cameraPos && history.notAtPano.cameraPos.equals(cameraPos)){
+                    ifShelter = history.notAtPano.ifShelter
+                }
+            }
+            
+            let index = shelterHistory.indexOf(history)  //先取出,稍后放回
+            shelterHistory.splice(index, 1)
+            
+        }else{//新增
+            history = {pos3d,  panos:{}, notAtPano:{}} 
+             
+            const minCount = 100
+            if(shelterHistory.length > minCount){//去除最早的
+                let old 
+                while(old = shelterHistory[0], now - old.lastTime > 1000){//因为不知热点个数,所以需要加上时间限制,超过时间才能删。
+                    if(old == history || shelterHistory.length == minCount)break;
+                    shelterHistory.splice(0,1)
+                    //console.log('delete')
+                } 
+            } 
+        }  
+        
+        
+        if(ifShelter == void 0){ 
+            delete history.waitCompute
+        
+            if(this.mainViewport.view.isFlying()){ 
+                return useLastResult() 
+            }
+            
+            if(panoId != void 0){
+                let pano = this.images360.getPano(panoId)
+                if((soon || this.shelterCount.byTex<this.shelterCount.maxByTex) && pano.depthTex){
+                    ifShelter = !!viewer.inputHandler.ifBlockedByIntersect({pos3d, margin:Potree.config.shelterMargin, useDepthTex:true, pano }  ) 
+                    history.panos[panoId] = ifShelter
+                    this.shelterCount.byTex ++ ;
+                    //console.log('computeByTex direct', panoId, pos3d, ifShelter)
+                }else{
+                    //console.log('延迟tex',panoId, pos3d )
+                    history.waitCompute = {panoId,   forceGet:extraPanoId  }
+                    return useLastResult() 
+                }
+            }else{ 
+                if(/* history.ifShelter == void 0 || */ this.shelterCount.byCloud<this.shelterCount.maxByCloud){//弊端:第一个总是直接计算,后面的都是延后。但无法改进,因是一个个传进来的,无法预测。 
+                    ifShelter = !!viewer.inputHandler.ifBlockedByIntersect({pos3d, margin:Potree.config.shelterMargin, pickWindowSize:3}  ) 
+                    history.notAtPano = {cameraPos , ifShelter }
+                    this.shelterCount.byCloud ++ ; 
+                    //console.log('computeByCloud direct', pos3d.toArray())
+                }else{
+                    //console.log('延迟cloud' )
+                    history.waitCompute = {cameraPos}
+                    return useLastResult() 
+                }
+                
+            }
+             
         }
+        
+        
+        history.ifShelter = ifShelter
+        history.lastTime = now
+        shelterHistory.push(history) //最新使用的保持在最后一个,使队列按照从旧到新排列
+        
+        
+        
+        
+        function useLastResult(){//暂时先用上一次的值
+            shelterHistory.push(history)
+            history.lastTime = now
+            return history.ifShelter
+        }
+         
+        
         return ifShelter 
     }
+     
+    
+    
+    
+    computeShelter(){
+        //先算用深度图的,然后再点云;
+        
+        let depthTiming = Potree.timeCollect.depthSampler.median 
+        let byTex=0,  byCloud=0;
+        let len = shelterHistory.length;
+        let waitCloud = []
+        let max = this.mainViewport.view.isFlying() ? 1 : Math.min(1/depthTiming, 10); //起飞时lastFrameChanged还是false,所以不用lastFrameChanged
+        let maxTexCount = Common.getBestCount('shelterMaxDepthSample', 1,  max,   1,  13   /*  ,true */    ) 
+        
+        
+        
+        for(let i=len-1; i>=0; i--){
+            let history = shelterHistory[i];
+            
+            if(history.waitCompute){
+                if(history.waitCompute.panoId != void 0){
+                     if(!history.waitCompute.forceGet && (history.waitCompute.panoId != this.images360.currentPano.id || !this.images360.isAtPano(0.1))){
+                         delete history.waitCompute //取消计算
+                     }else{
+                         if(this.images360.currentPano.depthTex){ 
+                             if(byTex >= maxTexCount)break
+                             
+                             byTex ++
+                             let ifShelter = !!viewer.inputHandler.ifBlockedByIntersect({pos3d:history.pos3d, margin:Potree.config.shelterMargin, useDepthTex:true }  ) 
+                             history.panos[this.images360.currentPano.id] = ifShelter 
+                             history.ifShelter = ifShelter
+                             delete history.waitCompute  
+                             //console.log('补1', history.pos3d.toArray())
+                         }else{
+                             if(this.images360.currentPano.pointcloud.hasDepthTex){
+                                 //先等待加载完深度图
+                             }else{
+                                waitCloud.push(history)
+                             }
+                         }
+                     }
+                }else{
+                    waitCloud.push(history)
+                }
+            }  
+        }
+        
+        let maxCloudCount
+        if(byTex < maxTexCount && waitCloud.length){
+            maxCloudCount = this.lastFrameChanged ? Common.getBestCount('shelterMaxCloud', 0, 2,  4,  8    /*  ,true   */   ) : 5;
+            let waitCloud2 = []
+            if(maxCloudCount){
+                for(let i=0; i<waitCloud.length; i++){ 
+                    
+                    let history = waitCloud[i];
+                    if(history.waitCompute.cameraPos){
+                        if(!viewer.mainViewport.view.position.equals(history.waitCompute.cameraPos)){
+                            delete history.waitCompute 
+                            //console.log('delete history.waitCompute', history)
+                            continue; //取消计算
+                        }else{
+                            waitCloud2.push(history) 
+                        }
+                    }else{
+                        waitCloud2.push(history) 
+                    } 
+                }
+             
+            
+            
+            
+                let list = waitCloud2.map(e=>e.pos3d) 
+                let result = Common.batchHandling.getSlice('shelterByCloud', list, {maxUseCount:maxCloudCount,useEquals:true, stopWhenAllUsed:true} ) //iphonex稳定后大概在7-10。  
+                //list.length>0 && console.log('list',list, maxCloudCount)
+                
+                result.list.forEach(e=>{
+                    let history = waitCloud2.find(a=>a.pos3d.equals(e))
+                     
+                    let ifShelter = !!viewer.inputHandler.ifBlockedByIntersect({pos3d:history.pos3d, margin: Potree.config.shelterMargin , pickWindowSize:3}  ) 
+                    
+                    if(history.waitCompute.cameraPos){
+                        history.notAtPano = {cameraPos: history.waitCompute.cameraPos , ifShelter }
+                    }else{
+                        history.panos[this.images360.currentPano.id] = ifShelter 
+                    }
+                    history.ifShelter = ifShelter
+                    byCloud++
+                    //console.log('补2',  history.pos3d.toArray()) 
+                    delete history.waitCompute
+                })
+            
+            } 
+            
+        }
+        
+        
+        if(byTex || byCloud){
+             //console.log('shelterComputed',byTex,byCloud, maxTexCount, maxCloudCount)
+             Common.intervalTool.isWaiting('shelterComputed', ()=>{  
+                //console.log('shelterComputed update')
+                this.dispatchEvent('shelterComputed')  
+            },340)
+        }
+        
+    }
     
     updateDatasetAt(force){//更新所在数据集
     
@@ -780,9 +990,9 @@ export class Viewer extends ViewerBase{
                     this.dispatchEvent({type:'pointcloudAtChange',pointclouds:at}) 
                 }
                 force = false 
-                return true 
-            //}
-        }            
+                 
+            }
+        //}            
         if(force)fun()
         else Common.intervalTool.isWaiting('atWhichDataset', fun , 300)  
         
@@ -799,22 +1009,29 @@ export class Viewer extends ViewerBase{
     
     updateMarkerVisibles(){//限制显示的marker个数,因镜头内marker多的时候可能会卡 
         if(!Potree.settings.ifShowMarker)return
-        
+       
         
         const minRadius = 8 * this.images360.zoomLevel,  //当视线垂直于marker时的最小可见距离,此范围内可见的pano绝对可见
             maxRadius = 50 * this.images360.zoomLevel,    //当视线垂直于marker时的最大可见距离,此范围外绝对不可见
             hopeCount = browser.isMobile() ? 8 : 15   //期望达到的真实可见的marker数
-            
-        let panoMap = new Map
         
-        let set = ()=>{
+        
+        let sheltered = (pano)=>{
+            if(/* Potree.settings.displayMode == 'showPanos' && !Potree.Features.EXT_DEPTH.isSupported() && */this.images360.isAtPano() && !this.mainViewport.view.isFlying()){
+                return !this.images360.currentPano.neighbours.includes(pano) && this.images360.currentPano != pano //起初因不支持EXT_DEPTH时无法用depthTex遮住marker, 后为了减少绘制,都判断
+            } 
+        }
+        
+        let panoMap = new Map //先记录想要设置为可见的
+        
+        let set = ()=>{//最后确定设置
             let count = 0
             viewer.images360.panos.forEach(pano=>{
                 let v = panoMap.get(pano).visible
                 v && count++
                 Potree.Utils.updateVisible(pano.marker, 'limitMarkerShow', v )  
             })
-            //console.log('marker显示个数', count)
+            //console.log('updateMarkerVisibles marker显示个数', count)
         }
         let isWithinDis = (pano,maxDis)=>{//是否marker到相机的距离 没有超出可视距离。可视距离考虑上倾斜角,倾斜越大可视距离越短
             let camPos = viewer.mainViewport.camera.position
@@ -824,24 +1041,25 @@ export class Viewer extends ViewerBase{
             return o.dis < maxDis * o.sin 
         }
         
+        
         viewer.images360.panos.forEach(pano=>{//minRadius内的记录为可见 
             let o = {}           
             panoMap.set(pano, o)
-            if(pano.visible && isWithinDis(pano, minRadius)){
+            if(pano.visible && !sheltered(pano) && isWithinDis(pano, minRadius)){
                 o.visible = true; 
             }  
         })
-        //真正可见的要过滤本来pano就不可见的
+        //不超过hopeCount的话,可以直接确定设置
         if(viewer.images360.panos.filter(pano=> panoMap.get(pano).visible ).length >= hopeCount)return set()
         
         //距离超过maxRadius就绝对不可见
-        let insideOutCirle = viewer.images360.panos.filter(pano=> pano.visible && isWithinDis(pano, maxRadius))
+        let insideOutCirle = viewer.images360.panos.filter(pano=> pano.visible && !sheltered(pano) && isWithinDis(pano, maxRadius))
         if(insideOutCirle.length <= hopeCount){
             insideOutCirle.forEach(pano=>panoMap.get(pano).visible = true )
             return set()
         }            
         
-        //数量超过minCount时,根据距离排序
+        //数量超过hopeCount时,根据距离排序
         insideOutCirle.sort((a,b)=>{return panoMap.get(a).dis - panoMap.get(b).dis  }) 
         let slice = insideOutCirle.slice(0,hopeCount)
         slice.forEach(pano=>panoMap.get(pano).visible = true )
@@ -994,6 +1212,7 @@ export class Viewer extends ViewerBase{
                 else {
                     this.removeEventListener('camera_changed',test)
                     console.log('testPointcloudsMaxLevel结束')
+
                 }
                 
             }, count<10 ? 250 : 500) 
@@ -2235,7 +2454,11 @@ export class Viewer extends ViewerBase{
 	update(delta, timestamp){
         
 		viewer.addTimeMark('update','start')
-
+        TWEEN.update(timestamp);
+        transitions.update(delta);//写在开头,因为这时候最为固定,计时准确
+        
+        
+        
 		this.dispatchEvent({
 			type: 'update_start',
 			delta: delta,
@@ -2389,8 +2612,7 @@ export class Viewer extends ViewerBase{
 			}
 		}
 
-		TWEEN.update(timestamp);
-        transitions.update(delta);
+		
         this.transformationTool.update();
         
  	
@@ -2789,7 +3011,7 @@ export class Viewer extends ViewerBase{
             if(view.render){ 
                 if(!view.render($.extend({}, params, {
                     renderer:this.renderer,   clear:this.clear.bind(this), resize:null,
-                    renderBG:this.renderBG.bind(this),  force:!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
+                    renderBG:this.renderBG.bind(this),  force:true  //viewer content_change时map也直接渲染吧     //!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
                 })))return
             }else{ 
                 this.clear(params)  
@@ -2839,7 +3061,7 @@ export class Viewer extends ViewerBase{
         
         
         this.renderer.setRenderTarget(null)
-        this.needRender = false
+        
         
         viewer.scene.pointclouds[0] && this.addFakeMeasure('visibleNodes', viewer.scene.pointclouds[0].visibleNodes.length )// 
         this.addFakeMeasure('numVisiblePoints', Potree.numVisiblePoints/100000)//十万  numVisiblePoints和帧率成反比(若每一帧都render的话),和render用时成正比 (y=kn+b)。但visibleNodes个数也影响,多的话也更卡。visibleNodes和numVisiblePoints不成正比,少的visibleNodes可能numVisiblePoints多
@@ -3061,6 +3283,7 @@ export class Viewer extends ViewerBase{
         if(vrActive){
             this.renderVR();
         }else{ 
+            let specialRender =  !!params.viewports 
             let viewports = params.viewports || this.viewports
         
             if(!this.needRender){
@@ -3068,15 +3291,15 @@ export class Viewer extends ViewerBase{
             }
             viewports = viewports.filter(v=>v.active) 
             if(viewports.length > 0){   
-                params.viewports = viewports 
-            
+                params.viewports = viewports  
                 if(this.outlinePass.selectedObjects.length && this.outlinePass.edgeStrength > 0 && !params.screenshot){  
                     let scenes = this.inputHandler.interactiveScenes.concat(this.scene.scene).concat(viewer.scene.scenePointCloud) 
                     this.composer.render(scenes, null, this.viewports, this.renderDefault.bind(this));  
                 }else{  
                     this.renderDefault(params);
                 } 
-            }            
+            } 
+            if(!specialRender) this.needRender = false            
         }
 		viewer.addTimeMark('render','end')
 	} 
@@ -3333,7 +3556,7 @@ export class Viewer extends ViewerBase{
         let target  = new THREE.Vector3,  //相机focus的位置
             position = new THREE.Vector3, //相机最终位置
             dis;                          //相机距离目标
-        duration = duration == void 0 ? 1500 : duration;      
+        duration = duration == void 0 ? 1200 : duration;      
         let camera = viewer.scene.getActiveCamera()
         let cameraPos = camera.position.clone()
          
@@ -3499,12 +3722,12 @@ export class Viewer extends ViewerBase{
                 
                 //if(o.checkIntersect){
                     let checkIntersect = ( )=>{ 
-                        let intersect = this.inputHandler.ifBlockedByIntersect(position, null , true, target)// 不一定准确
+                        let intersect = this.inputHandler.ifBlockedByIntersect({pos3d:position, cameraPos: target})// 不一定准确
                         if(intersect){ 
                             let blockCount = 0, unblockCount = 0, visi;
                             for(let i=0;i<object.points.length;i++){ //如果顶点超过一半不可见,就要更改位置
                                 let p = object.points[i]
-                                let blocked = this.inputHandler.ifBlockedByIntersect(p, 0.3 , true, position, 4);
+                                let blocked = this.inputHandler.ifBlockedByIntersect({pos3d:p, margin:0.3 , cameraPos:position, pickWindowSize:4});
                                 if(blocked){
                                     blockCount ++;
                                     if(blockCount / object.points.length >= 0.5){
@@ -3535,7 +3758,7 @@ export class Viewer extends ViewerBase{
                                     let position1 = position.clone()
                                     let dir = new THREE.Vector3().subVectors(position, target) 
                                     position.copy(target).sub(dir) 
-                                    let intersect2 = this.inputHandler.ifBlockedByIntersect(position, null , true, target)// 不一定准确
+                                    let intersect2 = this.inputHandler.ifBlockedByIntersect({pos3d: position,  cameraPos:target})// 不一定准确
                                     if(intersect2){
                                         if(intersect2.distance < intersect.distance ){
                                             position.copy(position1)//恢复
@@ -3594,6 +3817,7 @@ export class Viewer extends ViewerBase{
                 this.mapViewer.moveTo(target.clone(), null, duration)
             }
             
+           
             if(Potree.settings.displayMode == 'showPointCloud'){ 
                 if(o.dontChangePos){
                     position.copy(cameraPos)
@@ -3603,30 +3827,56 @@ export class Viewer extends ViewerBase{
                     if(o.dontLookUp && dir.z<0)  dir.z *= -1
                     position.copy(target).add(dir.multiplyScalar(dis))
                 } 
-                /* if(o.checkIntersect){//识别被点云遮住的话 
-                    let ifShelter 
-                    
-                    while(1){
-                        ifShelter = this.inputHandler.ifBlockedByIntersect(target, o.checkMargin, true, position)  
-                        if(ifShelter){
-                            if(dis > 0.5){ 
-                                dis -- 
-                                dir.dot(ifShelter.normal)>0 ? dir.copy(ifShelter.normal).negate() : dir.copy(ifShelter.normal); 
-                                position.copy(target).add(dir.multiplyScalar(dis))
+                
+                if(o.sameFloor){//需要在同一楼层
+                    let atFloor = this.modules.SiteModel.pointInWhichEntity(target, 'floor')
+                    if(atFloor){
+                        let camFloor = this.modules.SiteModel.pointInWhichEntity(position, 'floor')
+                        if(camFloor != atFloor){
+                            let raycaster = new THREE.Raycaster();
+                            let origin = target
+                            let dir = new THREE.Vector3().subVectors( position, target ).normalize()
+                            raycaster.set(origin, dir) 
+                            let intersect = Potree.Utils.getIntersect(null, [atFloor.box], null, raycaster) 
+                            if(intersect){
+                                let dis = THREE.Math.clamp(intersect.distance - 0.2,  camera.near, intersect.distance)
+                                position.addVectors(origin, dir.multiplyScalar(dis)) 
+                                console.log('移动到楼层')    
+                            }else{
+                                console.error('?no intersect?')
                             }
-                        }  
+                           
+                        }                           
                     }
-                } */
+                }
+                
+                
+                if(o.checkIntersect){//识别被点云遮住的话 
+                    let intersect  //反向查找从target到相机的第一个intersect
+                    intersect = this.inputHandler.ifBlockedByIntersect({pos3d:position, margin:0,  cameraPos:target}   /* {pos3d:target, margin: 0.2,  cameraPos:position} */)  
+                     
+                    if(intersect){ 
+                        position.copy(intersect.location) 
+                        console.log('移近')    
+                    }  
+                     
+                }  
             
             }else if(Potree.settings.displayMode == 'showPanos'){
                 let pano = viewer.images360.fitPanoTowardPoint({
                     point : target,    
                     dir :  this.scene.view.direction, //尽量不改相机方向,避免镜头晃动
-                    checkIntersect: true,
+                    checkIntersect: o.checkIntersect, sameFloor:o.sameFloor,
                     bestDistance,   maxDis:  o.maxDis   //越近越好,但不要太近,bestDistance左右差不多
                 })
+                let result = {promise:deferred.promise() }    
+                if(pano && pano.msg){
+                    pano = pano.pano
+                    result.msg = pano.msg 
+                }
                 pano && viewer.images360.flyToPano({pano, target, duration, deferred, dontMoveMap:true , basePanoSize:o.basePanoSize })
-                return {promise:deferred.promise() }               
+                 
+                return result           
             }
         }else if(object.boundingBox && type == 'boundingBox'){//使屏幕刚好看全boundingBox
             target = object.boundingBox.getCenter(new THREE.Vector3)
@@ -3694,7 +3944,7 @@ export class Viewer extends ViewerBase{
                 Images360.scoreFunctions.distanceSquared({position: center}) 
             ]
             let r = Common.sortByScore(pointcloud.panos, request, rank);
-            if(r && r.length){
+            if(r.length){
                 return r[0].item
             }
         }
@@ -3746,7 +3996,7 @@ export class Viewer extends ViewerBase{
  
     addTimeMark(name, type){
         let record = Potree.timeCollect[name] 
-        let needRecord = record && record.measures.length < record.minCount
+        let needRecord = record && record.start && record.measures.length < record.minCount
              
         if(needRecord || Potree.measureTimings){
             performance.mark(name+"-"+type)
@@ -3756,7 +4006,7 @@ export class Viewer extends ViewerBase{
                 if(needRecord){ 
                     record.measures.push( measure.duration ) 
                     record.sum += measure.duration;
-                    record.mean = record.sum / record.measures.length;  
+                    record.ave = record.sum / record.measures.length;  
                     record.measures.sort( (a, b) => a - b   ); 
                     record.median = record.measures[parseInt(record.measures.length / 2)] 
                    
@@ -3913,15 +4163,21 @@ export class Viewer extends ViewerBase{
 		}
        
         performance.mark('loop-start') ;// 无论有没有reportTimings都要获取,因为getBestCound需要
+        
+        let depthTiming = Potree.timeCollect.depthSampler.median 
+        this.shelterCount = {byTex:0, byCloud:0,   maxByTex: THREE.Math.clamp(0.2/depthTiming, 1, 10), maxByCloud:0   } //清空 因ifPointBlockedByIntersect可能在任何时候触发,所以需要一开始就定义这个,且每次计算最大可计算次数太麻烦了就定义一个吧。
+        
+
 
-		this.update(this.clock.getDelta(), timestamp);
+        let deltaTime = this.clock.getDelta()
+		this.update(deltaTime, timestamp);
         this.magnifier.render();
         this.render(); 
          
-         
+        
         this.objs.children.forEach(e=>{
-            if(e.name == '3dTiles'){
-                e.runtime.update(this.clock.getDelta(), this.renderer, this.mainViewport.camera)
+            if(e.fileType == '3dTiles'){
+                e.runtime.update(deltaTime, this.renderer, this.mainViewport.camera)
             }
         }) 
          
@@ -3944,9 +4200,8 @@ export class Viewer extends ViewerBase{
         //-------------
         this.images360.tileDownloader.update() 
         this.images360.panoRenderer.update()
-       
-        
-        
+        this.images360.getNeighbours()
+        this.computeShelter()
         //-------------
 		if(this.stats){
 			this.stats.end();
@@ -3956,7 +4211,7 @@ export class Viewer extends ViewerBase{
         viewer.addTimeMark('loop','end')
         viewer.addTimeMark('loopWaitNext','start')
 		this.resolveTimings(timestamp, Potree.measureTimings);
-
+        //Potree.measureTimings = 1
 	}
 
 	postError(content, params = {}){
@@ -4183,8 +4438,8 @@ export class Viewer extends ViewerBase{
             }
         ])
         //若要求更准确的话,可以使用ifContainsPoint判断一下是否在bound中
-        let r = result && result[0];
-        return r.score > 1 ? result[0].item : null  
+        let r = result[0];
+        return r && r.score > 1 ? result[0].item : null  
     }
     
     /* addObjectTest1(){//加水管
@@ -4257,7 +4512,7 @@ export class Viewer extends ViewerBase{
      */
 
     async loadModel(fileInfo, done, onProgress_, onError){ 
-        
+        console.log('开始加载',  Common.getNameFromURL(fileInfo.name) )
     
         let boundingBox = new THREE.Box3()
         /* if(!Potree.settings.boundAddObjs){
@@ -4282,15 +4537,13 @@ export class Viewer extends ViewerBase{
                 return
             }
         }
-        
-        
+        fileInfo.url = Common.dealURL(fileInfo.url) //去除'+'
+        fileInfo.loadStartTime = Date.now()   
         //let fileType =  fileInfo.tilesUrl ? '3dTiles' :  fileInfo.objurl ? 'obj' : 'glb'
          
         
         let loadDone = (object,   fileInfo_    /* , total, url */)=>{ 
-            /* let weight = Math.round((total / 1024 / 1024) * 100) / 100;
-            url && console.log(url.split('/').pop() ,  '加载完毕, 模型数据量:' + weight + 'M')
-             */
+            
             fileInfo_ = fileInfo_ || fileInfo
             if(fileInfo_.parentInfo){
                 object.name = fileInfo_.name   
@@ -4304,6 +4557,7 @@ export class Viewer extends ViewerBase{
             }
             
             object.name = fileInfo_.name != void 0 ? fileInfo_.name : fileInfo_.type
+            object.fileType = fileInfo_.fileType
             object.boundingBox = boundingBox  //未乘上matrixWorld的本地boundingBox
             //object.scale.set(1,1,1);//先获取原始的大小时的boundingBox
             object.opacity = 1 //初始化 记录
@@ -4312,6 +4566,11 @@ export class Viewer extends ViewerBase{
             if(fileInfo_.id != void 0)object.dataset_id = fileInfo_.id
             
             
+            fileInfo_.loadCostTime = Date.now() - fileInfo_.loadStartTime
+            /* let weight = Math.round((total / 1024 / 1024) * 100) / 100;*/
+            console.log( '加载完毕:', Common.getNameFromURL(fileInfo_.name),  '耗时(ms)', fileInfo_.loadCostTime, /* 模型数据量:' + weight + 'M' */)
+             
+             
             if(fileInfo_.fileType == '3dTiles'){
                 let tileset = object.runtime.getTileset()
                
@@ -4345,12 +4604,12 @@ export class Viewer extends ViewerBase{
                 
                 json.root.refine = 'ADD';
                 json.refine = 'ADD';
-            }else{
+            }else {
                 Potree.Utils.setObjectLayers(object,'model')  
                 
                 
                 object.traverse( ( child )=>{ 
-                    if ( child instanceof THREE.Mesh ) { 
+                    if ( child instanceof THREE.Mesh || child instanceof THREE.Points ) { 
                         child.renderOrder = Potree.config.renderOrders.model;
                         if(Potree.settings.boundAddObjs){
                             child.geometry.computeBoundingBox()
@@ -4431,6 +4690,19 @@ export class Viewer extends ViewerBase{
                 loadDone(gltf.scene/* , total, fileInfo.url */) 
             }, onProgress, onError)
             
+        }else if(fileInfo.fileType == 'ply'){
+            loaders.plyLoader.load( fileInfo.url, (geometry) => {
+                let object
+                console.log('ply加载完毕', geometry)
+                if(!geometry.index){//点云 
+                    object = new THREE.Points(geometry, new THREE.PointsMaterial({vertexColors:true, size:0.02})) 
+                    //141M的点云,intersect费时300ms以上
+                }else{//mesh
+                    object = new THREE.Mesh(geometry) 
+                }
+                loadDone(object)
+            })
+            
         }else if(fileInfo.fileType == '3dTiles'){
              
             let result = await Loader3DTiles.load({
@@ -4440,9 +4712,11 @@ export class Viewer extends ViewerBase{
                 options: {
                     //dracoDecoderPath: '../utils/loaders/DRACOLoader/draco',
                     //basisTranscoderPath: '../utils/loaders/KTX2Loader/basis',
-                    maximumScreenSpaceError: 48,
-                    maxDepth: 100,
-                    showAllTile: true,
+                    maximumScreenSpaceError: 50,
+                    maxDepth: 100, 
+                    maximumMemoryUsage: 700, //缓存大小。 若太小,密集的tile反复加载很卡
+                    //debug:true,
+                    parent: this.scene.scene
                 },
             })
             console.log(result)
@@ -4450,7 +4724,7 @@ export class Viewer extends ViewerBase{
             loadDone(result.model/* , null, fileInfo.url */)  
               
 
-              
+               
             let loaded = false
             let tileset = result.runtime.getTileset()
             tileset.addEventListener('endTileLoading', function (data) {//Tileset3D
@@ -4647,6 +4921,9 @@ export class Viewer extends ViewerBase{
         }) 
  
      
+     
+     
+        
         
         /* this.images360.addEventListener('flyToPano' ,(e)=>{//飞之前
             if(Potree.settings.displayMode != 'showPanos') return

+ 103 - 43
src/custom/viewer/map/Map.js

@@ -39,7 +39,12 @@ function defineLocalProj(locationLonLat){
 }
  
 
-
+const getSid = (function(){
+    let sid = 0
+    return function(){
+        return sid++
+    }
+})()
 
 //高德坐标拾取工具 : https://lbs.amap.com/tools/picker
  
@@ -436,6 +441,93 @@ export class TiledMapBase extends THREE.EventDispatcher{
     
 }
 
+
+
+
+let maxLoading = 3 
+let loadFailCount = 0
+const waitQueue = [] //等待加载的
+const loadDone = (tile, success)=>{
+    tile.map.mapLayer.loadingInProgress--
+    tile.loading = false
+     
+    let next = waitQueue[0]
+    if(next){
+        addLoadTile(next)
+    }else{
+        if(tile.map.mapLayer.loadingInProgress == 0){
+            tile.map.mapLayer.dispatchEvent('loadDone') 
+        }
+    }
+    tile.mesh && (tile.mesh.material.needsUpdate = true)
+    
+}
+let lastTile 
+function addLoadTile(tile){
+    
+    if(tile.map.mapLayer.loadingInProgress < maxLoading){
+        if(!tile.mesh)return;  //有时候会遇到这种情况, 为什么没有被cancelLoad呢?
+
+        
+        tile.map.mapLayer.loadingInProgress ++
+        //console.log('addLoadTile', tile.id, tile.texURL )
+        
+         
+        tile.loading = true
+        let index = waitQueue.indexOf(tile);
+        index > -1 && waitQueue.splice(index,1)
+        
+        
+        let tex = tile.mesh.material.map = texLoader.load(tile.texURL, (tex)=>{      //如果一直加载不了会影响其他的加载,如google地图没有vpn会使全景图一直加载不了
+            if(tile.mesh){//如果还要显示的话
+                tile.textureLoaded = true
+                tile.mesh.material.opacity = 1  
+                tile.map.mapLayer.viewer.mapChanged = true
+                tile.map.mapLayer.needUpdate = true //表示还要继续update(以removeChildren)
+                
+                if(tile.map instanceof TiledMapOpenStreetMap){
+                    maxLoading = browser.isMobile() ? 5 : 10; 
+                }
+                
+            }else{
+                tex.dispose()   
+            } 
+            loadDone(tile, true)
+        } , void 0, (()=>{//error
+            tile.textureLoaded = !0
+            if(tile.mesh){ 
+                tile.mesh.material.dispose() 
+                tile.mesh.material = errorMaterial 
+                tile.map.mapLayer.viewer.mapChanged = true 
+            } 
+            loadDone(tile, false)
+            loadFailCount ++ ;
+            
+            if(tile.map instanceof TiledMapOpenStreetMap && Potree.settings.mapCompany == 'google' && loadFailCount > 3){//极有可能没有vpn为了防止影响到其他资源加载,减少加载的个数
+                maxLoading = 2;
+            }
+            
+        }))  
+        tex.anisotropy = 0 
+        tex.generateMipmaps = !1 
+        tex.minFilter = THREE.LinearFilter 
+        tex.magFilter = THREE.LinearFilter 
+          
+    }else{
+        waitQueue.includes(tile) || waitQueue.push(tile) 
+    }
+    
+}
+function cancelLoad(tile){//如果等待加载,但还没开始加载,取消加载
+    ///* tile.texURL.includes('testdata') &&  */console.log('cancelLoad', tile.id, tile.loading)
+    if(!tile.loading){ 
+        let index = waitQueue.indexOf(tile);
+        index > -1 && waitQueue.splice(index,1) 
+    }
+     
+}
+
+
 export class MapTile{
     constructor(map,  e, n, parent, name){
         this.map = map;
@@ -446,6 +538,7 @@ export class MapTile{
         this.meshAdded = !1,
         this.textureLoaded = !1,
         this.children = []
+        this.id = getSid();
     }
     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))
@@ -517,52 +610,15 @@ export class MapTile{
         Potree.Utils.setObjectLayers(this.mesh, 'map' )
         this.mesh.renderOrder = -(1e6 - l - 100 * (t.zIndex || 0));
         this.mesh.name = this.name //add
-        var h = this.mesh.material;
+        this.texURL = p
         
         /* let area = math.getArea(this.mesh.geometry.vertices.slice(0,3));
         if(area >0){
             this.mesh.visible = false
             console.log('area>0',this.mesh.name)
         } */
+        addLoadTile(this)
         
-        var loadDone = ()=>{
-            this.map.mapLayer.loadingInProgress--
-            if(this.map.mapLayer.loadingInProgress == 0){
-                this.map.mapLayer.dispatchEvent('loadDone') 
-            }
-        }
-        
-        h.map = texLoader.load(p, (tex)=>{      //如果一直加载不了会影响其他的加载,如google地图没有vpn会使全景图一直加载不了
-            if(this.mesh){//如果还要显示的话
-                this.textureLoaded = true
-                this.mesh.material.opacity = 1
-                //this.mapLayer.view.invalidateScene()
-                 
-                this.map.mapLayer.viewer.mapChanged = true
-                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.mapChanged = true
-                
-                
-            } 
-            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){
@@ -612,14 +668,18 @@ export class MapTile{
     
     removeObject3D(){
         if (this.mesh){
-            if (this.objectGroup.remove(this.mesh),
-            this.textureLoaded){
+            this.objectGroup.remove(this.mesh)
+            if (this.textureLoaded){
                 var t = this.mesh.material.map;
-                t && t.dispose()
+                t && t.dispose() 
+            }else{
+                cancelLoad(this)
             } 
+            
             this.mesh.material.dispose() //o.disposeMeshMaterial(this.mesh),
             this.mesh.geometry.dispose() 
             this.mesh = void 0
+            ///* this.texURL && this.texURL.includes('testdata') &&  */console.log('removeObject3D',this.id)
         }
         this.meshAdded = !1,
         this.textureLoaded = !1

+ 5 - 11
src/custom/viewer/map/MapViewer.js

@@ -294,7 +294,7 @@ export class MapViewer extends ViewerBase{
     } 
     
     updateCursor(){
-         
+        //console.log('pos', viewer.mainViewport.camera.position.toArray() ) 
         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()),
@@ -401,19 +401,13 @@ export class MapViewer extends ViewerBase{
 
         
         var filterFuncs = [
+            Images360.filters.isEnabled(),
+            Images360.filters.isVisible(),//只走显示的点,否则会走到别的层  
             (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)

+ 11 - 4
src/custom/viewer/viewerBase.js

@@ -212,20 +212,27 @@ export class ViewerBase extends THREE.EventDispatcher{
             this.needRender = false
             return true
         } */
+        
+        
         for(let i=0,j=this.viewports.length;i<j;i++){
-            let changeInfo = this.viewports[i].cameraChanged()
+            let viewport = this.viewports[i]
+            let changeInfo = viewport.cameraChanged()
             if(changeInfo.changed){
                 changed = true
                 //if(!this.changeTime ||this.changeTime<100){ 
                     this.dispatchEvent({
                         type: "camera_changed", 
-                        camera: this.viewports[i].camera,
-                        viewport : this.viewports[i],
+                        camera: viewport.camera,
+                        viewport ,
                         changeInfo 
                     }) 
                     //this.changeTime = (this.changeTime || 0) +1
                 //}
-                 this.viewports[i].needRender = true  //直接写这咯   
+                viewport.needRender = true  //直接写这咯  
+                if(viewport.resolutionChanged){
+                    this.emitResizeMsg({viewport})
+                }
+                  
             }                
         }
         return changed

+ 16 - 9
src/navigation/FirstPersonControlsNew.js

@@ -413,7 +413,7 @@ export class FirstPersonControls extends THREE.EventDispatcher {
                         let min = math.linearClamp(dis, disBound1, disBound2,  closeMin, standardMin) //触屏和滚轮不一样,触发较为连续,所以最小值设低一点。若要保持双指相对点云位置不变,理想最小值是0,但那样就无法穿越点云(最小值太小的话穿越密集点云如树丛很困难;太大会打滑)所以当离点云近时增大最小值 
                         speed = Math.sign(r) * THREE.Math.clamp(dis * Math.abs(r),   min, speed) 
                         
-                        console.log(speed, dis, e.scale)
+                        //console.log(speed, dis, e.scale)
                         
                     }else{  
                         const constantDis =  this.currentViewport.getMoveSpeed() * 200 //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整
@@ -560,16 +560,23 @@ export class FirstPersonControls extends THREE.EventDispatcher {
             //根据和漫游点的最短距离算moveSpeed。缺点:对于导入的无漫游点的数据集没有意义。
             if(viewport == viewer.mainViewport && viewer.images360){
                 let position = viewer.mainViewport.view.position 
-             
-                //let pointclouds = [viewer.atDatasets, ...viewer.scene.pointclouds.filter(pointcloud=>pointcloud.bound2.distanceToPoint(position) < )]
+                let speed
                 let pano = viewer.images360.findNearestPano()
-                if(!pano)return
-                let dis = pano.position.distanceTo(position)
-                let minSpeed = 0.05, minDis = 3, multiplier = 0.005;
-                let speed = dis <= minDis ? minSpeed : minSpeed + (dis-minDis) * multiplier
-                //console.log('dis', dis, 'speed', speed,  pano.id )
-                     
+                if(!pano){
+                    if(!viewer.bound)return
+                    let boundFloor = viewer.bound.boundingBox.clone();
+                    boundFloor.max.z = boundFloor.min.z
+                    speed = boundFloor.distanceToPoint(viewer.mainViewport.view.position)
+                    speed = Math.sqrt(speed) / 50;
+                      
+                }else{
+                    let dis = pano.position.distanceTo(position)
+                    let minSpeed = 0.05, minDis = 3, multiplier = 0.005;
+                    speed = dis <= minDis ? minSpeed : minSpeed + (dis-minDis) * multiplier
+                    //console.log('dis', dis, 'speed', speed,  pano.id )
+                }  
                 viewer.setMoveSpeed(speed)
+                
             }
             //调试场景t-FhDWmV5xur 两个数据集,大的数据集没有漫游点。
         }

+ 32 - 32
src/navigation/InputHandlerNew.js

@@ -399,10 +399,11 @@ export class InputHandler extends THREE.EventDispatcher {
       
         
         //if(isTouch || !Potree.settings.intersectWhenHover ){ 
+        if(!this.dragViewport.view.isFlying()){
             this.hoveredElements = this.getHoveredElements();
             this.intersect = this.getIntersect(viewport) //更新intersect,避免在没有mousemove但flyToPano后intersect未更新。
             //this.intersect = this.getWholeIntersect()  
-        //}
+        }
         if(!viewport)return //why add this?
         if (!this.drag) {
             let target = (isTouch||e.button == THREE.MOUSE.LEFT) && this.hoveredElements.find(el => (//只有左键能拖拽
@@ -750,7 +751,7 @@ export class InputHandler extends THREE.EventDispatcher {
                 }
                   
                 
-            } 
+            }  
        
         return { 
             camera, viewport, pointer 
@@ -758,15 +759,11 @@ export class InputHandler extends THREE.EventDispatcher {
     }
 
 
-    ifBlockedByIntersect(point, margin=0, usePointcloud, cameraPos, pickWindowSize, pano){//某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换)
-         
-        if(cameraPos){
-            usePointcloud = true  //只有使用点云才允许换位置
-        }
-         
-        let intersect = this.getIntersect(this.hoverViewport, true, pickWindowSize, null, usePointcloud, {point, cameraPos, pano})
-        let cameraPos_ = (!usePointcloud && pano) ? pano.position : (cameraPos||this.hoverViewport.view.position)
-        if(intersect && intersect.distance+margin <= point.distanceTo(cameraPos_)){
+    ifBlockedByIntersect({pos3d, margin=0, cameraPos, pickWindowSize, pano, useDepthTex}={}){//某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换)
+          
+        let intersect = this.getIntersect(this.hoverViewport, true, pickWindowSize, null, null, useDepthTex, {pos3d, cameraPos, pano})
+        let cameraPos_ = (!cameraPos && pano) ? pano.position : (cameraPos||this.hoverViewport.view.position)
+        if(intersect && intersect.distance+margin <= pos3d.distanceTo(cameraPos_)){
             return intersect //被遮挡
         }
         //点云模式,对没加载出的点云不准确。 尤其是需要修改相机位置时,因临时修改并不能使点云加载。
@@ -774,28 +771,23 @@ export class InputHandler extends THREE.EventDispatcher {
 
 
 
-    getIntersect(viewport,   onlyGetIntersect, pickWindowSize, dontIntersect, usePointcloud, prop={}){// usePointcloud:必须使用点云
+    getIntersect(viewport,   onlyGetIntersect, pickWindowSize, dontIntersect, usePointcloud, useDepthTex, prop={}){// usePointcloud:必须使用点云
         let intersectPoint  
         let camera = viewport.camera
         let raycaster 
         
-        let startTime = performance.now()
+        viewer.addTimeMark('getIntersect','start')
 
-        let getByDepthTex = ()=>{
-            /* if(prop.point){
-                raycaster = new THREE.Raycaster() 
-                var dir = new THREE.Vector3().subVectors(prop.point, camera.position).normalize()
-                raycaster.set(camera.position, dir) //var origin = new THREE.Vector3(pointer.x, pointer.y, -1).unproject(camera),
-            }  */
+        let getByDepthTex = ()=>{ 
             let intersect
-            if(prop.point){
+            if(prop.pos3d){
                 let cameraPos = prop.pano ? prop.pano.position : camera.position
-                let dir = new THREE.Vector3().subVectors(prop.point, cameraPos).normalize(); 
+                let dir = new THREE.Vector3().subVectors(prop.pos3d, cameraPos).normalize(); 
                 intersect = {dir} 
             }else{
                 intersect = Utils.getIntersect(camera, [viewer.images360.cube], this.pointer, raycaster) 
             } 
-            intersectPoint = viewer.images360.depthSampler.sample(intersect, prop.pano, !!prop.point)  //可能不准确, 因pano可能未加载depthTex
+            intersectPoint = viewer.images360.depthSampler.sample(intersect, prop.pano, !!prop.pos3d)  //可能不准确, 因pano可能未加载depthTex
             if(intersectPoint && Potree.settings.depTexLocBindDataset){
                 intersectPoint.pointcloud = (prop.pano || viewer.images360.currentPano).pointcloud
                 //在全景模式下,虽然深度图上的点可能对应别的pointcloud,但因为是在当前全景图处得到的,所以即使将原本对应的点云移走,该点也不移动是有道理的。它可以永远跟着该全景图。
@@ -803,9 +795,9 @@ export class InputHandler extends THREE.EventDispatcher {
         }
         
         let getByCloud = ()=>{
-            if(prop.point){ 
+            if(prop.pos3d){//指定了目标点,而非只是用pointer所在位置
                 prop.cameraPos && camera.position.copy(prop.cameraPos)
-                camera.lookAt(prop.point)
+                camera.lookAt(prop.pos3d)
                 camera.updateMatrixWorld()
                 prop.pointer = this.pointer.clone()
                 prop.mouse = this.mouse.clone()
@@ -820,11 +812,11 @@ export class InputHandler extends THREE.EventDispatcher {
                 camera, 
                 this.viewer, 
                 this.viewer.scene.pointclouds,
-                {pickClipped: true, isMeasuring: this.isMeasuring, pickWindowSize, cameraChanged: !!prop.point }  
+                {pickClipped: true, isMeasuring: this.isMeasuring, pickWindowSize, cameraChanged: !!prop.pos3d }  
                 
             );
             //恢复
-            if(prop.point){
+            if(prop.pos3d){
                 viewport.view.applyToCamera(camera)
                 this.pointer.copy(prop.pointer)
                 this.mouse.copy(prop.mouse)
@@ -833,7 +825,8 @@ export class InputHandler extends THREE.EventDispatcher {
         
         
         
-        let canUseDepthTex = Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.pointcloud.hasDepthTex  && viewport == viewer.mainViewport && !usePointcloud 
+        let canUseDepthTex = (Potree.settings.displayMode == 'showPanos' || useDepthTex)
+            && viewer.images360.currentPano.pointcloud.hasDepthTex && viewport == viewer.mainViewport && !usePointcloud 
         
         
         if(canUseDepthTex)getByDepthTex()
@@ -872,11 +865,13 @@ export class InputHandler extends THREE.EventDispatcher {
                     hoveredElement : allElements[0] ,
                     location: allElements[0].point,
                     //point: {normal: allElements[0].face.normal },
-                    normal: allElements[0].face.normal,
+                    normal: allElements[0].face && allElements[0].face.normal,
                     distance: allElements[0].distance,
                     object: allElements[0].object
                 } 
             }
+            
+             
         }
         
         if(intersectPoint && intersectOnModel){
@@ -906,8 +901,8 @@ export class InputHandler extends THREE.EventDispatcher {
         }
         
         
-        let endTime = performance.now()
-        //console.log('getIntersect费时:' ,parseInt((endTime-startTime)*100 )/100)
+        
+        viewer.addTimeMark('getIntersect','end')
         //点云费时:2-15ms
         //深度图费时: 0.1-0.2ms
 
@@ -969,7 +964,12 @@ export class InputHandler extends THREE.EventDispatcher {
         
             let dontIntersect =  this.drag && viewport.alignment || isFlying /* viewer.images360.flying */ // flying 时可能卡顿
             //console.log('dontIntersectPointcloud',dontIntersectPointcloud)
-            intersect =  this.getIntersect(viewport,  e.onlyGetIntersect, e.pickWindowSize, !!dontIntersect, e.whichPointcloud) //数据集多的时候卡顿
+              
+            /* if(e.onlyGetIntersect )   */intersect = this.getIntersect(viewport,  e.onlyGetIntersect, e.pickWindowSize, !!dontIntersect, e.whichPointcloud) //数据集多的时候卡顿
+            /* else  Potree.Common.intervalTool.isWaiting('getIntersect', ()=>{  
+                 this.intersect = this.getIntersect(viewport,  e.onlyGetIntersect, e.pickWindowSize, !!dontIntersect, e.whichPointcloud) //数据集多的时候卡顿
+            }, 156); */
+	                 
             //console.log('intersectPoint', intersectPoint)
         } 
         
@@ -1347,7 +1347,7 @@ export class InputHandler extends THREE.EventDispatcher {
         //this.hoverViewport.beforeRender && this.hoverViewport.beforeRender()
         
         viewer.dispatchEvent( {type:'raycaster',  viewport: this.hoverViewport})//add
-		let intersections = raycaster.intersectObjects(interactables.filter(o => o.visible), true); //原本是false 检测不到children
+		let intersections = raycaster.intersectObjects(interactables.filter(o => o.visible), true, null, true); //原本是false 检测不到children
     
         let intersectionsCopy = intersections.slice()
         

+ 10 - 10
src/viewer/ExtendView.js

@@ -161,10 +161,10 @@ class ExtendView extends View {
         return type == 'pos' ? a : type == 'rotate' ? b :  (a || b)
     }
     
-    cancelFlying(type='all'){//外界只能通过这个来cancel
-        type == 'pos' ? transitions.cancelById(this.FlyTransition, true )
-         : type == 'rotate' ? transitions.cancelById(this.LookTransition, true )    
-         : (transitions.cancelById(this.FlyTransition, true ), transitions.cancelById(this.LookTransition, true ))
+    cancelFlying(type='all', dealCancel=true){//外界只能通过这个来cancel
+        type == 'pos' ? transitions.cancelById(this.FlyTransition, dealCancel )
+         : type == 'rotate' ? transitions.cancelById(this.LookTransition, dealCancel )    
+         : (transitions.cancelById(this.FlyTransition, dealCancel ), transitions.cancelById(this.LookTransition, dealCancel ))
         //console.log('cancelFlying ' , this.sid,  type)
     }
     
@@ -237,9 +237,9 @@ class ExtendView extends View {
             let posChange = !this.position.equals(endPosition)
             if(posChange){
                 posWaitDone = true 
-                transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{
+                transitions.start(lerp.vector(this.position, endPosition, (pos, progress, delta)=>{
                     
-                    info.onUpdate && info.onUpdate(progress) 
+                    info.onUpdate && info.onUpdate(progress, delta) 
                     
                 }), info.duration, posDone , 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.FlyTransition, ()=>{
                     //中途取消 
@@ -251,24 +251,24 @@ class ExtendView extends View {
                     } 
                     posWaitDone = false 
                     info.cancelFun && info.cancelFun()
-                });  
+                }, info.ignoreFirstFrame);  
             } 
             
             if(endQuaternion){
                 rotWaitDone = true 
-                transitions.start( (progress)=>{
+                transitions.start( (progress, delta )=>{
                 
                     let quaternion = (new THREE.Quaternion()).copy(startQuaternion) 
                     lerp.quaternion(quaternion, endQuaternion)(progress),
                     this.rotation = new THREE.Euler().setFromQuaternion(quaternion)
                     
-                    //posChange || info.onUpdate && info.onUpdate(progress)  //位置没变化uniforms不需要变progress
+                    posChange || info.onUpdate && info.onUpdate(progress, delta)  
                     
                 }, info.duration, rotDone , 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.LookTransition, ()=>{
                     //中途取消
                     rotWaitDone = false
                     info.cancelFun && info.cancelFun()
-                }); 
+                }, info.ignoreFirstFrame); 
                   
             }