1
0

10 Commity bc28b61e8a ... f504c02c47

Autor SHA1 Správa Dátum
  xzw f504c02c47 0 1 mesiac pred
  xzw 07a5d3b1c3 1 1 mesiac pred
  xzw 8daf7a59f4 fix: 0 1 mesiac pred
  xzw 50120a437e 1 1 mesiac pred
  xzw ed182253d2 1 1 mesiac pred
  xzw d02f43dcbf 1 1 mesiac pred
  xzw 57eb0d7794 1 1 mesiac pred
  xzw f6845f0528 1 1 mesiac pred
  xzw de8316d99d 1 2 mesiacov pred
  xzw 18a36b840c 1 2 mesiacov pred

+ 1 - 1
gulpfile.js

@@ -91,7 +91,7 @@ let shaders = [
 gulp.task('webserver', gulp.series(async function() {
 	server = connect.server({
 		port: 1234,
-        host:'192.168.0.113',
+        host:'192.168.0.59',
 		https: false,
 	});
 }));

+ 1 - 1
src/PotreeRendererNew.js

@@ -824,7 +824,7 @@ export class Renderer {
 
 			if (visibilityTextureData) {
 				let vnStart = visibilityTextureData.offsets.get(node);
-                console.log('vnStart',vnStart)
+                //console.log('vnStart',vnStart)
 				shader.setUniform1f("uVNStart", vnStart);
 			}  
 

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 233 - 96
src/custom/modules/panos/Images360.js


+ 11 - 11
src/custom/modules/panos/Panorama.js

@@ -97,8 +97,8 @@ class Panorama extends THREE.EventDispatcher{
             1       ifShowMarker(marker显示开关)       unvisible 
             0       pointcloudVisi(隐藏了数据集)       unvisible
          */ 
-         
-        this.panosData = o
+       
+        this.panoData = o
            
         this.originPosition = new THREE.Vector3().copy(o.pose.translation) 
         this.originFloorPosition = new THREE.Vector3().copy(o.puck)
@@ -184,7 +184,7 @@ class Panorama extends THREE.EventDispatcher{
 
     waitForLoad(){
         viewer.waitForLoad(this,  ()=>{//发送loading
-            return this.depthTex && this.skyboxTex
+            return (!this.pointcloud.hasDepthTex || this.depthTex) && this.skyboxTex
         });
     }
 
@@ -192,7 +192,7 @@ class Panorama extends THREE.EventDispatcher{
     loadTex(){ 
         if(this.skyboxTex || this.texLoading)return
         this.texLoading = true
-        let src =  `${Potree.settings.urls.prefix1}/images/${this.originID}.jpg`  //`server\test\SS-t-P1d6CwREny2\${this.id}.jpg`    //`${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/depthmap/${this.originID}.png`
+        let src =  this.panoData.imageSrc || `${Potree.settings.urls.prefix1}/images/${this.originID}.jpg`  //`server\test\SS-t-P1d6CwREny2\${this.id}.jpg`    //`${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/depthmap/${this.originID}.png`
         //console.log('开始下载depthImg', this.id)
         
         let startLoad = (src)=>{
@@ -403,12 +403,17 @@ class Panorama extends THREE.EventDispatcher{
     }
     
  
-    
+    dispose(){
+        this.dispatchEvent('dispose')
+        this.marker.parent.remove(this.marker);
+        this.label && this.label.parent.remove(this.label);
+        this.label2 && this.label2.parent.remove(this.label2);
+    }
 
     
     
     addLabel(){ 
-        this.removeTextLabel()
+        this.label && this.label.parent.remove(this.label);
         this.label = new TextSprite(Object.assign({},
            labelProp, {text: this.id + "("+this.originID+")"}) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
         ); 
@@ -429,11 +434,6 @@ class Panorama extends THREE.EventDispatcher{
     }
      
     
-    removeTextLabel(){
-        if(this.label){ 
-            this.label.parent.remove(this.label);
-        }
-    }
     
      
     

+ 17 - 13
src/custom/start.js

@@ -67,17 +67,9 @@ export function start(dom, navDom, number ){ //t-Zvd3w0m
             scene.addPointCloud(pointcloud);
             
               
-             
-             
-        
-            Potree.loadPanos( (data) => { 
-                //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
-                viewer.images360.addPanoData(data  ) 
-                viewer.images360.loadDone() 
-                viewer.scene.add360Images(viewer.images360); 
-               
+            let done = ()=>{
                 viewer.updateModelBound() //需等pano加载完
-              
+                  
                 let {boundSize, center} = viewer.bound
                  
                 viewer.dispatchEvent({type:'loadPointCloudDone'})
@@ -115,15 +107,27 @@ export function start(dom, navDom, number ){ //t-Zvd3w0m
                  
                  
                 let boxHelper = new Potree.Box3Helper(viewer.bound.boundingBox);
-				boxHelper.matrixAutoUpdate = false; 
+                boxHelper.matrixAutoUpdate = false; 
                 viewer.scene.scene.add(boxHelper) 
                 viewer.bound.boxHelper = boxHelper
                 boxHelper.visible = false
                 
                 console.log('allLoaded')
                 viewer.dispatchEvent('allLoaded')
-            }) 
-                
+            }
+            
+            
+            if(Potree.settings.noPanos){
+                done()
+            }else{ 
+                Potree.loadPanos( (data) => { 
+                    //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
+                    viewer.images360.addPanoData(data  ) 
+                    viewer.images360.loadDone()  
+                    
+                    done() 
+                }) 
+            }
         }) 
         
     } 

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

@@ -1,7 +1,9 @@
 
 
 import * as THREE from "../../../libs/three.js/build/three.module.js";
-import math from './math.js'
+import math from './math.js' 
+ 
+ 
 
 var Common = {
      
@@ -118,35 +120,34 @@ var Common = {
     
     ,
      
-    CloneObject : function(copyObj, result, isSimpleCopy, simpleCopyList=[]) {
+    CloneObject : function (copyObj, isSimpleCopy, simpleCopyList = [], judgeSimpleCopyFun) {
         //isSimpleCopy 只复制最外层
-        //复制json		result的可能:普通数字或字符串、普通数组、复杂对象
-         
-        simpleCopyList.push(THREE.Object3D) //遇到simpleCopyList中的类直接使用不拷贝
+        //复制json		result的可能:普通数字或字符串、普通数组、复杂对象 
+        judgeSimpleCopyFun || (judgeSimpleCopyFun=()=>{})
         
-        if(!copyObj || typeof copyObj == 'number' || typeof copyObj == 'string' || copyObj instanceof Function || simpleCopyList.some(className => copyObj instanceof className)){
-            return copyObj 
+        if (!copyObj || typeof copyObj == 'number' || typeof copyObj == 'string' ||copyObj.isObject3D || copyObj instanceof Function || simpleCopyList.some(className => copyObj instanceof className) || judgeSimpleCopyFun(copyObj)) {
+            return copyObj
         }
-        
-        result = result || {};
+
         if (copyObj instanceof Array) {
-            return copyObj.map(e=>{ 
-                return this.CloneObject(e) 
-            }) 
-        }else{
-            if(copyObj.clone instanceof Function ){ //解决一部分
+            return copyObj.map(e => {
+                return this.CloneObject(e, isSimpleCopy, simpleCopyList, judgeSimpleCopyFun)
+            })
+        } else {
+            if (copyObj.clone instanceof Function) {
+                //解决一部分
                 return copyObj.clone()
             }
         }
+
+        let result = {}
         for (var key in copyObj) {
-            if (copyObj[key] instanceof Object && !isSimpleCopy)
-                result[key] = this.CloneObject(copyObj[key]);
-            else
-                result[key] = copyObj[key];
+            if (copyObj[key] instanceof Object && !isSimpleCopy ) result[key] = this.CloneObject(copyObj[key], isSimpleCopy, simpleCopyList, judgeSimpleCopyFun)
+            else result[key] = copyObj[key]
             //如果是函数类同基本数据,即复制引用
         }
-        return result;
-    }
+        return result
+    }  
     ,
     CloneClassObject :function(copyObj, {ignoreList=[],simpleCopyList=[]}={}){//复杂类对象
         var newobj = new copyObj.constructor();
@@ -166,7 +167,7 @@ var Common = {
             }else if(simpleCopyList.includes(i)){
                 targetObj[i] = copyObj[i]
             }else{
-                targetObj[i] = this.CloneObject(copyObj[i], null, false, simpleCopyList )  
+                targetObj[i] = this.CloneObject(copyObj[i], false, simpleCopyList )  
             }
                 
             
@@ -225,12 +226,6 @@ var Common = {
         
     } 
     ,
-    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;
@@ -240,15 +235,59 @@ var Common = {
         save_link.dispatchEvent(event);
         cb && cb();
     }, 
+     
+    replaceAll : function (str, f, e ) {
+        //f全部替换成e
+       
+        if(str.replaceAll ) return str.replaceAll(f, e)
+        else{ 
+            let escapeRegExp = (string)=>{
+                return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+            }
+             
+            return str.replace(new RegExp(escapeRegExp(f), 'g'), e);
+     
+            /* var reg = new RegExp(f, "g"); //创建正则RegExp对象    这个没法转换'('
+            return str.replace(reg, e);        //str.split(f).join(e); */
+        }
+    }, 
       
-    dealURL(url){
-        return this.replaceAll(url, "\\+", "%2B");// 浏览器似乎不支持访问带+的地址
+    
+    cleanUrl(url){//斜杠处理   协议aaa://保留双斜杠,其余单斜杠
+        //分离协议和路径部分
+        const protocolMatch = url.match(/^(\w+:)\/{2,}/);
+
+        if (protocolMatch) {
+            const protocol = protocolMatch[1];
+            const path = url.slice(protocolMatch[0].length);
+            return protocol + '//' + path.replace(/\/+/g, '/');
+        } else {
+            return url.replace(/\/+/g, '/');
+        }
+    },
+    
+    joinUrl(){//拼接地址。总是出现前后多个/造成双斜杠或者缺斜杠的问题,处理一下
+        return this.cleanUrl(Array.from(arguments).join('/'))
+    },
+    
+    dealURL(url=''){ 
+        let urlNew = this.replaceAll(url, "+", "%2B"); //this.replaceAll(url, "\\+", "%2B");// 浏览器似乎不支持访问带+的地址
+    
+        urlNew = this.replaceAll(urlNew, "/.//", "/") //去除双斜杠(/.//)
+         
+          
+        //urlNew = encodeURIComponent(url)
+         
+        return urlNew
     },
     
     
-    getNameFromURL(url){
+    getNameFromURL(url, removePostfix){
+        if(!url)return ''
         let get = (e)=>{
-            return e.split('/').pop()
+            let a = e.split('/').pop()
+            if(removePostfix) a = a.split('.')[0]
+            return a
         }
         if(url instanceof Array){
             return url.map(e=>get(e))
@@ -266,35 +305,31 @@ var Common = {
     
     intervalTool:{  //延时update,防止卡顿
         list:[],
-         
-        /* isWaiting:function(name, func, delayTime ){   
-            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);
+        
+        /* isWaiting:function(name, func, delayTime){
+            if(!this.list.includes(name)){  //如果没有该项, 则开始判断
+                var needWait = func(); //触发了改变,则等待一段时间后再自动判断
+                if(needWait){
+                    this.list.push(name);
+                    setTimeout(()=>{
+                        var a = this.list.indexOf(name);
                         this.list.splice(a,1);
-                        if(item.requestUpdate || ifContinue )  this.isWaiting(name, func, delayTime) //循环
+                        this.isWaiting(name, func, delayTime) //循环
                     },delayTime)
-                 
-            }else{//如果有该项,说明现在请求下一次继续更新
-                //if(delayTime == 0){//想立刻更新一次
-                //    func()
-                //}else{  
-                    item.requestUpdate = true
-                //}
-                
+                } 
             }
         }, */
         
         
+        
         isWaiting:function(name, func, delayTime/* , autoCycle */){   
             let item = this.list.find(e=>e.name == name)
             if(!item){  //如果没有该项, 则加入循环
                 let ifContinue = func() 
                 item = {name, func, delayTime} 
+                /* if(name == 'processPriorityQueue'){
+                    console.log('isWaiting', delayTime)
+                } */
                 this.list.push(item);
                     setTimeout(()=>{ 
                         var a = this.list.indexOf(item);
@@ -310,24 +345,64 @@ var Common = {
                     //更新属性  
                     item.func = func
                     item.delayTime = delayTime
-                    item.requestUpdate = true
-                     
-                     
-                //}
-                
+                    item.requestUpdate = true 
+                //} 
+            }
+        },
+    }
+    ,
+    
+    
+    
+    waitTool:{//定时器,在等待的这段时间内如果又触发则重新计时
+        list:[],
+        
+        wait(name, func, time){
+            let item = this.list.find(e=>e.name == name) 
+            
+            let timer = setTimeout(()=>{
+                func()
+                let index = this.list.indexOf(item)
+                this.list.splice(index, 1)
+            }, time)
+            if(item){
+                clearTimeout(item.timer) 
+            }else{
+                item = {name}
+                this.list.push(item)
             }
+            item.timer = timer
         },
         
+        cancel(name){
+            let index = this.list.findIndex(e=>e.name == name) 
+            index>-1 && this.list.splice(index, 1)
+        }
+        
     }
+    
+    
+    
+    
+    
+    
     ,
-    pushToGroupAuto : function(items, groups, recognizeFunction){//自动分组。 items是将分到一起的组合。items.length = 1 or 2. 
+    pushToGroupAuto : function(items, groups, recognizeFunction, recognizeGroup){//自动分组。 items是将分到一起的组合。items.length = 1 or 2. 
     
         recognizeFunction = recognizeFunction || function(){}
     
-        var atGroups = groups.filter(group=>group.find(
-            item => items[0] == item || recognizeFunction(item, items[0]) || items[1] == item || items[1] && recognizeFunction(item, items[1])
         
-        )) 
+        if(recognizeGroup){ //有更复杂的识别处理,直接传递整个组
+            var atGroups = groups.filter(group=>recognizeGroup(group, items))
+                 
+        }else{
+            var atGroups = groups.filter(group=>group.find(
+                item => items[0] == item || recognizeFunction(item, items[0]) || items[1] == item || items[1] && recognizeFunction(item, items[1])
+            )) 
+        
+        }
+        
+        
         if(atGroups.length){//在不同组
             //因为items是一组的,所以先都放入组1
             items.forEach(item=> {if(!atGroups[0].includes(item)) atGroups[0].push(item);})
@@ -347,30 +422,58 @@ var Common = {
         }
     },
      
-   
- 
-    getBestCount : function(name, minCount=1,maxCount=6, durBound1 = 1.2, durBound2 = 10, ifLog){
-       
+    
+    getBestCount : (function(){
+        let lastCount = {}
+        return function(name, minCount=1,maxCount=6, durBound1 = 1.2, durBound2 = 10, ifLog, maxHistory){ 
+            let timeStamp = performance.getEntriesByName("loop-start");
+            let count
+            if(timeStamp.length){
+                let dur = performance.now() - timeStamp[timeStamp.length-1].startTime;  //dur在iphoneX中静止有7,pc是2
+               
+                count = Math.round(math.linearClamp(dur, [durBound1,durBound2],   [maxCount,  minCount]))
+                 
+                if (maxHistory) {
+                    if (!lastCount[name]) lastCount[name] = []
+                    if (count == 0 && lastCount[name].length > maxHistory - 1 && !lastCount[name].some(e => e > 0)) {
+                        count = 1
+                    }
+                    lastCount[name].push(count)
+                    if (lastCount[name].length > maxHistory) lastCount[name].splice(0, 1)
+                }  
+                 
+                 
+                 
+                if(ifLog){//注意,console.log本身用时挺高, 降4倍时可能占用0.5毫秒
+                   name   && count  && console.log(name,   count , ' ,dur:', dur.toFixed(3))
+                } 
+            }else{
+                count = maxCount  //  ? 
+            }   
+            //主要在手机端有效果。 
+            return count 
+        }
+    })(),
+    
+    getBestCountFPS(name, ifLog, minCount=1, maxCount=6, minFps = 10, maxFps = 60, minPanoCount = 200, maxPanoCount = 1000 ){
+        
+        let fps = Potree.fps
+        
+        let count = math.linearClamp(fps, [minFps, maxFps],   [minCount,  maxCount])
+         
+        let count2 =  math.linearClamp(viewer.images360.panos.length, [minPanoCount, maxPanoCount],   [minCount,  maxCount])
+        count = (count + count2)/2
        
-        let timeStamp = performance.getEntriesByName("loop-start");
-        let count
-        if(timeStamp.length){
-            let dur = performance.now() - timeStamp[timeStamp.length-1].startTime;  //dur在iphoneX中静止有7,pc是2
-           
-            count = Math.round(math.linearClamp(dur, durBound1,durBound2,   maxCount,  minCount))
-             
-            if(ifLog){//注意,console.log本身用时挺高, 降4倍时可能占用0.5毫秒
-               name   && count  && console.log(name,   count , ' ,dur:', dur.toFixed(3))
-            } 
-        }else{
-            count = maxCount  //  ? 
-        } 
+        count = Math.round(count)
+        
+        
+        if(ifLog){ 
+           name && count && console.log(name, count , ' ,fps:', fps.toFixed(3))
+        }
+        
+        return count
         
-        //主要在手机端有效果。 
-        return count 
     },
-    
-    
         
     batchHandling : {//分批处理
         
@@ -436,6 +539,20 @@ var Common = {
         
     }, 
         
+    getRootWindow(){//获取包含Potree的根window
+        let win = window
+        try{
+            while(win.parent!=win && win.parent.Potree){
+                win = win.parent
+            }
+            if(window != win)return win 
+        }catch(e){
+            //console.log(e) //可能跨域,从而win.parent.Potree报错 
+            console.log('getRootWindow 跨域')
+            return  
+        }
+        
+    },
     
     watch: function(object, propName, initialValue){ //监听某个属性的变化
         let v = initialValue
@@ -450,8 +567,189 @@ var Common = {
         }) 
     },
       
+    imgAddLabel : function (img, labelImg, labelInfo = {}) {
+        //图上加另一张小图,用于添加水印
+        let canvas
+        if(img instanceof Image){
+            canvas = document.createElement('canvas')
+        }else{
+            canvas = img
+        }
+          
+        let context = canvas.getContext('2d')
+        let marginLeft = labelInfo.bgMargin && labelInfo.bgMargin.left || 0 
+        let marginRight = labelInfo.bgMargin && labelInfo.bgMargin.right || 0 
+        let marginTop = labelInfo.bgMargin && labelInfo.bgMargin.top || 0 
+        let marginBottom = labelInfo.bgMargin && labelInfo.bgMargin.bottom || 0 
+        
+        if(img instanceof Image){//如果img是canvas,说明已绘制在canvas上了就不用绘制了
+            let width = img.width + marginLeft + marginRight
+            let height = img.height + marginTop + marginBottom
+            canvas.width = width
+            canvas.height = height 
+            if(labelInfo.bgColor){
+                context.fillStyle = 'rgba(' + labelInfo.bgColor.r + ',' + labelInfo.bgColor.g + ',' + labelInfo.bgColor.b + ',' + labelInfo.bgColor.a + ')';
+                context.fillRect(0,0,width,height); 
+            }
+            context.drawImage(img, marginLeft, marginTop, img.width, img.height)
+        }
+        let labelWidth = labelInfo.widthRatioToImg ? img.width * labelInfo.widthRatioToImg : labelImg.width //widthRatioToImg:label的width占img的width的比例
+        let labelHeight = (labelWidth * labelImg.height) / labelImg.width
+
+        if (labelInfo.leftRatioToImg == void 0 && labelInfo.rightRatioToImg != void 0) {
+            labelInfo.leftRatioToImg = 1 - labelInfo.rightRatioToImg - (labelInfo.widthRatioToImg || labelImg.width / img.width)
+        }
+        if (labelInfo.topRatioToImg == void 0 && labelInfo.bottomRatioToImg != void 0) {
+            labelInfo.topRatioToImg = 1 - labelInfo.bottomRatioToImg - labelHeight / img.height
+        }
+
+        let labelLeft = img.width * labelInfo.leftRatioToImg + marginLeft //leftRatioToImg:label的left占img的width的比例
+        let labelTop = img.height * labelInfo.topRatioToImg + marginTop //topRatioToImg:label的top占img的height的比例
+
+        context.globalAlpha = labelInfo.opacity != void 0 ? labelInfo.opacity : 1
+        context.drawImage(labelImg, labelLeft, labelTop, labelWidth, labelHeight)
+        
+        if(labelInfo.outputCanvas){
+            return canvas
+        }
+
+        var dataUrl = canvas.toDataURL('image/png', labelInfo.compressRatio)
+        //Common.downloadFile(dataUrl, 'screenshot.png')
+        context.clearRect(0,0,canvas.width,canvas.height)
+        return dataUrl
+
+ 
+    },
+    
+    
+    mobileAutoPlay(media, playFun){//移动端。不这么写video不会播放 . (2022.11.29: 可为何加了Hot.updateHots之后又会自动播了?https有关?
+        
+        
+        if(this.autoPlayList.includes(media))return
+        this.autoPlayList.push(media)
+        viewer.addEventListener       
+        let events = ['global_touchstart','global_mousedown']    
+        
+        let fun = ()=>{
+            let index = this.autoPlayList.indexOf(media)
+            if(index>-1){
+                
+                console.log( 'try autoplay '+ media.src)
+                playFun()
+                this.autoPlayList.splice(index,1)
+                events.forEach((eventName)=>{ 
+                    viewer.removeEventListener(eventName,fun)
+                })
+            }
+        }
+    
+        events.forEach((eventName)=>{ 
+            viewer.addEventListener(eventName,fun)
+        })
+ 
+        
+    },
+    
+    /* changeShaderToWebgl2(vs, fs, matType, otherReplaces=[]){//部分shader要根据webgl版本作更改
+        if(!Potree.settings.isWebgl2)return {vs, fs}
+        let turnTo300 = matType != 'ShaderMaterial' && (vs.includes('gl_FragDepthEXT') || fs.includes('gl_FragDepthEXT') ) 
+        let addV300 = turnTo300 && matType != 'RawShaderMaterial' // RawShaderMaterial直接material.glslVersion = '300 es' 以加在define之前
+        let change = (shader, shaderType)=>{ 
+            let newShader = shader
+            
+            if(turnTo300){ //非shaderMaterial需要手动改为300 es的写法
+                addV300 && (newShader = '#version 300 es \n' + newShader)   //需要加 #version 300 es。 three.js自带的渲染会自动加所以不用
+                newShader = newShader.replaceAll('varying ', shaderType == 'vs' ? 'out ' : 'in ')
+                newShader = newShader.replaceAll('attribute ', 'in ') 
+                if(shaderType == 'fs'){
+                    newShader = newShader.replaceAll('gl_FragColor', 'fragColor')
+                    newShader = newShader.replace('void main', 'out vec4 fragColor;\n  void main' )//在void main前加入这个声明 
+                }  
+                newShader = newShader.replaceAll('gl_FragDepthEXT','gl_FragDepth')
+            
+                newShader = newShader.replaceAll('texture2D','texture')
+                newShader = newShader.replaceAll('textureCube','texture')
+            
+            }
+            
+            newShader = newShader.replace('#extension GL_EXT_frag_depth : enable','') 
+            newShader = newShader.replaceAll('defined(GL_EXT_frag_depth) &&','')
+             
+            
+            otherReplaces.forEach(({oldStr,newStr})=>{
+                newShader = newShader.replaceAll(oldStr,newStr)   
+            })
+                
+            return newShader
+        }
+        
+        vs = change(vs,'vs')
+        fs = change(fs,'fs')
+         
+        //console.log('成功替换为webgl2' )
+        return {vs,fs}
+    }//three.js的shaderMaterial也有替换功能,搜 '#define gl_FragDepthEXT gl_FragDepth',
+     */
+    changeShaderToWebgl2(vs, fs, matType, otherReplaces=[]){//部分shader要根据webgl版本作更改
+        if(!Potree.settings.isWebgl2)return {vs, fs}
+        let turnTo300 = matType != 'ShaderMaterial' && (vs.includes('gl_FragDepthEXT') || fs.includes('gl_FragDepthEXT') ) 
+        let addV300 = turnTo300 && matType != 'RawShaderMaterial' // RawShaderMaterial直接material.glslVersion = '300 es' 以加在define之前
+        let change = (shader, shaderType)=>{ 
+            let newShader = shader
+            
+            if(turnTo300){ //非shaderMaterial需要手动改为300 es的写法
+                addV300 && (newShader = '#version 300 es \n' + newShader)   //需要加 #version 300 es。 three.js自带的渲染会自动加所以不用
+                newShader = this.replaceAll(newShader, 'varying ', shaderType == 'vs' ? 'out ' : 'in ')
+                newShader = this.replaceAll(newShader, 'attribute ', 'in ') 
+                if(shaderType == 'fs'){
+                    newShader = this.replaceAll(newShader, 'gl_FragColor', 'fragColor')
+                    newShader = newShader.replace('void main', 'out vec4 fragColor;\n  void main' )//在void main前加入这个声明 
+                }  
+                newShader = this.replaceAll(newShader, 'gl_FragDepthEXT','gl_FragDepth')
+            
+                newShader = this.replaceAll(newShader, 'texture2D','texture')
+                newShader = this.replaceAll(newShader, 'textureCube','texture')
+            
+            }
+            
+            newShader = newShader.replace('#extension GL_EXT_frag_depth : enable','') 
+            newShader = this.replaceAll(newShader,'defined(GL_EXT_frag_depth) &&','')
+             
+            
+            otherReplaces.forEach(({oldStr,newStr})=>{
+                newShader = this.replaceAll(newShader, oldStr, newStr)   
+            })
+                
+            return newShader
+        }
+        
+        vs = change(vs,'vs')
+        fs = change(fs,'fs')
+         
+        //console.log('成功替换为webgl2' )
+        return {vs,fs}
+    },//three.js的shaderMaterial也有替换功能,搜 '#define gl_FragDepthEXT gl_FragDepth',
+    
     
     
+    //hsv感觉比hsl好一些,更能保持颜色本身不变。  色调(H),饱和度(S),明度(V/B)。
+
+       
+    getHSV(color, prop={}  ){
+        let c = new THREE.Color()
+        let hsv = c.set(color).getHSV()
+        if(prop.add){
+            for(let i in prop.add) hsv[i] += prop.add[i]
+        }else{
+            hsv = Object.assign(hsv, prop)
+        }
+        let {h, s, v} = hsv
+        return c.setHSV(h, s, v)  //setHSV(hsb.h, hsb.s, 100) 
+    }
+       
+//let c = new THREE.Color().set(this.color).getHSL({ h: 0, s: 0, l: 0 })  
+
+
 }  
 
 Potree.Common = Common

+ 20 - 3
src/custom/utils/math.js

@@ -618,9 +618,26 @@ var math = {
     },
 
         
-    linearClamp(value,  x1,x2, y1, y2){//x为bound.min, bound.max
-        value = THREE.Math.clamp(value, x1,x2)
-        return y1 + ( y2 - y1) * (value - x1)  / (x2 - x1)  
+    linearClamp(value, xArr , yArr){ //xArr需要按顺序从小到大,yArr对应xArr中的值
+        if (arguments.length == 5) {
+            xArr = [arguments[1], arguments[2]]
+            yArr = [arguments[3], arguments[4]]
+        }
+        let len = xArr.length 
+        if(value <= xArr[0]) return yArr[0]
+        if(value >= xArr[len - 1]) return yArr[len - 1]
+        let i = 0 
+        
+        while(++i < len ){
+            if(value < xArr[i]){
+                let x1 = xArr[i-1], x2 = xArr[i], y1 = yArr[i-1], y2 = yArr[i] 
+                value = y1 + ( y2 - y1) * (value - x1)  / (x2 - x1)  
+                break
+            }
+        }
+        return value
+        
+         
     },
     
     

+ 60 - 7
src/custom/viewer/ViewerNew.js

@@ -649,8 +649,8 @@ export class Viewer extends ViewerBase{
             })
             
             this.modules.Clip.init()
-            
-        }) 
+            this.createHackMesh() 
+        },{once:true}) 
         
         
         
@@ -3443,10 +3443,12 @@ export class Viewer extends ViewerBase{
             //是否是俯视或仰视的视角,这样看马路等平面的话尽量每处点云level都一致不要密度不同:
             let floorplanView = viewer.mainViewport.camera.type == 'OrthographicCamera'  
             if(!floorplanView){
-                let pano = this.images360.findNearestPano()
-                let dis = pano.position.distanceTo(this.mainViewport.camera.position)
-                if(dis > 3) floorplanView = true; //离远一点的平视希望也是全部加载好。勉强只能这么写
-                console.warn('floorplanView',floorplanView)
+                let pano = this.images360.findNearestPano() 
+                if(pano){
+                    let dis = pano.position.distanceTo(this.mainViewport.camera.position)
+                    if(dis > 3) floorplanView = true; //离远一点的平视希望也是全部加载好。勉强只能这么写
+                    console.warn('floorplanView',floorplanView)
+                }
             }    
                  
             let maxTime = floorplanView ? 3000 : 1500;    //注意交通一般要截图两次,先截带测量线的 
@@ -3619,7 +3621,9 @@ export class Viewer extends ViewerBase{
                 bound = boundingBox.applyMatrix4(inv);
                 scale = 1.0//0.9; 
             }
-            
+            if(o.boundScale){
+                scale = o.boundScale
+            }
             boundSize = bound.getSize(new THREE.Vector3)
             
             {
@@ -4533,6 +4537,55 @@ export class Viewer extends ViewerBase{
         return r && r.score > 1 ? result[0].item : null  
     }
     
+    
+    
+    createHackMesh(){//为了防止无depthTex的全景在pick点云时画面中仅有一个材质时会黑屏,所以在镜头前再加一个mesh。具体bug表现见bug记录。 
+        if(this.scene.pointclouds.every(e=>e.hasDepthTex) /* || viewer.images360.panos.length == 0 */)return 
+         
+        let mesh = window.hackmesh = new THREE.Mesh( new THREE.PlaneBufferGeometry(1,1) , new THREE.MeshBasicMaterial({color:"#F00",side:2  /* ,depthTest:false  */ }))
+            Potree.Utils.updateVisible(mesh,'show',false)
+            
+        this.images360.node.add(mesh)
+        
+         
+        let updatePos = ()=>{
+            let dir = this.mainViewport.view.direction
+            let radius = this.images360.cube.scale.length()
+            mesh.position.copy(this.mainViewport.view.position).add(dir.multiplyScalar(radius))//放置skybox之外
+            mesh.quaternion.copy(this.mainViewport.camera.quaternion)
+        }
+        
+        
+        this.addEventListener('camera_changed', e => {
+            if(e.viewport.name == 'MainView' && Potree.settings.displayMode == 'showPanos' && !this.images360.currentPano.depthTex
+             && e.changeInfo && (e.changeInfo.positionChanged || e.changeInfo.quaternionChanged)){
+                updatePos()
+            } 
+        })
+        
+        let judge = ()=>{
+            if(Potree.settings.displayMode == 'showPanos' && !this.images360.currentPano.depthTex){
+                Potree.Utils.updateVisible(mesh,'show',true)
+                updatePos()
+            }else{ 
+                Potree.Utils.updateVisible(mesh,'show',false)
+            }
+            
+        }
+        
+        
+        this.images360.addEventListener( 'flyToPanoDone', judge)
+            
+        this.images360.addEventListener( 'endChangeMode', judge)
+        
+
+        //不知道如果用点云计算非当前视角下的block会不会黑闪,如热点遮挡计算
+
+    }
+    
+    
+    
+    
     /* addObjectTest1(){//加水管
         
         if(Potree.settings.number == 't-8KbK1JjubE'){

+ 1 - 1
src/navigation/InputHandlerNew.js

@@ -782,7 +782,7 @@ export class InputHandler extends THREE.EventDispatcher {
     }
 
 
-    ifBlockedByIntersect({point, margin=0, cameraPos, pickWindowSize, pano, useDepthTex}={}){//某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换)
+    ifBlockedByIntersect({point, margin=0, cameraPos, pickWindowSize, pano, useDepthTex, viewport}={}){//某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换)
         viewport = viewport || this.hoverViewport || viewer.mainViewport
         let intersect = this.getIntersect({viewport, onlyGetIntersect:true, pickWindowSize,  useDepthTex,  point, cameraPos, pano })
         let cameraPos_ = (!cameraPos && pano) ? pano.position : (cameraPos||viewport.view.position)