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 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'; canvas.style.width = '1024px'; 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 = [] } 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) 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; h0) pano.depthData[h+'|'+w] = depth } } */ } 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') 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 */ 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 //未加载 /* 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() //var uv = intersect.uv //let dirInPano = math.getNormalDir(dir, currentPano)//转化为考虑漫游点旋转的方向 let dirInPano = dir.clone().applyMatrix4(currentPano.panoMatrix2Inverse).normalize(); //转化为考虑漫游点旋转的方向 let uv = math.getUVfromDir(dirInPano)//转化为uv 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){//漫游点底部识别不到的区域,给一个地板高度 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 if(uv.y < Potree.config.depthTexUVyLimit){ let ceilZ = currentPano.getCeilHeight() if(ceilZ == Infinity)return !1 else{ distance = (ceilZ - origin.z - margin) / dir.z location.copy(dir).multiplyScalar(distance).add(origin); let normal = new THREE.Vector3(0,0,-1) return {location, normal, distance} } } //console.log('无穷远') return !1; //应该是天空或模型外 , 因为很少有漫游点的地方还拍不到地板 } //console.log('distance', distance, dirInPano.clone().multiplyScalar(distance)) 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 ) } /* if(normal.x != normal.x ){ console.log('NAN', normal) 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); } */ //console.log(location, normal, distance) 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 DepthImageSampler