ExtendPointCloudOctree.js 34 KB

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