Panorama.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import {transitions, easing, lerp} from '../../utils/transitions.js'
  3. import TileUtils from './tile/TileUtils.js'
  4. import math from '../../utils/math.js'
  5. import {TextSprite} from '../../objects/TextSprite.js'
  6. import Sprite from '../../objects/Sprite.js'
  7. import DepthBasicMaterial from "../../materials/DepthBasicMaterial.js";
  8. let { PanoRendererEvents, PanoramaEvents, PanoSizeClass} = Potree.defines
  9. var texLoader = new THREE.TextureLoader()
  10. const markerOpacitys ={
  11. default : 0.4,
  12. hovered : 1,
  13. }
  14. const labelProp = {
  15. sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10},
  16. backgroundColor:{r: 255, g: 255, b: 255, a: 0.4 },
  17. textColor:{r: 0, g: 0, b: 0, a: 1 },
  18. borderRadius: 15,
  19. renderOrder:10,
  20. useDepth:true,
  21. clipDistance: 30, maxClipFactor:0.3, occlusionDistance:3,
  22. }
  23. const labelProp2 = {
  24. //sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10},
  25. backgroundColor:{r: 255, g: 255, b: 255, a: 0 },
  26. textColor:{r:255 , g: 255, b: 255, a: 1 },
  27. textBorderColor:{r:30 , g:30, b: 30, a: 1 },
  28. textBorderThick:3,
  29. dontFixOrient:true,
  30. renderOrder:10,
  31. fontsize:30,
  32. }
  33. let markerTex
  34. //显示全景图时marker没有被遮挡,如果需要,要换成depthBasicMaterial 或者直接把skybox的深度修改(拿到深度贴图后更如此)
  35. let planeGeo = new THREE.PlaneBufferGeometry(0.4,0.4);
  36. let sg = new THREE.SphereGeometry(0.1, 8, 8);
  37. let smHovered = new THREE.MeshBasicMaterial({/* side: THREE.BackSide, */color: 0xff0000});
  38. let sm = new THREE.MeshBasicMaterial({/* side: THREE.BackSide */});
  39. var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), Math.PI/2 ); //使用的是刚好适合全景图的,给cube贴图需要转90°
  40. //var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI/2 ); //4dkk->navvis
  41. //var rot901 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0), -Math.PI/2 ); //整张球幕图要旋转下
  42. //rot90 = new THREE.Quaternion().multiplyQuaternions( rot901, rot90)
  43. const rotQua2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), Math.PI )
  44. var old = null;
  45. /*
  46. 转成四维看看的axis:
  47. var a = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.Math.degToRad(-90)) 因为四维的要绕y转90
  48. 这里的quaternion.multiply(a);
  49. 先乘再换顺序 w : q.w, x:q.x , y:-q.z, z:q.y
  50. */
  51. //暂时直接用4dkkconsole输出的数据
  52. class Panorama extends THREE.EventDispatcher{
  53. constructor(o, images360){
  54. super()
  55. this.id = o.id; //唯一标识
  56. this.images360 = images360
  57. this.visible = true //for updateVisible
  58. this.enabled = true//是否可以走
  59. this.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
  60. //console.log('pano isVisible', this.id, e.visible)
  61. Potree.Utils.updateVisible(this.marker, 'panoVisi', e.visible)
  62. Potree.settings.showPanoMesh && (this.mesh.visible = e.visible)
  63. if(e.reason == 'screenshot' || e.visible){
  64. this.label && (this.label.visible = e.visible)//截图时隐藏下
  65. }
  66. this.label2 && Potree.Utils.updateVisible(this.label2, 'panoVisi', e.visible)
  67. })
  68. /*
  69. 漫游点可见性:新
  70. level reason 类型
  71. 2(最高)buildingChange(不在此楼层) unvisible
  72. 1 ifShowMarker(marker显示开关) unvisible
  73. 0 pointcloudVisi(隐藏了数据集) unvisible
  74. */
  75. if(Potree.settings.editType == 'pano'){//漫游点拼合编辑
  76. this.uuid = this.originID = o.uuid //对应4dkk中的id,可能不连续
  77. this.index = o.index //下标, 用于visibles
  78. this.pointcloud = viewer.scene.pointclouds.find(e=>e.panoUuid == o.uuid)
  79. this.pointcloud.panos.push(this)
  80. this.sid = this.pointcloud.dataset_id + '|' + this.uuid //不会更改的标记 用于entity.panos里的标记
  81. delete o.pointcloud
  82. this.panoData = o
  83. /* //数据中原本的位置朝向
  84. this.dataPosition = new THREE.Vector3().copy(o.pose.translation)
  85. this.dataQuaternion = new THREE.Quaternion().copy(o.pose.rotation)
  86. this.dataRotation = new THREE.Euler().setFromQuaternion(this.dataQuaternion) */
  87. //因为位置朝向随着点云位置改变,所以直接改变点云,这里清零
  88. this.originPosition = new THREE.Vector3()
  89. this.quaternion = new THREE.Quaternion().copy(rotQua2);
  90. this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion
  91. this.quaternion2 = this.quaternion.clone()
  92. this.quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
  93. const height = 1.4; //相机高度
  94. this.originFloorPosition = this.originPosition.clone()
  95. this.originFloorPosition.z -= height
  96. }else{
  97. this.originPosition = new THREE.Vector3().fromArray(o.dataset_location) //完全对应vision.txt的translation
  98. this.originFloorPosition = new THREE.Vector3().fromArray(o.dataset_floor_location)
  99. this.originID = parseInt(o.file_id)//"file_id":"00022" 对应是4dkk的id --来自vision.txt
  100. this.pointcloud = o.pointcloud //viewer.scene.pointclouds.find(e=>e.dataset_id == o.dataset_id) || viewer.scene.pointclouds[0]
  101. this.pointcloud.panos.push(this)
  102. //this.sid = this.pointcloud.sceneCode + '|' + this.originID //不会更改的标记
  103. this.sid = this.pointcloud.dataset_id + '|' + this.originID //不会更改的标记
  104. //全景图和Cube的水平采样起始坐标相差90度
  105. /* if(from4dkk){
  106. var qua = o.dataset_orientation
  107. var quaternion = new THREE.Quaternion().fromArray(qua)
  108. quaternion = new THREE.Quaternion().multiplyQuaternions(quaternion, rot901);//整张球幕图要旋转下 因为在4dkk里转过,还原。如果是tiles的不用
  109. this.quaternion = new THREE.Quaternion(quaternion.x, -quaternion.z, quaternion.y, quaternion.w) //转化坐标
  110. }else{ */
  111. var qua = o.dataset_orientation //完全对应vision.txt的rotation
  112. qua = [qua[1], qua[2], qua[3], qua[0]]
  113. this.quaternion = new THREE.Quaternion().fromArray(qua)
  114. this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion
  115. this.quaternion2 = this.quaternion.clone()
  116. this.quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
  117. this.rotation4dkk = new THREE.Euler().setFromQuaternion(this.quaternion4dkk)
  118. }
  119. this.neighbours = [];
  120. this.rotation = new THREE.Euler().setFromQuaternion(this.quaternion)
  121. this.build()
  122. this.transformByPointcloud() //初始化位移
  123. {//tile
  124. this.minimumTiledPanoLoaded = !1;
  125. this.highestPartialTileRenderOpCompleted = 0;
  126. this.highestFullTileRenderOpCompleted = 0;
  127. this.shouldRedrawOnBaseLoaded = !1;
  128. this.resolutionPromise = {}
  129. this.tiledPanoRenderTarget = null;
  130. this.zoomed = !1;
  131. images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderSuccess, this.onTileRendered.bind(this));
  132. images360.panoRenderer.addEventListener(PanoRendererEvents.PanoRenderComplete, this.onPanoRendered.bind(this));
  133. images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderFailure, this.onTileRenderFail.bind(this));
  134. images360.panoRenderer.addEventListener(PanoRendererEvents.UploadAttemptedForAllTiles, this.onUploadAttemptedForAllTiles.bind(this));
  135. }
  136. this.addEventListener('hoverOn', (e)=>{//from Map
  137. if(!e.byMainView){
  138. this.hoverOn(e)
  139. }
  140. })
  141. this.addEventListener('hoverOff', (e)=>{
  142. if(!e.byMainView){
  143. this.hoverOff(e)
  144. }
  145. })
  146. }
  147. get noNeighbour(){//是否绝对到不到的孤立点
  148. for(let i=0,j=this.images360.panos.length; i<j; i++){
  149. if(this.images360.neighbourMap[this.id][i] !== false ){
  150. return false
  151. }
  152. }
  153. return true
  154. //return this.neighbours.length == 0
  155. }
  156. setEnable(enable){//是否可以走
  157. Potree.Utils.updateVisible(this, 'isEnabled', enable) //令所有marker不可见
  158. this.enabled = enable
  159. viewer.dispatchEvent('content_changed')
  160. //如果当前在全景模式且在这个点,需要切换显示吗? 目前用不到
  161. }
  162. loadDepthImg(){
  163. if(!this.pointcloud.hasDepthTex || this.depthTex || this.depthTexLoading)return
  164. this.depthTexLoading = true
  165. let mapping = Potree.settings.isLocal2 ? '' : this.pointcloud.datasetData.mapping //非离线包的话加mapping
  166. let src
  167. if(Potree.settings.urls.templates.depthTex){
  168. src = Potree.Common.replaceAll(Potree.settings.urls.templates.depthTex, '{sceneCode}', this.pointcloud.sceneCode) + `/${this.originID}.png`
  169. }else{
  170. let prefix = Potree.settings.urls.getPrefix(1,this.pointcloud)//Potree.settings.urls.laserOSSRoot || this.pointcloud.prop?.raw?.laserOssRoot || Potree.settings.urls.prefix1
  171. src = `${prefix}/${mapping?(mapping+'/'):''}${Potree.settings.webSite}/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/depthmap/${this.originID}.png`
  172. }
  173. //console.log('开始下载depthImg', this.id)
  174. let texture = texLoader.load( src, ()=>{
  175. this.depthTex = texture
  176. this.dispatchEvent({type:'loadedDepthImg', loaded:true})
  177. this.images360.dispatchEvent({type:'loadedDepthImg', pano:this})
  178. this.depthTexLoading = false
  179. this.images360.updateDepthTex(this)
  180. },null,(e)=>{//error
  181. console.error('loadDepthImg失败, 数据集sceneCode'+ this.pointcloud.sceneCode, this.id )
  182. this.pointcloud.hasDepthTex = false
  183. this.dispatchEvent({type:'loadedDepthImg' })
  184. });
  185. texture.wrapS = THREE.RepeatWrapping;
  186. texture.flipY = false
  187. texture.magFilter = THREE.LinearFilter
  188. texture.minFilter = THREE.LinearFilter
  189. texture.generateMipmaps = false
  190. //平均一张0.75M。2048*1024。 和tile一样加载后永不删除,是否会造成崩溃?
  191. }
  192. /* loadTypeImg(type){
  193. if(!this['has_'+type] || this[type+'Tex'] || this[type+'Loading'])return
  194. let url = `${Potree.settings.urls.prefix1}/testdata/${Potree.settings.number}/data/${this.pointcloud.sceneCode}/imagemap/${this.originID}_${type}.png`
  195. //let url = `${Potree.settings.urls.prefix1}/testdata/${Potree.settings.number}/data/${this.pointcloud.sceneCode}/imagemap/${this.originID}_temp.png`
  196. let texture = texLoader.load( url, ()=>{
  197. this[type+'Tex'] = texture
  198. this.dispatchEvent({type:'loaded_'+type, loaded:true})
  199. this.images360.dispatchEvent({type:'loaded_'+type, pano:this})
  200. this[type+'Loading'] = false
  201. },null,(e)=>{//error
  202. console.error('load '+type+'Img失败, 数据集sceneCode'+ this.pointcloud.sceneCode, this.id )
  203. this['has_'+type] = false
  204. this['loadFailed_'+type] = false
  205. this.dispatchEvent({type:'loaded_'+type })
  206. });
  207. texture.wrapS = THREE.RepeatWrapping;
  208. texture.flipY = false
  209. texture.magFilter = THREE.LinearFilter
  210. texture.minFilter = THREE.LinearFilter
  211. texture.generateMipmaps = false
  212. } */
  213. loadTypeImg(type){
  214. if(!this['has_'+type] || this[type+'Tex'] || this[type+'Loading'])return
  215. let url = this.pointcloud.typesUrl + `${this.originID}_${type}.png`
  216. //let url = `${Potree.settings.urls.prefix1}/testdata/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/imagemap/${this.originID}_temp.png`
  217. let range = {min:Infinity, max:-Infinity, minPixel:null, maxPixel:null}
  218. let pixels
  219. let getRangeFun = (type == 'ir' || type == 'temp') && function(uint16Value, index, pixelCount){
  220. if(type == 'ir'){
  221. pixels || (pixels = new Uint16Array(pixelCount))
  222. pixels[index] = uint16Value
  223. }
  224. if(uint16Value != 0 && uint16Value < range.min){
  225. range.min = uint16Value
  226. range.minPixel_ = index
  227. }
  228. if(uint16Value != 0 && uint16Value > range.max){
  229. range.max = uint16Value
  230. range.maxPixel_ = index
  231. }
  232. }
  233. let texture = Potree.Common.load16bitPngTex(url,()=>{
  234. this[type+'Tex'] = texture
  235. if(getRangeFun){
  236. if(type == 'ir'){
  237. let p = [range.minPixel_, range.maxPixel_].map(index=>{
  238. let row = Math.floor(index / texture.image.width)
  239. let col = index % texture.image.width
  240. return {x:col,y:row}
  241. })
  242. range.minPixel = p[0]
  243. range.maxPixel = p[1]
  244. let pixelCount = texture.image.width * texture.image.height
  245. const stopMemberCount = 50;
  246. (['min','max']).forEach(name =>{
  247. let value = range[name]
  248. let groups = []
  249. let isNeigh = (A,B)=>{
  250. return (Math.abs(A.x - B.x)<=1 || Math.abs(A.x - B.x) == texture.image.width-1) && Math.abs(A.y - B.y)<=1
  251. }
  252. for(let i=0;i<pixelCount;i++){
  253. if(Math.abs(value - pixels[i]) == 0 ){
  254. let row = Math.floor(i / texture.image.width)
  255. let col = i % texture.image.width
  256. let data = {x:col, y:row, value: pixels[i]}
  257. Potree.Common.pushToGroupAuto([data], groups, isNeigh )
  258. if(groups.some(e=>e.length > stopMemberCount && e.some(e=>e==range[name]))){
  259. break
  260. }
  261. }
  262. }
  263. let groups2 = groups.filter(e=>e.length>1)
  264. if(groups2.length < 3) groups2 = groups
  265. groups2.forEach(group=>{//x的因边界不好写,就只判断y尽量接近中间且范围不大不小即可
  266. group.sort((a,b)=>{return a.y-b.y})
  267. let minY = group[0].y
  268. let maxY = group[group.length-1].y
  269. let centerY = (minY + maxY) / 2
  270. let rectRatio = (maxY - minY) * 1//除非竖的一列,否则一般y范围越大越好 Math.abs(2 - Potree.math.getBaseLog(maxY - minY, group.length)) //范围圆润度, 最好个数是y范围的平方
  271. group.score = group.length * 0.2 + rectRatio - Math.abs(texture.image.height/2 - centerY) * 0.5 //y尽量接近中间
  272. group.center = group[Math.floor(group.length/2)] //忽略是否横向的中间
  273. })
  274. groups2.sort((b,a)=>{return a.score - b.score})
  275. range[name+'Pixel'] = groups2[0]?.center
  276. //range[name+'PixelGroup'] = groups2
  277. //console.log('groups2', this.id, name,groups2)
  278. })
  279. }
  280. range.min /= 10, range.max /= 10
  281. texture.image.range = range
  282. viewer.gatherTempRange(type,range)
  283. }
  284. this.images360.waitDelTexDataList.push(texture)
  285. this.dispatchEvent({type:'loaded_'+type, loaded:true})
  286. this.images360.dispatchEvent({type:'loaded_'+type, pano:this})
  287. this[type+'Loading'] = false
  288. },(e)=>{
  289. console.error('load '+type+'Img失败, 数据集sceneCode'+ this.pointcloud.sceneCode, e, this.id )
  290. this['has_'+type] = false
  291. this['loadFailed_'+type] = false
  292. this.dispatchEvent({type:'loaded_'+type })
  293. }, getRangeFun)
  294. texture.wrapS = THREE.RepeatWrapping;
  295. texture.flipY = false
  296. texture.magFilter = THREE.LinearFilter
  297. texture.minFilter = THREE.LinearFilter
  298. texture.generateMipmaps = false
  299. }
  300. build(){
  301. { // orientation
  302. //add
  303. //var quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot901);//改 为球目全
  304. //quaternion.premultiply(rot90)
  305. this.panoMatrix = new THREE.Matrix4().makeRotationFromQuaternion(this.quaternion)
  306. this.oriPanoMatrix = this.panoMatrix.clone()
  307. if(this.quaternion2)this.oriPanoMatrix2 = new THREE.Matrix4().makeRotationFromQuaternion(this.quaternion2)
  308. //console.log(this.quaternion)
  309. //this.quaternion = quaternion
  310. }
  311. let marker = new THREE.Mesh(planeGeo, this.getMarkerMat() ) //new Sprite({mat:this.getMarkerMat(), dontFixOrient:true })
  312. marker.name = 'marker_'+this.id
  313. marker.up.set(0,0,1)
  314. this.addEventListener('changeMarkerTex',(e)=>{
  315. marker.material.map = markerTex[e.name]
  316. })
  317. this.marker = marker
  318. marker.pano = this;
  319. this.images360.node.add(marker)
  320. Potree.settings.isTest && this.addLabel()
  321. marker.addEventListener('mouseover', this.hoverOn.bind(this));
  322. marker.addEventListener('mouseleave', this.hoverOff.bind(this));
  323. }
  324. transformByPointcloud(){
  325. this.ceilZ = null //need reset
  326. let position = this.originPosition.clone().applyMatrix4(this.pointcloud.transformMatrix);//也可以用datasetPosTransform算
  327. let floorPosition = this.originFloorPosition.clone().applyMatrix4(this.pointcloud.transformMatrix);
  328. this.setPosition(position, floorPosition)
  329. this.panoMatrix = new THREE.Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix )
  330. //this.panoMatrix2 = Potree.Utils.datasetRotTransform({fromDataset:true, pointcloud:this.pointcloud, matrix:this.oriPanoMatrix, getMatrix:true}) //和上一行结果一样
  331. //quaternion也变下
  332. if(this.oriPanoMatrix2){
  333. this.panoMatrix2 = new THREE.Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix2 )//供DepthImageSampler使用
  334. this.panoMatrix2Inverse = this.panoMatrix2.clone().invert();
  335. }
  336. this.dispatchEvent('rePos')
  337. }
  338. setPosition(position, floorPosition){
  339. this.position = position
  340. this.floorPosition = floorPosition
  341. //this.mesh.position.copy(this.position)
  342. this.marker.position.copy(this.floorPosition)
  343. this.marker.lookAt(position) //融合页面marker可能跟随模型倾斜
  344. let upVec = new THREE.Vector3().subVectors(position, floorPosition).normalize().multiplyScalar(0.04*this.pointcloud.scale.x)
  345. this.marker.position.add(upVec) //this.marker.position.z+=0.04//会被点云遮住
  346. if(this.label){
  347. if(Potree.settings.editType == 'pano'){
  348. this.label.position.copy(this.position)
  349. }else{
  350. this.label.position.copy(this.floorPosition)
  351. }
  352. this.label.position.z+=0.14
  353. this.label.updatePose()
  354. }
  355. }
  356. /* getRealPos(){//当整体移动以后
  357. return this.position.clone().applyMatrix4(viewer.scene.scene.matrix)
  358. } */
  359. getMarkerMat(){
  360. if(!markerTex) {
  361. markerTex = {
  362. default:texLoader.load( Potree.resourcePath+'/textures/marker.png' ),
  363. ring:texLoader.load( Potree.resourcePath+'/textures/marker2.png' )
  364. }
  365. markerTex.default.anisotropy = 4 // 各向异性过滤 .防止倾斜模糊
  366. markerTex.ring.anisotropy = 4
  367. //有可能被点云遮住吗。
  368. }
  369. let mat = new DepthBasicMaterial({opacity: markerOpacitys.default, side: THREE.DoubleSide , map:markerTex.default ,transparent:true,
  370. clipDistance: 2, occlusionDistance:1, //不能设置太短,因为过渡时深度不准确
  371. useDepth: !!( Potree.settings.useDepthTex && this.pointcloud.hasDepthTex || Potree.settings.modelSkybox && this.pointcloud.is4dkkModel ),
  372. autoDepthTest: true,
  373. //改为DepthBasicMaterial是因为原Basic的材质在有深度图时过渡会先隐藏后出现。 注:没有深度图时全景模式的marker无法遮挡
  374. })
  375. mat.mapTransparent = true
  376. return mat
  377. }
  378. hoverOn(e={}) {
  379. //console.log("hoverOn " + this.id )
  380. transitions.start(lerp.property(this.marker.material, "opacity", markerOpacitys.hovered,()=>{
  381. viewer.dispatchEvent('content_changed')
  382. }), this.marker.visible ? 250 : 0)
  383. if(!e.byMap) this.dispatchEvent({type:'hoverOn', byMainView:true})
  384. if(!e.byImages360) this.images360.dispatchEvent({type:'markerHover', hovered:true, pano:this})
  385. }
  386. hoverOff(e={}){
  387. //console.log("hoverOff " + this.id )
  388. transitions.start(lerp.property(this.marker.material, "opacity", markerOpacitys.default,()=>{
  389. viewer.dispatchEvent('content_changed')
  390. }), this.marker.visible ? 250 : 0)
  391. if(!e.byMap) this.dispatchEvent({type:'hoverOff', byMainView:true})
  392. if(!e.byImages360) this.images360.dispatchEvent({type:'markerHover', hovered:false, pano:this})
  393. }
  394. setZoomed(zoomed){
  395. this.zoomed = zoomed;
  396. Potree.settings.displayMode == 'showPanos' && this.updateSkyboxForZoomLevel(); //放大后换成zoomTarget贴图
  397. viewer.dispatchEvent({type:'panoSetZoom', zoomed})
  398. }
  399. enter(){
  400. this.entered = true
  401. this.setZoomed(!1)
  402. viewer.dispatchEvent({type:PanoramaEvents.Enter, oldPano:old, newPano:this } )
  403. old = this
  404. //console.log("enter pano "+ this.id)
  405. }
  406. exit(){
  407. this.clearWaitDeferreds();
  408. this.minimumTiledPanoLoaded = !1;
  409. this.tiledPanoRenderTarget = null;
  410. this.setZoomed(!1);
  411. this.images360.panoRenderer.deactivateTiledPano(this);
  412. this.highestPartialTileRenderOpCompleted = 0;
  413. this.highestFullTileRenderOpCompleted = 0;
  414. this.depthTex && this.depthTex.dispose() //贴图不使用后先dispose,下次到该点时会自动还原
  415. this.entered = false //add
  416. //console.log("exit pano "+ this.id)
  417. viewer.dispatchEvent({type:PanoramaEvents.Exit, pano:this});
  418. }
  419. updateSkyboxForZoomLevel(){
  420. if(this.minimumTiledPanoLoaded){
  421. this.images360.updateProjectedPanos();
  422. }
  423. }
  424. getSkyboxTexture(){
  425. if(this.minimumTiledPanoLoaded)
  426. {
  427. if(this.zoomed && this.images360.qualityManager.maxRenderTargetSize > this.images360.qualityManager.maxNavPanoSize)//change 如果放大后和不放大都是2k就不用这个
  428. {
  429. return this.images360.panoRenderer.zoomRenderTarget.texture;
  430. }
  431. else
  432. {
  433. this.tiledPanoRenderTarget.texture.mapping = THREE.UVMapping//add
  434. return this.tiledPanoRenderTarget.texture;
  435. }
  436. }
  437. else
  438. {
  439. return null;
  440. }
  441. }
  442. isLoaded(e){
  443. if (e && "string" == typeof e)
  444. console.error("Wrong panoSize given to Panorama.isLoaded(); a tiled pano uses PanoSizeClass");
  445. return !!this.minimumTiledPanoLoaded && (!e || this.highestFullTileRenderOpCompleted >= e)//改:原本是:this.highestPartialTileRenderOpCompleted >= e, 希望这代表全部加载完
  446. }
  447. getWaitDeferred(size){//获取不同size的tile贴图的promiss
  448. var t = this.resolutionPromise[this.id];
  449. t || (t = {}, this.resolutionPromise[this.id] = t);
  450. var i = t[size];
  451. return i || (i = {
  452. deferred: $.Deferred(),
  453. active: !1
  454. },
  455. t[size] = i),
  456. i
  457. }
  458. clearWaitDeferreds(){
  459. var e = this.resolutionPromise[this.id];
  460. e || (e = {},
  461. this.resolutionPromise[this.id] = e);
  462. for (var t in e)
  463. if (e.hasOwnProperty(t)) {
  464. var i = e[t];
  465. i.active = !1,
  466. i.deferred = $.Deferred()
  467. }
  468. }
  469. resetWaitDeferred(e){
  470. var t = this.getWaitDeferred(e);
  471. t.active = !1;
  472. t.deferred = $.Deferred();
  473. }
  474. onTileRendered(ev){
  475. ev.id === this.id && this.dispatchEvent({
  476. type:PanoramaEvents.TileLoaded,
  477. size:ev.panoSize, index:ev.tileIndex, count:ev.totalTiles
  478. });
  479. }
  480. onPanoRendered(ev) {
  481. if(ev.id === this.id)
  482. {
  483. this.minimumTiledPanoLoaded = !0;
  484. this.updateSkyboxForZoomLevel();//更新贴图 setProjected
  485. ev.panoSize > this.highestPartialTileRenderOpCompleted && (this.highestPartialTileRenderOpCompleted = ev.panoSize);//应该是更新最高获取到的Partial size
  486. ev.updateFullComplete && ev.panoSize > this.highestFullTileRenderOpCompleted && (this.highestFullTileRenderOpCompleted = ev.panoSize); //应该是更新最高获取到的Full size
  487. //this.dispatchEvent("load", ev.panoSize);
  488. viewer.ifAllLoaded( this);
  489. this.dispatchEvent({type:PanoramaEvents.LoadComplete, size:ev.panoSize, count:ev.totalTiles});
  490. }
  491. }
  492. onTileRenderFail(ev) {
  493. ev.id === this.id && this.dispatchEvent({type:PanoramaEvents.LoadFailed });
  494. }
  495. onUploadAttemptedForAllTiles(ev) {
  496. if (ev.id === this.id) {
  497. var n = this.images360.qualityManager.getPanoSize(PanoSizeClass.BASE);
  498. if(ev.panoSize === n && this.shouldRedrawOnBaseLoaded) //shouldRedrawOnBaseLoaded一直是false。在4dkk里只有初始点在quickstart后变为true。
  499. {
  500. this.shouldRedrawOnBaseLoaded = !1;
  501. this.panoRenderer.resetRenderStatus(this.id, !0, !1);
  502. this.panoRenderer.renderPanoTiles(this.id, null, !0, !0);
  503. }
  504. }
  505. }
  506. addLabel(){
  507. this.removeTextLabel()
  508. this.label = new TextSprite(Object.assign({},
  509. labelProp, {text: this.id + "("+this.originID+")"}) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
  510. );
  511. this.images360.node.add(this.label);
  512. this.floorPosition && this.label.position.copy(this.floorPosition)
  513. }
  514. addLabel2(){
  515. if(this.label2)return
  516. this.label2 = new TextSprite(Object.assign({},
  517. labelProp2, {text: /* this.originID */ parseInt(this.id)+1 }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
  518. );
  519. //this.images360.node.add(this.label2);
  520. this.marker.add(this.label2)
  521. //this.floorPosition && this.label2.position.copy(this.floorPosition)
  522. let s = 0.2
  523. this.label2.scale.set(s,s,s)
  524. Potree.Utils.updateVisible(this.label2, 'notDisplay', false)
  525. Potree.Utils.updateVisible(this.label2, 'panoVisi', this.visible)
  526. //Potree.Utils.setObjectLayers(this.label2, 'bothMapAndScene')
  527. }
  528. removeTextLabel(){
  529. if(this.label){
  530. this.label.parent.remove(this.label);
  531. }
  532. }
  533. dispose(){
  534. let i = viewer.images360.panos.indexOf(this);
  535. if(i==-1)return
  536. viewer.images360.panos.splice(i,1);
  537. i = this.pointcloud.panos.indexOf(this)
  538. this.pointcloud.panos.splice(i,1);
  539. this.marker.parent.remove(this.marker)
  540. this.removeTextLabel()
  541. if(this.depthTex) this.depthTex.dispose()
  542. this.dispatchEvent('dispose')
  543. //删除tile贴图、depthTex等以后再写
  544. }
  545. getCeilHeight(){//天花板高度值 (假设不存在depth为0的点,所有为0的要么是在盲区,要么是无穷远。)
  546. if(this.ceilZ == void 0){
  547. //const depthTiming = Potree.timeCollect.depthSampler.median //pc firefox达到4. chrome为0.01
  548. //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算)
  549. let rotMat = new THREE.Matrix4().makeRotationX((Potree.config.depthTexUVyLimit+0.01)*Math.PI)// 角度不能小于天花板中空的半径
  550. let dir0 = new THREE.Vector3(0,0,1).applyMatrix4(rotMat)
  551. let dirs = [
  552. dir0,
  553. dir0.clone().applyMatrix4(new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3)),
  554. dir0.clone().applyMatrix4(new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3))
  555. ];
  556. /* if(depthTiming < 1){
  557. let rotMat1 = new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3);
  558. dirs.push(dirs[0].clone().applyMatrix4(rotMat1))
  559. }
  560. if(depthTiming < 0.3){
  561. let rotMat2 = new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3);
  562. dirs.push(dirs[0].clone().applyMatrix4(rotMat2));
  563. } */
  564. let zs = dirs.map(dir_=>{
  565. let dir = dir_.clone().applyMatrix4(this.panoMatrix2) //pano不一定是垂直的, 需要把之前的dirInPano先转成真实的dir,防止超出角度限制
  566. let intersect = viewer.images360.getIntersect(this, dir)
  567. let z = intersect ? intersect.location.z : Infinity/* this.position.z+skyHeight */ //没有intersect代表可能是天空
  568. return z
  569. })
  570. zs.sort((a,b)=>{return b-a});//得最大值 (不用中位数的原因:在屋檐处,如果仅有一个intersect是天空,因到了室外所以也用天空高度)
  571. this.ceilZ = zs[0]
  572. let min = this.position.z + 1 // 防止意外太低
  573. this.ceilZ = Math.max(min, this.ceilZ)
  574. //console.log(this.id, 'ceilZ:', this.ceilZ )
  575. }
  576. return this.ceilZ
  577. }
  578. };
  579. Panorama.prototype.loadTiledPano = function() {
  580. //var downloads = [] , t = [];
  581. var downloaded = {} , eventAdded = {}, latestPartialRequest = {}; //每个pano对应一组这些
  582. return function(size, dirs, fov, o, a, download) {
  583. var dir = dirs.datasetsLocal.find(e=>e.datasetId == this.pointcloud.dataset_id).direction;
  584. //var dir = dirs
  585. null !== o && void 0 !== o || (o = !0),
  586. null !== a && void 0 !== a || (a = !0);
  587. var l = this.getWaitDeferred(size)
  588. , c = l.deferred
  589. , h = null
  590. , u = null;
  591. fov && ("number" == typeof fov ? h = fov : (h = fov.hFov, u = fov.vFov))
  592. if (!this.isLoaded(size)) {
  593. //console.log('loadTiledPano', this.id, size, fov)
  594. if (!l.active) {
  595. l.active = !0
  596. let name = this.id + ":" + size
  597. downloaded[name] = downloaded[name] || []
  598. /*
  599. this.downloaded = downloaded
  600. this.latestPartialRequest = latestPartialRequest
  601. */
  602. latestPartialRequest[name] = null
  603. if (fov) {
  604. let tileArr = []//add
  605. var d = TileUtils.matchingTilesInDirection(this, size, dir, h, u, tileArr);
  606. latestPartialRequest[name] = tileArr
  607. downloaded[name].forEach((e)=>{
  608. let item = latestPartialRequest[name].find(a=>e.faceTileIndex == a.faceTileIndex && e.face == a.face)
  609. if(item){
  610. item.loaded = true
  611. }
  612. })
  613. if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的全部加载成功
  614. //let total = TileUtils.getTileCountForSize(size)
  615. //this.onPanoRendered(this.id, size, total, !0);
  616. c.resolve(size/* , total */);
  617. this.resetWaitDeferred(size)
  618. //console.log('该部分早已经加载好了'+size, this.id)
  619. latestPartialRequest[name] = null
  620. }
  621. //console.log("Loading partial pano: " + this.id + " with " + d + " tiles")
  622. }
  623. if(!eventAdded[this.id]) {
  624. eventAdded[this.id] = !0
  625. this.addEventListener(PanoramaEvents.LoadComplete, function(ev/* e, t */) {//本次任务全部加载完毕
  626. //console.warn('点位(可能部分)下载完成 ', 'id:'+this.id, 'size:'+ev.size )
  627. var i = this.getWaitDeferred(ev.size).deferred;//"pending"为还未完成
  628. i && "pending" === i.state() && this.highestPartialTileRenderOpCompleted >= ev.size && (i.resolve(ev.size, ev.count),
  629. this.resetWaitDeferred(ev.size))//恢复active为false
  630. }.bind(this))
  631. this.addEventListener(PanoramaEvents.LoadFailed, function(ev) {
  632. var t = this.getWaitDeferred(e).deferred;
  633. t && "pending" === t.state() && this.highestPartialTileRenderOpCompleted >= ev.t && (t.reject(ev.t),
  634. this.resetWaitDeferred(ev.t))//恢复active为false
  635. }.bind(this))
  636. this.addEventListener(PanoramaEvents.TileLoaded, function(ev/* t, i, n */) {//每张加载完时
  637. //console.log('tileLoaded', 'id:'+this.id, 'size:'+ev.size, 'tileIndex:'+ev.index )
  638. let tileIndex = ev.index
  639. let total = ev.count
  640. let size = ev.size
  641. let name = this.id + ":" + size
  642. downloaded[name] = downloaded[name] || [] //不是所有的加载都是从loadTiledPano获取的所以会有未定义的情况
  643. let {faceTileIndex,face} = TileUtils.getTileLocation(size, tileIndex, {})
  644. downloaded[name].push({faceTileIndex,face})
  645. var r = this.getWaitDeferred(size).deferred;
  646. if (r && "pending" === r.state()) {
  647. r.notify(size, tileIndex, total);
  648. if(latestPartialRequest[name]){
  649. let item = latestPartialRequest[name].find(e=>e.faceTileIndex == faceTileIndex && e.face == face)
  650. item && (item.loaded = true )
  651. if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的局部tiles全部加载成功
  652. this.onPanoRendered(this.id, size, total, !0); //onPanoRendered还会触发 PanoramaEvents.LoadComplete
  653. r.resolve(size, total);
  654. this.resetWaitDeferred(size)
  655. //console.log('该部分加载好了'+size, this.id)
  656. latestPartialRequest[name] = null
  657. }
  658. }
  659. }
  660. viewer.dispatchEvent('content_changed')
  661. /* var r = this.getWaitDeferred(ev.size).deferred;
  662. if (r && "pending" === r.state()) {
  663. r.notify(ev.size, ev.index, ev.count);
  664. var o = downloads[this.id + ":" + ev.size];
  665. if(o){//如果有规定下载哪些tile,只需要下载这些tile则LoadComplete
  666. o.tileCount++
  667. if(o.tileCount === o.targetTileCount){//达到下载目标数
  668. this.onPanoRendered(this.id, ev.size, ev.count, !0);
  669. r.resolve(ev.size, ev.count);
  670. this.resetWaitDeferred(ev.size)
  671. }
  672. }
  673. } */
  674. }.bind(this))
  675. }
  676. }
  677. this.images360.tileDownloader.clearForceQueue()
  678. this.images360.tileDownloader.forceQueueTilesForPano(this, size, dir, h, u, download)
  679. this.tiledPanoRenderTarget = this.images360.panoRenderer.activateTiledPano(this, this.images360.qualityManager.getMaxNavPanoSize(), o)
  680. this.images360.panoRenderer.renderPanoTiles(this.id, dirs, a) //将512的先贴上
  681. }else{
  682. //console.log('早已经全加载好了' +size, this.id)
  683. c.resolve(size)
  684. }
  685. return c.promise()
  686. }
  687. }()
  688. /*
  689. 经观察发现,navvis的也存在的问题是点云和全景有微小的偏差,导致远处的热点在全景和点云上看位置差别感大,比如一个在路上一个在天空上。
  690. */
  691. export default Panorama