ExtendPointCloudOctree.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. import * as THREE from "../libs/three.js/build/three.module.js";
  2. import {ExtendPointCloudMaterial} from "./materials/ExtendPointCloudMaterial.js";
  3. import {PointCloudOctree} from './PointCloudOctree.js'
  4. import {PointSizeType } from "./defines.js";
  5. import math from './custom/utils/math.js'
  6. export class ExtendPointCloudOctree extends PointCloudOctree{
  7. constructor(geometry, material){
  8. material = material || new ExtendPointCloudMaterial();
  9. super(geometry, material)
  10. //xzw move from material 。 adaptive_point_size才使用
  11. /* this.visibleNodesTexture = Utils.generateDataTexture(2048, 1, new THREE.Color(0xffffff));
  12. this.visibleNodesTexture.minFilter = THREE.NearestFilter;
  13. this.visibleNodesTexture.magFilter = THREE.NearestFilter; */
  14. this.boundingBox = this.pcoGeometry.tightBoundingBox//this.pcoGeometry.boundingBox; //boundingBox是正方体,所以换掉
  15. this.boundingSphere = this.boundingBox.getBoundingSphere(new THREE.Sphere());
  16. this.nodeMaxLevel = 0;//add
  17. this.maxLevel = Infinity;
  18. this.temp = { sizeFitToLevel:{}, opacity:{}}//add
  19. //add
  20. this.panos = []
  21. this.matrixAutoUpdate = false //最好禁止updateMatrix 直接使用matrixWorld
  22. this.orientationUser = 0
  23. this.translateUser = new THREE.Vector3;
  24. this.rotateMatrix = new THREE.Matrix4;
  25. this.transformMatrix = new THREE.Matrix4;// 数据集的变化矩阵
  26. this.transformInvMatrix = new THREE.Matrix4;
  27. /* this.transformMatrix2 = new THREE.Matrix4;// 数据集的变化矩阵
  28. this.transformInvMatrix2 = new THREE.Matrix4; */
  29. this.rotateInvMatrix = new THREE.Matrix4;
  30. this.material.spacing = this.pcoGeometry.spacing;//初始化一下 以便于设置pointsize
  31. this.nodeMaxLevelPredict = this.predictNodeMaxLevel()//预测maxNodeLevel
  32. this.testMaxNodeCount = this.testMaxNodeCount2 = 0
  33. this._visible = true;
  34. this.pcoGeometry.addEventListener('updateNodeMaxLevel', this.updateNodeMaxLevel.bind(this))
  35. this.isPointcloud = true //add
  36. }
  37. /*
  38. 注释:node的level从最大的box 0开始。
  39. 且加载任意一个node必定也会加载它的所有祖先。(如visibleNodes中有一个level为4,则一定有3,2,1,0)
  40. visibleNodes就是所有可见的node,比如:
  41. 如果相机在0这个位置朝下,这时候的visibleNodes中只有一个level为0的node;
  42. 而如果朝上看,上方的几个node如果在视野中占据足够大的位置的话,就会加载。
  43. 如果相机在2这个位置朝上,这时候的visibleNodes中所包含的level为: 0,1,2
  44. ________________
  45. | | | |
  46. |__2| | |
  47. | 1 | 1 |
  48. |_______|_______|
  49. | |
  50. | |
  51. | 0 |
  52. |_______________|
  53. 查看box可在potree中开启
  54. */
  55. updateNodeMaxLevel(e){//目前点云包含node的最高level
  56. var level = Math.max(e.level, this.nodeMaxLevel)
  57. if(level != this.nodeMaxLevel){
  58. this.nodeMaxLevel = level
  59. //viewer.dispatchEvent({type:'updateNodeMaxLevel', pointcloud: this, nodeMaxLevel:level})
  60. //console.log('updateNodeMaxLevel ' + this.dataset_id + " : "+ this.nodeMaxLevel)
  61. this.setPointLevel()//重新计算
  62. if(!Potree.settings.sizeFitToLevel){
  63. this.changePointSize()
  64. }
  65. }
  66. }//注:在没有加载到真实的 nodeMaxLevel之前,点云会显示得偏大
  67. //panoEdit时比预测值小很多?
  68. testMaxNodeLevel(camera){//手动使maxLevel达到最高,从而迫使updateNodeMaxLevel。 因为Potree.settings.pointDensity 不为 'high'时,maxLevel不是所加载的最高,就很容易加载不出下一个层级,导致无法知道nodeMaxLevel
  69. if(this.testMaxNodeLevelDone ) return
  70. //if(this.nodeMaxLevel > this.nodeMaxLevelPredict.min )return
  71. if( this.nodeMaxLevel==0 )return true
  72. if(camera.type == "OrthographicCamera" || this.testMaxNodeCount < 50){
  73. if(!Potree.Utils.isInsideFrustum(this.bound, camera)){
  74. return true
  75. }
  76. }else if( !viewer.atDatasets.includes(this))return true //否则老远就count++
  77. let levels = this.visibleNodes.map(e=>e.getLevel())
  78. let actMaxLevel = Math.max.apply(null, levels) //实际加载到的最高的node level
  79. if(actMaxLevel < this.maxLevel)return true// 还没加载到能加载到的最高。 但在细节设置较低时,排除作用微弱。
  80. //尝试加载出更高级的level
  81. let old = this.maxLevel
  82. this.maxLevel = 12;
  83. //var visibleNodes1 = this.visibleNodes.map(e=>e.getLevel())
  84. //console.log('visibleNodes1',visibleNodes1)
  85. Potree.updatePointClouds([this], viewer.scene.getActiveCamera(), viewer.mainViewport.resolution );
  86. //不在camera可视范围内还是加载不出来。即使临时修改位置
  87. var visibleNodes2 = this.visibleNodes.map(e=>e.getLevel())
  88. //console.log('visibleNodes2',visibleNodes2)
  89. this.maxLevel = old;
  90. this.testMaxNodeCount ++
  91. viewer.testMaxNodeCount ++
  92. //console.log('testMaxNodeCount', this.dataset_id, this.testMaxNodeCount, 'nodeMaxLevel', this.nodeMaxLevel )
  93. if( this.testMaxNodeCount == Potree.config.testNodeCount1 ){//差不多等当前所在数据集nodeMaxLevel加载出来
  94. this.changePointSize() //重新更新一下大小。因之前用的是nodeMaxLevelPredict (防止刚开始因nodeMaxLevel没涨完,导致过大的点云突然出现
  95. }
  96. if(this.testMaxNodeCount > 100){
  97. console.log('testMaxNodeLevel次数超出,强制结束:',this.dataset_id, this.nodeMaxLevel, this.nodeMaxLevelPredict.min)
  98. this.testMaxNodeLevelDone = 'moreThanMaxCount'
  99. return; //在可以看见点云的情况下,超时,有可能是预测的max是错的
  100. }
  101. if(this.nodeMaxLevel < this.nodeMaxLevelPredict.min) return true //仍需要继续testMaxNodeLevel
  102. this.testMaxNodeCount2 ++; // 已经> this.nodeMaxLevelPredict.min 后,开始计数。因为min可能低于真实nodeMaxLevel所以要再试几次
  103. if(this.testMaxNodeCount2 < 50) return true //再试几次 ( 主要是细节调得低时需要多测几次才加载到
  104. this.testMaxNodeLevelDone = true
  105. }
  106. updateMatrixWorld(force){//add
  107. super.updateMatrixWorld(force);
  108. this.matrixWorldInverse = this.matrixWorld.clone().invert();
  109. }
  110. setPointLevel(){
  111. var pointDensity = Potree.settings.pointDensity
  112. var config = Potree.config.pointDensity[pointDensity];
  113. if(!config)return
  114. /* if(this.testingMaxLevel){
  115. this.maxLevel = 12;//先加载到最大的直到测试完毕。由于5个level为一组来加载,所以如果写4最高能加载到5,如果写5最高能加载到下一个级别的最高也就是10
  116. //console.log('maxLevel: '+e.maxLevel + ' testingMaxLevel中 ' )
  117. }else{ */
  118. let percent = config.percentByUser && Potree.settings.UserDensityPercent != void 0 ? Potree.settings.UserDensityPercent : config.maxLevelPercent
  119. this.maxLevel = Math.round( percent * this.nodeMaxLevel);
  120. //console.log('maxLevel: '+e.maxLevel + ', density : '+Potree.settings.pointDensity, ", percent :"+percent);
  121. if(Potree.settings.sizeFitToLevel){
  122. this.changePointSize()
  123. }
  124. this.changePointOpacity()
  125. //}
  126. viewer.dealBeforeRender || viewer.dispatchEvent('content_changed')
  127. }
  128. //预测可能的nodeMaxLevel:
  129. predictNodeMaxLevel(){//预测maxNodeLevel。 可能只适用于我们相机拍的点云
  130. let spacing = {min:0.005, max:0.014};//最小节的两点间的距离 ,获得方法:spacing / Math.pow(2, nodeMaxLevel)。 目前观测的我们自己拍的这个数值的范围大概是这样
  131. let min = Math.log2(this.material.spacing / spacing.max); //有见过最大是0.01368
  132. let max = Math.log2(this.material.spacing / spacing.min); //大部分是 0.006
  133. //console.log('predictNodeMaxLevel:', this.name , min, max )
  134. return {min, max}
  135. }
  136. getHighestNodeSpacing(){
  137. return this.material.spacing / Math.pow(2, this.nodeMaxLevel) //前提是这个nodeMaxLevel是准确的
  138. }
  139. updateMaterial (material, visibleNodes, camera, renderer, resolution) {//改
  140. material.fov = camera.fov * (Math.PI / 180);
  141. /* material.screenWidth = renderer.domElement.clientWidth;
  142. material.screenHeight = renderer.domElement.clientHeight; */
  143. material.resolution = resolution
  144. material.spacing = this.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z);
  145. material.near = camera.near;
  146. material.far = camera.far;
  147. material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x;
  148. }
  149. pick(viewer, viewport, camera, ray, params = {}){//改
  150. let renderer = viewer.renderer;
  151. let pRenderer = viewer.pRenderer;
  152. viewer.addTimeMark('pick','start')
  153. let getVal = (a, b) => a != void 0 ? a : b;
  154. let r0 = this.nodeMaxLevel > 0 ? this.maxLevel/this.nodeMaxLevel : 0.5
  155. let pickWindowSize_ = THREE.Math.clamp( Math.round((1.1-r0)*80), 5, 100)
  156. let pickWindowSize = getVal(params.pickWindowSize, pickWindowSize_ ); /* 65 */ //拾取像素边长,越小越精准,但点云稀疏的话可能容易出现识别不到的情况。 另外左下侧会有缝隙无法识别到,缝隙大小和这个值有关//突然发现pickWindowSize在一百以内的变化对pick费时影响甚微,1和100差1毫秒不到,但400时会多4毫秒
  157. if(camera.type == 'OrthographicCamera'){
  158. var cameraDir = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion)
  159. pickWindowSize *= 4 //pointsize比较大时截取太小会没多少点可以选
  160. }
  161. let pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false);
  162. let size = viewport ? viewport.resolution : renderer.getSize(new THREE.Vector2());
  163. let width = Math.ceil(getVal(params.width, size.width)); //renderTarget大小。影响识别精度
  164. let height = Math.ceil(getVal(params.height, size.height));
  165. let screenshot = ()=>{
  166. if(window.testScreen){
  167. let dataUrl = Potree.Utils.renderTargetToDataUrl(pickState.renderTarget, width, height, renderer)
  168. Common.downloadFile(dataUrl, 'screenshot.jpg') //为什么图片上不是只有pickWindowSize区域有颜色??
  169. window.testScreen = 0
  170. }
  171. }
  172. let pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType);
  173. let pointSize = getVal(params.pointSize, this.material.size);
  174. let nodes = this.nodesOnRay(this.visibleNodes, ray);
  175. if (nodes.length === 0) {
  176. return null;
  177. }
  178. //console.log('nodes.length != 0', this.name)
  179. if (!this.pickState) {
  180. let scene = new THREE.Scene();
  181. let material = new ExtendPointCloudMaterial();
  182. material.activeAttributeName = "indices";
  183. let renderTarget = new THREE.WebGLRenderTarget(
  184. 1, 1,
  185. { minFilter: THREE.LinearFilter,
  186. magFilter: THREE.NearestFilter,
  187. format: THREE.RGBAFormat }
  188. );
  189. this.pickState = {
  190. renderTarget: renderTarget,
  191. material: material,
  192. scene: scene
  193. };
  194. };
  195. let pickState = this.pickState;
  196. let pickMaterial = pickState.material;
  197. { // update pick material
  198. pickMaterial.pointSizeType = pointSizeType;
  199. //pickMaterial.shape = this.material.shape;
  200. pickMaterial.shape = Potree.PointShape.PARABOLOID;
  201. pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value;
  202. pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value;
  203. pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value;
  204. pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value;
  205. pickMaterial.activeAttributeName = "indices";
  206. pickMaterial.size = pointSize;
  207. pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
  208. pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
  209. pickMaterial.classification = this.material.classification;
  210. pickMaterial.recomputeClassification();
  211. //pickClipped判断转移到上一层函数
  212. let {bigClipInBox,clipBoxes_in,clipBoxes_out} = this.material
  213. pickMaterial.setClipBoxes(bigClipInBox, clipBoxes_in, clipBoxes_out, [])
  214. this.updateMaterial(pickMaterial, nodes, camera, renderer, new THREE.Vector2(width, height));
  215. }
  216. pickState.renderTarget.setSize(width, height); //仅绘制屏幕大小的,而不乘以devicePixelRatio
  217. let pixelPos = new THREE.Vector2(params.x, params.y);
  218. let gl = renderer.getContext();
  219. gl.enable(gl.SCISSOR_TEST);
  220. gl.scissor( //规定渲染范围,只渲染一小块
  221. parseInt(pixelPos.x - (pickWindowSize - 1) / 2),
  222. parseInt(pixelPos.y - (pickWindowSize - 1) / 2),
  223. parseInt(pickWindowSize), parseInt(pickWindowSize));
  224. renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
  225. renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
  226. renderer.state.setBlending(THREE.NoBlending);
  227. { // RENDER
  228. renderer.setRenderTarget(pickState.renderTarget);
  229. gl.clearColor(0, 0, 0, 0);
  230. renderer.clear(true, true, true);
  231. let tmp = this.material;
  232. this.material = pickMaterial;
  233. pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
  234. screenshot();
  235. this.material = tmp;
  236. }
  237. let clamp = (number, min, max) => Math.min(Math.max(min, number), max);
  238. let x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width));
  239. let y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height));
  240. /* let w = parseInt(Math.min(x + pickWindowSize, width) - x);
  241. let h = parseInt(Math.min(y + pickWindowSize, height) - y); */
  242. let pixelCount = pickWindowSize * pickWindowSize//w * h;
  243. let buffer = new Uint8Array(4 * pixelCount);
  244. //w<pickWindowSize会报错
  245. gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer); //这句花费最多时间 pc:2-4, 即使只有1*1像素
  246. renderer.setRenderTarget(null);
  247. renderer.state.reset();
  248. renderer.setScissorTest(false);
  249. gl.disable(gl.SCISSOR_TEST);
  250. let pixels = buffer;
  251. let ibuffer = new Uint32Array(buffer.buffer); //四个数整合成一个
  252. // find closest hit inside pixelWindow boundaries
  253. let min = Number.MAX_VALUE;
  254. let hits = [], hits2 = [], rSquare;
  255. if(!params.all){
  256. let r = THREE.Math.clamp(Math.floor(pickWindowSize / 2),1, 40);
  257. rSquare = r * r
  258. }
  259. for (let u = 0; u < pickWindowSize; u++) {
  260. for (let v = 0; v < pickWindowSize; v++) {
  261. let offset = (u + v * pickWindowSize);
  262. let distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2);
  263. let pcIndex = pixels[4 * offset + 3];//nodes index(第四位)
  264. pixels[4 * offset + 3] = 0; //去除nodes index后剩下的是index(前三位)
  265. let pIndex = ibuffer[offset]; //index
  266. if(!(pcIndex === 0 && pIndex === 0) && (pcIndex !== undefined) && (pIndex !== undefined)){
  267. let hit = {
  268. pIndex: pIndex,
  269. pcIndex: pcIndex,
  270. distanceToCenter: distance
  271. };
  272. if(params.all){
  273. hits.push(hit);
  274. }else{
  275. if(hits.length > 0){//最后选取的是最靠近鼠标的那个pick
  276. if(distance < hits[0].distanceToCenter){
  277. hits[0] = hit;
  278. }
  279. }else{
  280. hits.push(hit);
  281. }
  282. if(distance < rSquare) hits2.push(hit); //add
  283. }
  284. }
  285. }
  286. }
  287. if(!params.all){
  288. if(hits2.length){//add
  289. hits = hits2
  290. }
  291. }
  292. // { // DEBUG: show panel with pick image
  293. // let img = Utils.pixelsArrayToImage(buffer, w, h);
  294. // let screenshot = img.src;
  295. // if(!this.debugDIV){
  296. // this.debugDIV = $(`
  297. // <div id="pickDebug"
  298. // style="position: absolute;
  299. // right: 400px; width: 300px;
  300. // bottom: 44px; width: 300px;
  301. // z-index: 1000;
  302. // "></div>`);
  303. // $(document.body).append(this.debugDIV);
  304. // }
  305. // this.debugDIV.empty();
  306. // this.debugDIV.append($(`<img src="${screenshot}"
  307. // style="transform: scaleY(-1); width: 300px"/>`));
  308. // //$(this.debugWindow.document).append($(`<img src="${screenshot}"/>`));
  309. // //this.debugWindow.document.write('<img src="'+screenshot+'"/>');
  310. // }
  311. for(let hit of hits){
  312. let point = {};
  313. if (!nodes[hit.pcIndex]) {
  314. return null;
  315. }
  316. let node = nodes[hit.pcIndex];
  317. let pc = node.sceneNode;
  318. let geometry = node.geometryNode.geometry;
  319. for(let attributeName in geometry.attributes){
  320. let attribute = geometry.attributes[attributeName];
  321. if (attributeName === 'position') {
  322. let x = attribute.array[3 * hit.pIndex + 0];
  323. let y = attribute.array[3 * hit.pIndex + 1];
  324. let z = attribute.array[3 * hit.pIndex + 2];
  325. let position = new THREE.Vector3(x, y, z);
  326. position.applyMatrix4( pc.matrixWorld );
  327. point[attributeName] = position;
  328. //add
  329. if(!params.all){
  330. if(camera.type == 'OrthographicCamera'){
  331. let vec = new THREE.Vector3().subVectors(position, camera.position)
  332. hit.disSquare = vec.projectOnVector( cameraDir ).length(); //只考虑到相机的垂直距离
  333. }else{
  334. hit.disSquare = camera.position.distanceTo(position)
  335. }
  336. }
  337. } else if (attributeName === 'indices') {
  338. } else {
  339. let values = attribute.array.slice(attribute.itemSize * hit.pIndex, attribute.itemSize * (hit.pIndex + 1)) ;
  340. if(attribute.potree){
  341. const {scale, offset} = attribute.potree;
  342. values = values.map(v => v / scale + offset);
  343. }
  344. point[attributeName] = values;
  345. //debugger;
  346. //if (values.itemSize === 1) {
  347. // point[attribute.name] = values.array[hit.pIndex];
  348. //} else {
  349. // let value = [];
  350. // for (let j = 0; j < values.itemSize; j++) {
  351. // value.push(values.array[values.itemSize * hit.pIndex + j]);
  352. // }
  353. // point[attribute.name] = value;
  354. //}
  355. }
  356. }
  357. hit.point = point;
  358. }
  359. viewer.addTimeMark('pick','end')
  360. if(params.all){
  361. return hits.map(hit => hit.point);
  362. }else{
  363. if(hits.length === 0){
  364. return null;
  365. }else{
  366. //为了防止透过点云缝隙,选到后排的点云,将选取的位置离相机的距离考虑进去。倾向选择离相机近、且离鼠标位置近的点。(否则按照原方案只选离鼠标位置最近的,可能从高楼不小心走到下层,导航选点也是)
  367. let sorted1 = hits.sort( (a, b) => a.disSquare - b.disSquare ).slice();
  368. let nearest = sorted1[0] //return nearest.point; //直接用最近点 在点云稀疏时不太跟手,如地面上,最近点往往在鼠标下方
  369. let r = 10
  370. hits.forEach( hit=>{
  371. let disDiff = hit.disSquare - nearest.disSquare //和最近点的偏差
  372. hit.disDiff = disDiff
  373. hit.score = -hit.distanceToCenter - disDiff * r
  374. })
  375. let sorted2 = hits.sort( (a, b) => b.score - a.score );
  376. //console.log(sorted2[0].point.position.z )
  377. return sorted2[0].point;
  378. }
  379. }
  380. }
  381. // 设置点大小
  382. changePointSize(num, sizeFitToLevel) {
  383. let size, nodeMaxLevel
  384. let dontRender = viewer.dealBeforeRender
  385. if(this.material.pointSizeType != PointSizeType.ATTENUATED){
  386. num && (size = num / Potree.config.material.realPointSize / 1.3)
  387. }else{
  388. let num_ = num
  389. if (num_ == void 0) {
  390. num_ = this.temp.pointSize
  391. } else {
  392. this.temp.pointSize = num_
  393. }
  394. num_ = num_ / (Potree.config.material.realPointSize / Potree.config.material.pointSize) //兼容
  395. num_ = Math.pow(num_, 1.05) * 6
  396. nodeMaxLevel = this.testMaxNodeCount >= Potree.config.testNodeCount1 ? this.nodeMaxLevel : this.nodeMaxLevelPredict.max //防止刚开始因nodeMaxLevel没涨完,导致过大的点云突然出现
  397. if(sizeFitToLevel || Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本: 近似将pointSizeType换成ADAPTIVE
  398. let str = this.temp.pointSize+':'+this.maxLevel+':'+ nodeMaxLevel
  399. let value = this.temp.sizeFitToLevel[str] //储存。防止每次渲染(反复切换density)都要算。
  400. if(value){
  401. size = value
  402. }else{
  403. let base = this.material.spacing / Math.pow(2, this.maxLevel) //点云大小在level为0时设置为spacing,每长一级,大小就除以2. (不同场景还是会有偏差)
  404. let r = nodeMaxLevel > 0 ? this.maxLevel / nodeMaxLevel : 0.5 //越大,越精细,需要越缩小
  405. base *= nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(r, 3*r+0.3 )) : 0.1 //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙(但是ortho是不用缩小的,如果能分开判断就好了)
  406. //base *= nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(this.maxLevel / nodeMaxLevel, 1.1)) : 0.1 //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙(但是ortho是不用缩小的,如果能分开判断就好了)
  407. size = base * 5 * num_/* * window.devicePixelRatio */
  408. //在t-8BCqxQAr93 会议室 和 t-e2Kb2iU 隧道 两个场景里调节,因为它们的spacing相差较大,观察会议室墙壁的龟裂程度
  409. this.temp.sizeFitToLevel[str] = size
  410. }
  411. }else{
  412. /* let base = 0.007; */ let base = this.material.spacing / Math.pow(2, nodeMaxLevel) //点云大小在level为0时设置为spacing,每长一级,大小就除以2
  413. //base的数值理论上应该是右侧算出来的,但发现有的场景nodeMaxLevel和nodeMaxLevelPredict差别较大的点云显示也过大,而直接换成固定值反而可以适应所有场景。该固定值来源于 getHighestNodeSpacing 最小值,修改了下。(会不会是我们的相机其实该值是固定的,根据该值算出的spacing才是有误差的? 如果换了相机是否要改值?)
  414. //2022-12-21又换回非固定值。因为有的场景如SS-t-t01myDqnfE的两个数据集密集程度差别很大,应该将稀疏点云的大小设置的大些。 但是这样的缺点是两个数据集因相接处有大有小无法融合。
  415. size = base * 5 * num_ /* * window.devicePixelRatio */
  416. }
  417. }
  418. //console.log('changePointSize:' + this.dataset_id + ' , num: ' + (num && num.toPrecision(3)) + ' , size: ' + size.toPrecision(3), 'nodeMaxLevel', nodeMaxLevel.toPrecision(3), 'testMaxNodeCount',this.testMaxNodeCount /* this.material.spacing */)
  419. if(size){
  420. if(Potree.settings.sortCloudMat){//被废弃,不给material分组了
  421. this.size = size;this.material.size = size
  422. }else{
  423. this.material.size = size
  424. }
  425. }
  426. dontRender || viewer.dispatchEvent('content_changed')
  427. }
  428. // 设置点透明度
  429. changePointOpacity(num, canMoreThanOne ) {
  430. //num:0-1 navvis用的是亮度
  431. if (num == void 0) {
  432. num = this.temp.pointOpacity
  433. } else {
  434. this.temp.pointOpacity = num
  435. }
  436. let dontRender = viewer.dealBeforeRender //在执行beforeRender时更改的话不要发送content_changed 尤其分屏
  437. if(Potree.settings.notAdditiveBlending){
  438. return this.material.opacity = num
  439. }
  440. let opacity
  441. if (num == 1) {
  442. opacity = 1
  443. } else {
  444. let str = (Potree.settings.sizeFitToLevel?'sizeFit:':'')+ (canMoreThanOne ? 'canMoreThanOne:':'') +this.temp.pointOpacity+':'+this.maxLevel+':'+this.nodeMaxLevel
  445. let value = this.temp.opacity[str] //储存。防止每次渲染(反复切换density)都要算。
  446. if(value){
  447. opacity = value
  448. }else{
  449. if(Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本:
  450. let base = this.material.spacing / Math.pow(1.7, this.maxLevel) //随着level提高,点云重叠几率增多
  451. let minBase = this.material.spacing / Math.pow(1.7, this.nodeMaxLevel)
  452. let ratio = Math.min(1 / base, 1 / minBase / 3) //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
  453. opacity = base * ratio * num
  454. if(!canMoreThanOne){
  455. opacity = THREE.Math.clamp(opacity, 0, 0.999) //到1就不透明了(可能出现一段一样)
  456. }
  457. }else{
  458. let base = this.material.spacing / Math.pow(2, this.maxLevel)
  459. let minBase = this.material.spacing / Math.pow(2, this.nodeMaxLevel)
  460. //console.log(1 / base, 1 / minBase / 6)
  461. let ratio = Math.min(1 / base, 1 / minBase / 3) //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
  462. opacity = base * ratio * num
  463. if(!canMoreThanOne){
  464. opacity = THREE.Math.clamp(opacity, 0, 0.999) //到1就不透明了(可能出现一段一样)
  465. }
  466. }
  467. this.temp.opacity[str] = opacity
  468. }
  469. //缺点:防止颜色过亮主要是相机离远时,当在漫游点处由于离点云太近,可能会导致高质量点云看起来很暗。
  470. }
  471. //console.log('changePointOpacity ' + this.dataset_id + ', num : ' + num + ' , opacity : ' + this.material.opacity) //检查是否做到了低质量时num==opacity,中质量opacity稍小于num,高质量更小
  472. if(Potree.settings.sortCloudMat){
  473. this.opacity = opacity; this.material.opacity = opacity
  474. }else{
  475. this.material.opacity = opacity
  476. }
  477. dontRender || viewer.dispatchEvent('content_changed')
  478. }
  479. updateBound(){
  480. var boundingBox_ = this.pcoGeometry.tightBoundingBox.clone().applyMatrix4(this.matrixWorld)//tightBoundingBox是点云原始的bound,但经过(绕z)旋转后bound有所改变,比之前体积更大。
  481. this.bound = boundingBox_
  482. this.bound2 = this.getBoundWithPanos()
  483. }
  484. getBoundWithPanos(){//确保panoBound在内的bound
  485. let bound = this.bound.clone()
  486. this.panos.forEach(pano=>{
  487. let panoBound = new THREE.Box3
  488. panoBound.expandByPoint(pano.position)
  489. panoBound.expandByVector(new THREE.Vector3(1,1,1));//give pano a margin
  490. bound.union(panoBound)
  491. })
  492. return bound
  493. }
  494. getPanosBound(){//仅由所有pano构成的bound
  495. if(this.panos.length > 0){
  496. let minSize = new THREE.Vector3(1,1,1)
  497. this.panosBound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize)
  498. }else{
  499. this.panosBound = null
  500. }
  501. }
  502. getUnrotBoundPoint(type){//获取没有旋转的tightBounding的水平四个点
  503. //如果alighment支持任意轴旋转,水平面上看到的可能就是六边形了,失去意义,到时候不能用这个。也可以若只绕z旋转, 使用tightBoundingBox,否则bound
  504. let bound = this.pcoGeometry.tightBoundingBox
  505. if(type == 'all'){
  506. return [new THREE.Vector3(bound.min.x, bound.min.y, bound.min.z),
  507. new THREE.Vector3(bound.max.x, bound.min.y, bound.min.z),
  508. new THREE.Vector3(bound.max.x, bound.max.y,bound.min.z),
  509. new THREE.Vector3(bound.min.x, bound.max.y,bound.min.z),
  510. new THREE.Vector3(bound.min.x, bound.min.y, bound.max.z),
  511. new THREE.Vector3(bound.max.x, bound.min.y, bound.max.z),
  512. new THREE.Vector3(bound.max.x, bound.max.y,bound.max.z),
  513. new THREE.Vector3(bound.min.x, bound.max.y,bound.max.z),
  514. ].map(e=>e.applyMatrix4(this.matrixWorld))
  515. }else
  516. return [new THREE.Vector3(bound.min.x, bound.min.y,0),
  517. new THREE.Vector3(bound.max.x, bound.min.y,0),
  518. new THREE.Vector3(bound.max.x, bound.max.y,0),
  519. new THREE.Vector3(bound.min.x, bound.max.y,0),
  520. ].map(e=>e.applyMatrix4(this.matrixWorld))
  521. }
  522. getVolume(){
  523. /* var points = this.getUnrotBoundPoint() -----在只绕z轴旋转时这么写也行
  524. var area = Math.abs(math.getArea(points))
  525. return area * (this.bound.max.z - this.bound.min.z) */
  526. let bound = this.pcoGeometry.tightBoundingBox.clone()
  527. let size = bound.getSize(new THREE.Vector3)
  528. return size.x * size.y * size.z
  529. }
  530. ifContainsPoint(pos){//pos是否坐落于tightBound内
  531. /* if(!this.bound || !this.bound.containsPoint(pos))return ---这样写也行
  532. var points = this.getUnrotBoundPoint()
  533. return math.isPointInArea(points, null, pos) */
  534. //要把tightBoundingBox想象成一个volumeBox
  535. let box = this.pcoGeometry.tightBoundingBox
  536. let center = box.getCenter(new THREE.Vector3)
  537. let size = box.getSize(new THREE.Vector3)
  538. let boxMatrix = new THREE.Matrix4().setPosition(center.x,center.y,center.z);
  539. boxMatrix.scale(size);
  540. boxMatrix.premultiply(this.matrixWorld)
  541. return Potree.Utils.isIntersectBox(pos, boxMatrix)
  542. }
  543. intersectBox(boxWorldMatrix){
  544. let boxM = boxWorldMatrix.clone().premultiply(this.matrixWorld.clone().invert()); //box乘上点云逆矩阵 (因为点云的忽略掉其matrixWorld, 为了保持相对位置不变,box要左乘matrixWorld的逆)(因第一个参数bound不好变形,第二个参数box可以)
  545. return Potree.Utils.isIntersectBox(this.pcoGeometry.tightBoundingBox, boxM)
  546. }
  547. }