xzw 1 month ago
parent
commit
a5ac818cdd

+ 248 - 0
src/custom/utils/DepthTexSampler.js

@@ -0,0 +1,248 @@
+
+
+
+
+import math from "./math.js";
+
+class DepthTexSampler {
+    
+    constructor(edlRenderer){ 
+        this.needDraw = true
+        this.edlRenderer = edlRenderer
+        this.renderer = viewer.renderer
+        this.renderTarget = new THREE.WebGLRenderTarget(1,1, {
+            format: THREE.RGBAFormat,
+            type:  THREE.UnsignedByteType, //默认THREE.UnsignedByteType      THREE.FloatType   UnsignedShortType  UnsignedInt248Type
+            //depthBuffer:false,
+            /* minFilter: THREE.NearestFilter,
+            magFilter: THREE.NearestFilter,
+            format: THREE.RGBAFormat,
+            type: THREE.FloatType,
+            depthBuffer:false, */
+            //depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
+        });
+        /* document.getElementsByTagName('body')[0].appendChild(canvas);
+        canvas.style.position = 'fixed';
+        canvas.style.width = '1024px';
+        canvas.style.top = canvas.style.left = 0
+        canvas.style['z-index'] = 100
+         */
+        this.recoverToScreenMat = edlRenderer.recoverToScreenMat.clone();
+        delete this.recoverToScreenMat.defines.useDepth
+        this.recoverToScreenMat.needsUpdate = true
+        
+        viewer.addEventListener('pointcloud_changed',()=>{
+            this.needDraw = true
+        })
+        viewer.addEventListener('camera_changed',(e)=>{
+            
+            this.needDraw = true
+        })
+    }
+    
+    convertToLinear(zValue) { //zValue越接近相机越大, 0-1
+        var z = zValue * 2.0 - 1.0;   //   -1 to 1
+        return (2.0 * this.near * this.far) / (this.far + this.near - z * (this.far - this.near));
+    
+    }  
+    
+    perspectiveDepthToViewZ(zValue){
+        return -( this.near * this.far ) / ( ( this.far - this.near ) * zValue - this.far ); 
+    }
+    //convertToLinear和perspectiveDepthToViewZ结果好像差不多
+    
+    /* viewZToPerspectiveDepth(viewZ, near, far ) { 
+        return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );
+    } */
+    
+    
+    
+  
+    //改写成只渲染depthTex的一部分(改贴图的offset和scale)
+    getDepth(depthTex, x, y) {//根据图片像素获取深度值 
+        if(this.needDraw){
+            if(this.renderTarget.width != depthTex.image.width || this.renderTarget.height != depthTex.image.height)this.renderTarget.setSize(depthTex.image.width, depthTex.image.height)
+           /*  let oldTarget = this.renderer.getRenderTarget()
+            this.renderer.setRenderTarget(this.renderTarget) */ 
+            
+            this.recoverToScreenMat.uniforms.tDiffuse.value = depthTex
+            this.recoverToScreenMat.needsUpdate = true
+            Potree.Utils.screenPass.render(viewer.renderer, this.recoverToScreenMat, this.renderTarget);  //目前耗时有点久
+            //this.renderer.setRenderTarget(oldTarget)
+            this.needDraw = false
+        }
+         
+        //var r = this.context.getImageData(x, y, 1, 1).data;
+        
+        let pixelCount = 1;
+        let buffer = new Uint8Array(4 * pixelCount); 
+        this.renderer.readRenderTargetPixels(this.renderTarget, x,  this.renderTarget.height-y, 1, 1,    buffer);
+        //目前得到的r值不太准确,256个层级本身就很少,而near很小far很大时,分层就朝近处移动,远处很不准。 
+       
+        
+        let depth =  this.convertToLinear(buffer[0] / 255, this.near, this.far)
+        console.log('buffer', buffer, x,y, depth)
+        return depth 
+    }   
+    
+    
+    sample( viewport , mouse, dir ) {//通过和skybox的intersect得到真实的intersect的位置
+        
+        
+        let rtEdl = this.edlRenderer.getRtEDL(viewport)  
+        if(!rtEdl)return 'unsupport'
+        let depthTex = rtEdl.depthTexture
+        
+         this.near = viewport.camera.near, this.far = viewport.camera.far
+        let distance = this.getDepth(depthTex, mouse.x, mouse.y)
+        console.log('dis', distance )
+        
+        
+        let location = new THREE.Vector3
+        let normal
+        dir = dir || viewer.inputHandler.getMouseDirection().direction   //这里可能有问题: 该viewport不一定是在viewer上,不能用viewer.inputHandler
+        let origin = viewport.view.position
+        
+        location.copy(dir).multiplyScalar(distance).add(origin);
+        /* 
+        //
+        if (!distance){
+            if(uv.y > 0.75){//漫游点底部识别不到的区域,给一个地板高度
+                //let height = origin.distanceTo(currentPano.floorPosition); 
+                const margin =  0.1
+                distance = (currentPano.floorPosition.z - origin.z - margin) / dir.z
+                location.copy(dir).multiplyScalar(distance).add(origin);
+                let normal = new THREE.Vector3(0,0,1)
+                
+                return {location, normal, distance} 
+            }
+            else return !1;  //应该是天空或模型外 , 因为很少有漫游点的地方还拍不到地板
+        }  
+        
+        //this.mainDepth = depth
+       
+        location.copy(dir).multiplyScalar(distance).add(origin);
+        
+        if(!onlyPos){
+           
+           var pL = this.getNearbyPoint(origin, uv, -1, 0)
+          , pR = this.getNearbyPoint(origin, uv,  1, 0)
+          , pB = this.getNearbyPoint(origin, uv,  0, -1)
+          , pT = this.getNearbyPoint(origin, uv,  0, 1);
+           
+            normal = this.planeFit(dir,location, pL,pR,pB,pT  )  
+        } */
+ 
+          
+        
+        //console.log('normal',normal)
+          
+          
+        normal = new THREE.Vector3(1,0,0)
+          
+          
+        return {location, normal,  distance} 
+    }
+    
+    
+    getNearbyPoint(   origin, uv, x, y) { //获取附近的若干像素距离的点
+        let uv2 = uv.clone()  
+        uv2.x += x/(this.canvas.width-1); 
+        uv2.x = this.clampUV(uv2.x)
+         
+        uv2.y += y/(this.canvas.height-1);
+        uv2.y = this.clampUV(uv2.y)
+        
+        /* if(uv2.x < 0 || uv2.y < 0 || uv2.x > 1 || uv2.y > 1){
+            console.log('will nan')
+        } */
+        
+        let dir = math.getDirFromUV(uv2)//从uv获取到方向
+        dir.applyMatrix4(viewer.images360.currentPano.panoMatrix2)
+        let depth = this.getDepth(uv2.x, uv2.y);
+        /* if(Math.abs(depth - this.mainDepth) > 0.3){
+            console.log('Math.abs(depth - this.mainDepth) > 0.3')
+        } */
+        
+        //let dir = new THREE.Vector3().subVectors(intersect.point, origin).normalize()
+        let position = new THREE.Vector3().copy(dir).multiplyScalar(depth).add(origin);
+        
+        //console.log('getNearbyPoint', uv2, depth, dir, position )
+        
+        return position
+    } 
+
+    clampUV(v){
+        return (v + 1) % 1;   //  使输出在 0-1
+    }
+    
+    planeFit(dir, position, pL,pR,pB,pT ) {//求平均法线
+        let normal = new THREE.Vector3
+         
+        
+        let plane = new THREE.Plane; 
+        function addNormal(p1, p2) {//根据临接的四个点,分别求法线,然后法线相加能得到平均法线
+            if(!p1 || !p2)return
+            plane.setFromCoplanarPoints(position, p1, p2)
+            
+            //console.log('normalSub', plane.normal)
+            
+            normal.addScaledVector(plane.normal,  dir.dot(plane.normal) < 0 ? 1 : -1)//根据面的朝向判断加还是减
+        }
+        addNormal(pL, pB) 
+        addNormal(pL, pT) 
+        addNormal(pR, pB) 
+        addNormal(pR, pT) 
+         
+        if(0 !== normal.x || 0 !== normal.y || 0 !== normal.z){ 
+            normal.normalize()
+            //console.log(normal)
+            return normal
+        }
+         
+         
+        /* 四个面拼成一个菱形 */        
+          
+    } 
+      
+    
+    
+}
+
+ /* 
+    注:
+     
+
+    由于有时候获取intersect需要知道是哪个点云,所以还是不能用这个。如加测量线。
+
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+export default DepthTexSampler
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 40 - 0
src/custom/utils/Tween.js

@@ -0,0 +1,40 @@
+import * as THREE from "../../../libs/three.js/build/three.module.js";
+import Common  from "./Common.js";
+
+
+class Tween {
+
+    constructor(times, values) {
+      this.times = times || []
+      this.values = values || []
+    }
+  
+    lerp(t) {
+      if(this.times.length == 0) return
+      let i = 0, n = this.times.length
+      while(i < n && t > this.times[i]) i++
+      if(i == 0) return this.values[0]
+      if(i == n) return this.values[n-1]
+      const ratio = (t - this.times[i-1]) / (this.times[i] - this.times[i-1])
+      /* if(this.values[0] instanceof THREE.Vector3 || ) {
+        return this.values[i-1].clone().lerp(this.values[i], ratio)
+      } else {
+        return this.values[i-1] + ratio * (this.values[i] - this.values[i-1])
+      } */
+      if(typeof this.values[0] == 'number'){
+        return this.values[i-1] + ratio * (this.values[i] - this.values[i-1])
+      }else{ 
+        let lerpFun = this.values[i-1].isQuaternion ? 'slerp' : 'lerp'
+        return this.values[i-1].clone()[lerpFun](this.values[i], ratio)
+      }
+    }
+    
+    
+    clone () {
+        return Common.CloneClassObject(this)
+	}
+    
+  
+  }
+  
+  export default Tween

+ 147 - 0
src/custom/utils/media/FlvVideoPlayerBase.js

@@ -0,0 +1,147 @@
+
+import browser from '../browser.js'
+
+ 
+
+function exitFullScreen() {
+    if (document.fullscreenElement && document.fullscreenElement.tagName === 'VIDEO' && document.fullscreenElement.getAttribute('unfullscreen')) {
+        if (document.exitFullscreen) {
+            document.exitFullscreen()
+        } else if (document.msExitFullscreen) {
+            document.msExitFullscreen()
+        } else if (document.mozCancelFullScreen) {
+            document.mozCancelFullScreen()
+        } else if (document.oRequestFullscreen) {
+            document.oCancelFullScreen()
+        } else if (document.webkitExitFullscreen) {
+            document.webkitExitFullscreen()
+        } else if (document.webkitCancelFullScreen) {
+            document.webkitCancelFullScreen()
+        }
+        setTimeout(() => {
+            exitFullScreen()
+        }, 250)
+    }
+}
+if (browser.detectAndroidMobile()) {
+    //针对Android横屏时video会自动全屏播放的bug #39505 #43421
+    window.addEventListener('resize', () => {
+        //resize时如果全屏元素为video, 退出全屏
+        setTimeout(() => {
+            exitFullScreen()
+        }, 500) //太快可能检测不到document.fullscreenElement
+    })
+
+    window.addEventListener('orientationchange', e => {
+        // 空间视频横屏时会触发自动全屏
+        if (window.orientation == 0 || window.orientation == 180) {
+            //竖屏
+            let videos = document.querySelectorAll('video[box]') //只针对有box属性的video标签(空间视频)
+            videos.forEach(item => {
+                item.remove()
+            })
+            setTimeout(() => {
+                videos.forEach(i => {
+                    document.body.appendChild(i)
+                    i.play()
+                })
+            }, 500)
+        } else {
+            // 横屏
+            let videos = document.querySelectorAll('video[box]')
+            videos.forEach(item => {
+                item.remove()
+            })
+            setTimeout(() => {
+                videos.forEach(i => {
+                    document.body.appendChild(i)
+                    i.play()
+                })
+            }, 500)
+        }
+    })
+}
+
+export default class FlvVideoPlayerBase     {
+    constructor(player) {
+        
+
+        this.player = player
+        this.instances = new Map()
+        this.video = null
+    }
+
+    //overlay info -- [metadata.overlay]
+    addVideo( url) {
+        let instance = this._createVideo(url)
+        this.instances.set(url, instance)
+        instance.videoElement.masters = [] //一个video可以对应多个主体,因为它们链接一样
+
+        return instance
+    }
+
+    getVideo(url, master) {
+        let instance = this.instances.get(url)
+
+        if (!instance) {
+            instance = this.addVideo(url)
+        }
+        let video = instance.videoElement
+        master && video.masters.push(master)
+        
+        
+        video.onloadedmetadata = () => {
+            video.canPlayed = true
+            video.masters.forEach(m => {
+                 m.dispatchEvent({ type: 'loadedmetadata' })
+            })
+        }
+        
+        return video
+    }
+
+    _getVideoPath(sid) {
+        //在子类中扩展
+    }
+
+    _createVideo(url) {
+        let video = document.createElement('video')
+        video.setAttribute('crossOrigin', 'anonymous')
+        video.setAttribute('playsinline', 'true')
+        video.setAttribute('webkit-playsinline', 'true')
+        video.setAttribute('controls', 'true')
+        video.setAttribute('unfullscreen', 'true')
+
+        video.autoplay = false
+        video.muted = true
+        video.loop = true
+
+        video.style.position = 'fixed'
+        video.style.left = '0'
+        video.style.top = '0'
+        video.style.zIndex = '0'
+        video.style.width = '1px'
+        // video.style.display = 'none'
+        //video.isFirstPlay = true;
+        video.style.opacity = '0'
+        document.body.appendChild(video)
+
+        video.player = this
+
+        let player = flvjs.createPlayer({ type: 'flv', url: url })
+        player.videoElement = video
+        player.attachMediaElement(video)
+        player.on(flvjs.Events.ERROR, () => {
+            this._onPlayerError()
+            console.log('尝试使用mp4链接进行播放')
+            video.src = url.replace('.flv', '.mp4')
+        })
+        player.load()
+
+        return player
+    }
+
+    _onPlayerError() {
+        console.warn('视频加载失败')
+    }
+}

+ 67 - 0
src/custom/utils/media/H5VideoPlayerBase.js

@@ -0,0 +1,67 @@
+import browser from '../browser.js'
+
+export default class H5VideoPlayerBase   {
+    constructor( ) {
+       
+        
+
+        this.video = null
+        this.videos = new Map()
+    }
+
+    //overlay info -- [metadata.overlay]
+    addVideo( url) {
+        let video = this._createVideoElement(url)
+        this.videos.set(url, video)
+        video.masters = []
+
+        return video
+    }
+
+    getVideo(url, master) {
+        let video = this.videos.get(url)
+
+        if (!video) {
+            video = this.addVideo(url)
+        }
+        master && video.masters.push(master)
+        
+        video.onloadedmetadata = () => {
+            video.canPlayed = true
+            video.masters.forEach(m => {
+                 m.dispatchEvent({ type: 'loadedmetadata' })
+            })
+        }
+        
+        return video
+    }
+
+   
+    _createVideoElement(src) {
+        let video = document.createElement('video')
+        video.setAttribute('crossOrigin', 'anonymous')
+        video.setAttribute('playsinline', 'true')
+        video.setAttribute('x5-playsinline', 'true')
+        video.setAttribute('webkit-playsinline', 'true')
+        video.setAttribute('x5-video-player-type', 'h5')
+        video.setAttribute('controls', 'true')
+        video.setAttribute('x-webkit-airplay', 'allow')
+
+        video.autoplay = true
+        video.muted = true
+        video.loop = true
+        video.src = src
+
+        video.style.position = 'fixed'
+        video.style.left = '0'
+        video.style.top = '0'
+        video.style.zIndex = '1000'
+        video.style.width = '300px'
+        video.style.height = '300px'
+        //video.style.display = browser.urlHasValue('debug') ? 'block' : 'none'
+
+        //document.body.appendChild(video)
+
+        return video
+    }
+}