ExtendPointCloudOctree.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  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 = params.resolution || (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. pickMaterial.activeAttributeName = "rgba";
  180. pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
  181. let dataUrl = Potree.Utils.renderTargetToDataUrl(pickState.renderTarget, width, height, renderer)
  182. Potree.Common.downloadFile(dataUrl, 'screenshot.png')
  183. window.testScreen = 0
  184. pickMaterial.activeAttributeName = "indices";//indices
  185. renderer.clear(true, true, true);
  186. }
  187. }
  188. let pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType);
  189. let pointSize = getVal(params.pointSize, this.material.size);
  190. let nodes = this.nodesOnRay(this.visibleNodes, ray);
  191. if(window.testScreen){
  192. console.log('nodes.length', this.visibleNodes.length, nodes)
  193. }
  194. if (nodes.length === 0) {
  195. //console.log('nodes.length === 0')
  196. return null;
  197. }
  198. //console.log('nodes.length != 0', this.name)
  199. if (!this.pickState) {
  200. let scene = new THREE.Scene();
  201. let material = new ExtendPointCloudMaterial();
  202. material.activeAttributeName = "indices";//indices
  203. let renderTarget = new THREE.WebGLRenderTarget(
  204. 1, 1,
  205. { minFilter: THREE.LinearFilter,
  206. magFilter: THREE.NearestFilter,
  207. format: THREE.RGBAFormat }
  208. );
  209. this.pickState = {
  210. renderTarget: renderTarget,
  211. material: material,
  212. scene: scene
  213. };
  214. };
  215. let pickState = this.pickState;
  216. let pickMaterial = pickState.material;
  217. { // update pick material
  218. pickMaterial.pointSizeType = pointSizeType;
  219. //pickMaterial.shape = this.material.shape;
  220. pickMaterial.shape = Potree.PointShape.PARABOLOID;
  221. pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value;
  222. pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value;
  223. pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value;
  224. pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value;
  225. pickMaterial.size = pointSize;
  226. pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
  227. pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
  228. pickMaterial.classification = this.material.classification;
  229. pickMaterial.recomputeClassification();
  230. //pickClipped判断转移到上一层函数
  231. let {bigClipInBox,clipBoxes_in,clipBoxes_out} = this.material
  232. pickMaterial.setClipBoxes(bigClipInBox, clipBoxes_in, clipBoxes_out, [])
  233. this.updateMaterial(pickMaterial, nodes, camera, renderer, new THREE.Vector2(width, height));
  234. }
  235. pickState.renderTarget.setSize(width, height); //仅绘制屏幕大小的,而不乘以devicePixelRatio
  236. let pixelPos = new THREE.Vector2(params.x, params.y);
  237. let gl = renderer.getContext();
  238. //规定渲染范围,只渲染一小块
  239. /* renderer.setScissorTest(true);
  240. gl.enable(gl.SCISSOR_TEST);
  241. gl.scissor(
  242. parseInt(pixelPos.x - (pickWindowSize - 1) / 2),
  243. parseInt(pixelPos.y - (pickWindowSize - 1) / 2),
  244. parseInt(pickWindowSize), parseInt(pickWindowSize));
  245. */ //---这段没用
  246. pickState.renderTarget.scissor.set(parseInt(pixelPos.x - (pickWindowSize - 1) / 2), parseInt(pixelPos.y - (pickWindowSize - 1) / 2),parseInt(pickWindowSize), parseInt(pickWindowSize));
  247. pickState.renderTarget.scissorTest = true
  248. renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
  249. renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
  250. renderer.state.setBlending(THREE.NoBlending);
  251. { // RENDER
  252. renderer.setRenderTarget(pickState.renderTarget);
  253. //gl.clearColor(0, 0, 0, 0);
  254. renderer.setClearColor(0x000000, 0 )
  255. let tmp = this.material;
  256. this.material = pickMaterial;
  257. screenshot();
  258. pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget); //只绘制ray pick到的部分nodes
  259. this.material = tmp;
  260. }
  261. let clamp = (number, min, max) => Math.min(Math.max(min, number), max);
  262. let x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width));
  263. let y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height));
  264. /* let w = parseInt(Math.min(x + pickWindowSize, width) - x);
  265. let h = parseInt(Math.min(y + pickWindowSize, height) - y); */
  266. let pixelCount = pickWindowSize * pickWindowSize//w * h;
  267. let buffer = new Uint8Array(4 * pixelCount);
  268. //w<pickWindowSize会报错
  269. gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer); //这句花费最多时间 pc:2-4, 即使只有1*1像素
  270. renderer.clear(true, true, true); //绘制完就clear否则download的图会有上次的轨迹
  271. renderer.setRenderTarget(null);
  272. renderer.state.reset();
  273. renderer.setScissorTest(false);
  274. gl.disable(gl.SCISSOR_TEST);
  275. let pixels = buffer;
  276. let ibuffer = new Uint32Array(buffer.buffer); //四个数整合成一个
  277. // find closest hit inside pixelWindow boundaries
  278. let min = Number.MAX_VALUE;
  279. let hits = [], hits2 = [], rSquare;
  280. if(!params.all){
  281. let r = THREE.Math.clamp(Math.floor(pickWindowSize / 2),1, 40);
  282. rSquare = r * r
  283. }
  284. for (let u = 0; u < pickWindowSize; u++) {
  285. for (let v = 0; v < pickWindowSize; v++) {
  286. let offset = (u + v * pickWindowSize);
  287. let distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2);
  288. let pcIndex = pixels[4 * offset + 3];//nodes index(第四位)
  289. pixels[4 * offset + 3] = 0; //去除nodes index后剩下的是index(前三位)
  290. let pIndex = ibuffer[offset]; //index
  291. if(!(pcIndex === 0 && pIndex === 0) && (pcIndex !== undefined) && (pIndex !== undefined)){
  292. let hit = {
  293. pIndex: pIndex,
  294. pcIndex: pcIndex,
  295. distanceToCenter: distance
  296. };
  297. if(params.all){
  298. hits.push(hit);
  299. }else{
  300. if(hits.length > 0){//最后选取的是最靠近鼠标的那个pick
  301. if(distance < hits[0].distanceToCenter){
  302. hits[0] = hit;
  303. }
  304. }else{
  305. hits.push(hit);
  306. }
  307. if(distance < rSquare) hits2.push(hit); //add
  308. }
  309. }
  310. }
  311. }
  312. if(!params.all && Potree.settings.pickFrontPointRatio){
  313. if(hits2.length){//add
  314. hits = hits2
  315. }
  316. }
  317. // { // DEBUG: show panel with pick image
  318. // let img = Utils.pixelsArrayToImage(buffer, w, h);
  319. // let screenshot = img.src;
  320. // if(!this.debugDIV){
  321. // this.debugDIV = $(`
  322. // <div id="pickDebug"
  323. // style="position: absolute;
  324. // right: 400px; width: 300px;
  325. // bottom: 44px; width: 300px;
  326. // z-index: 1000;
  327. // "></div>`);
  328. // $(document.body).append(this.debugDIV);
  329. // }
  330. // this.debugDIV.empty();
  331. // this.debugDIV.append($(`<img src="${screenshot}"
  332. // style="transform: scaleY(-1); width: 300px"/>`));
  333. // //$(this.debugWindow.document).append($(`<img src="${screenshot}"/>`));
  334. // //this.debugWindow.document.write('<img src="'+screenshot+'"/>');
  335. // }
  336. for(let hit of hits){
  337. let point = {};
  338. if (!nodes[hit.pcIndex]) {
  339. return null;
  340. }
  341. let node = nodes[hit.pcIndex];
  342. let pc = node.sceneNode;
  343. let geometry = node.geometryNode.geometry;
  344. for(let attributeName in geometry.attributes){
  345. let attribute = geometry.attributes[attributeName];
  346. if (attributeName === 'position') {
  347. let x = attribute.array[3 * hit.pIndex + 0];
  348. let y = attribute.array[3 * hit.pIndex + 1];
  349. let z = attribute.array[3 * hit.pIndex + 2];
  350. let position = new THREE.Vector3(x, y, z);
  351. position.applyMatrix4( pc.matrixWorld );
  352. point[attributeName] = position;
  353. //add
  354. if(!params.all && Potree.settings.pickFrontPointRatio ){
  355. if(camera.type == 'OrthographicCamera'){
  356. let vec = new THREE.Vector3().subVectors(position, camera.position)
  357. hit.disSquare = vec.projectOnVector( cameraDir ).length(); //只考虑到相机的垂直距离
  358. }else{
  359. hit.disSquare = camera.position.distanceTo(position)
  360. }
  361. }
  362. } else if (attributeName === 'indices') {
  363. } else {
  364. let values = attribute.array.slice(attribute.itemSize * hit.pIndex, attribute.itemSize * (hit.pIndex + 1)) ;
  365. if(attribute.potree){
  366. const {scale, offset} = attribute.potree;
  367. values = values.map(v => v / scale + offset);
  368. }
  369. point[attributeName] = values;
  370. //debugger;
  371. //if (values.itemSize === 1) {
  372. // point[attribute.name] = values.array[hit.pIndex];
  373. //} else {
  374. // let value = [];
  375. // for (let j = 0; j < values.itemSize; j++) {
  376. // value.push(values.array[values.itemSize * hit.pIndex + j]);
  377. // }
  378. // point[attribute.name] = value;
  379. //}
  380. }
  381. }
  382. hit.point = point;
  383. }
  384. viewer.addTimeMark('pick','end')
  385. if(params.all){
  386. return hits.map(hit => hit.point);
  387. }else{
  388. if(hits.length === 0){
  389. return null;
  390. }else{
  391. if(Potree.settings.pickFrontPointRatio == 0){//如果不需要选偏离镜头近的,就要离中心近的即可
  392. //console.log(hits[0], hits2, pixelPos)
  393. return hits[0].point;
  394. }
  395. //为了防止透过点云缝隙,选到后排的点云,将选取的位置离相机的距离考虑进去。倾向选择离相机近、且离鼠标位置近的点。(否则按照原方案只选离鼠标位置最近的,可能从高楼不小心走到下层,导航选点也是)
  396. let sorted1 = hits.sort( (a, b) => a.disSquare - b.disSquare ).slice();
  397. let nearest = sorted1[0] //return nearest.point; //直接用最近点 在点云稀疏时不太跟手,如地面上,最近点往往在鼠标下方
  398. hits.forEach( hit=>{
  399. let disDiff = hit.disSquare - nearest.disSquare //和最近点的偏差
  400. hit.disDiff = disDiff
  401. hit.score = -hit.distanceToCenter - disDiff * Potree.settings.pickFrontPointRatio
  402. })
  403. let sorted2 = hits.sort( (a, b) => b.score - a.score );
  404. //console.log(sorted2[0].point.position.z )
  405. return sorted2[0].point;
  406. }
  407. }
  408. }
  409. // 设置点大小
  410. changePointSize(num, sizeFitToLevel) {
  411. let size, nodeMaxLevel
  412. //console.error('changePointSize',num)
  413. let dontRender = viewer.dealBeforeRender
  414. if(this.material.pointSizeType != PointSizeType.ATTENUATED){
  415. num && (size = num / Potree.config.material.realPointSize / 1.3)
  416. }else{
  417. let num_ = num
  418. if (num_ == void 0) {
  419. num_ = this.temp.pointSize
  420. } else {
  421. this.temp.pointSize = num_
  422. }
  423. num_ = num_ / (Potree.config.material.realPointSize / Potree.config.material.pointSize) //兼容
  424. num_ *= this.scale.x ;//for mergeEditor
  425. //num_ = Math.pow(num_, 1.05) * 5
  426. nodeMaxLevel = this.testMaxNodeCount >= Potree.config.testNodeCount1 ? this.nodeMaxLevel : Math.max(this.nodeMaxLevel, this.nodeMaxLevelPredict.max )//防止刚开始因nodeMaxLevel没涨完,导致过大的点云突然出现
  427. if(sizeFitToLevel || Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本: 近似将pointSizeType换成ADAPTIVE
  428. let str = this.temp.pointSize+':'+this.maxLevel+':'+ nodeMaxLevel
  429. let value = this.temp.sizeFitToLevel[str] //储存。防止每次渲染(反复切换density)都要算。
  430. if(value){
  431. size = value *= this.scale.x
  432. }else{
  433. if(this.maxLevel == Infinity)return
  434. let base = this.material.spacing / Math.pow(2, this.maxLevel) //点云大小在level为0时设置为spacing,每长一级,大小就除以2. (不同场景还是会有偏差)
  435. let r = nodeMaxLevel > 0 ? this.maxLevel / nodeMaxLevel : 0.5 //越大,越精细,需要越缩小
  436. base *= nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(r, 3*r+0.3 )) : 0.1 //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙(但是ortho是不用缩小的,如果能分开判断就好了)
  437. //base *= nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(this.maxLevel / nodeMaxLevel, 1.1)) : 0.1 //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙(但是ortho是不用缩小的,如果能分开判断就好了)
  438. size = base * 20 * num_/* * window.devicePixelRatio */
  439. //在t-8BCqxQAr93 会议室 和 t-e2Kb2iU 隧道 两个场景里调节,因为它们的spacing相差较大,观察会议室墙壁的龟裂程度
  440. this.temp.sizeFitToLevel[str] = size
  441. }
  442. }else{
  443. let base = 0.015 //2024.8.21 又要求需要不同场景点云大小相同,所以只能固定值了 (正式环境这两个场景 SG-5yxFuWhEMlg SG-XZNyvCDk0jz)
  444. //let base = this.material.spacing / Math.pow(2, nodeMaxLevel) //在保证最高级别大小刚好的情况下,不改变level降低后的大小
  445. //base的数值理论上应该是右侧算出来的,但发现有的场景nodeMaxLevel和nodeMaxLevelPredict差别较大的点云显示也过大,而直接换成固定值反而可以适应所有场景。该固定值来源于 getHighestNodeSpacing 最小值,修改了下。(会不会是我们的相机其实该值是固定的,根据该值算出的spacing才是有误差的? 如果换了相机是否要改值?)
  446. //2022-12-21又换回非固定值。因为有的场景如SS-t-t01myDqnfE的两个数据集密集程度差别很大,应该将稀疏点云的大小设置的大些。 但是这样的缺点是两个数据集因相接处有大有小无法融合。
  447. size = base * 20 * num_
  448. }
  449. //不同数据集点云最高级别时的间距不同,但不知道如何计算, 级别越高点云越密,spacing越大初始级别越密。 但实际并非2的level次方,底数可能1-2之间
  450. }
  451. //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 */)
  452. if(size){
  453. if(Potree.settings.sortCloudMat){//被废弃,不给material分组了
  454. this.size = size;this.material.size = size
  455. }else{
  456. this.material.size = size
  457. }
  458. }
  459. dontRender || viewer.dispatchEvent('content_changed')
  460. }
  461. // 设置点透明度
  462. changePointOpacity(num, canMoreThanOne ) {
  463. //num:0-1 navvis用的是亮度
  464. if (num == void 0) {
  465. num = this.temp.pointOpacity
  466. } else {
  467. this.temp.pointOpacity = num
  468. }
  469. let dontRender = viewer.dealBeforeRender //在执行beforeRender时更改的话不要发送content_changed 尤其分屏
  470. if(Potree.settings.notAdditiveBlending){
  471. return this.material.opacity = num
  472. }
  473. let opacity
  474. if (num == 1) {
  475. opacity = 1
  476. } else {
  477. let str = (Potree.settings.sizeFitToLevel?'sizeFit:':'')+ (canMoreThanOne ? 'canMoreThanOne:':'') +this.temp.pointOpacity+':'+this.maxLevel+':'+this.nodeMaxLevel
  478. let value = this.temp.opacity[str] //储存。防止每次渲染(反复切换density)都要算。
  479. if(value){
  480. opacity = value
  481. }else{
  482. if(Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本:
  483. let base = this.material.spacing / Math.pow(1.7, this.maxLevel) //随着level提高,点云重叠几率增多
  484. let minBase = this.material.spacing / Math.pow(1.7, this.nodeMaxLevel)
  485. let ratio = Math.min(1 / base, 1 / minBase / 3) //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
  486. opacity = base * ratio * num
  487. if(!canMoreThanOne){
  488. opacity = THREE.Math.clamp(opacity, 0, 0.999) //到1就不透明了(可能出现一段一样)
  489. }
  490. }else{
  491. let base = this.material.spacing / Math.pow(2, this.maxLevel)
  492. let minBase = this.material.spacing / Math.pow(2, this.nodeMaxLevel)
  493. //console.log(1 / base, 1 / minBase / 6)
  494. let ratio = Math.min(1 / base, 1 / minBase / 3) //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
  495. opacity = base * ratio * num
  496. if(!canMoreThanOne){
  497. opacity = THREE.Math.clamp(opacity, 0, 0.999) //到1就不透明了(可能出现一段一样)
  498. }
  499. }
  500. this.temp.opacity[str] = opacity
  501. }
  502. //缺点:防止颜色过亮主要是相机离远时,当在漫游点处由于离点云太近,可能会导致高质量点云看起来很暗。
  503. }
  504. //console.log('changePointOpacity ' + this.dataset_id + ', num : ' + num + ' , opacity : ' + this.material.opacity) //检查是否做到了低质量时num==opacity,中质量opacity稍小于num,高质量更小
  505. if(Potree.settings.sortCloudMat){
  506. this.opacity = opacity; this.material.opacity = opacity
  507. }else{
  508. this.material.opacity = opacity
  509. }
  510. dontRender || viewer.dispatchEvent('content_changed')
  511. }
  512. updateBound(){
  513. var boundingBox_ = this.pcoGeometry.tightBoundingBox.clone().applyMatrix4(this.matrixWorld)//tightBoundingBox是点云原始的bound,但经过(绕z)旋转后bound有所改变,比之前体积更大。
  514. this.bound = boundingBox_
  515. this.bound2 = this.getBoundWithPanos()
  516. }
  517. getBoundWithPanos(){//确保panoBound在内的bound
  518. let bound = this.bound.clone()
  519. this.panos.forEach(pano=>{
  520. let panoBound = new THREE.Box3
  521. panoBound.expandByPoint(pano.position)
  522. panoBound.expandByVector(new THREE.Vector3(1,1,1));//give pano a margin
  523. bound.union(panoBound)
  524. })
  525. return bound
  526. }
  527. getPanosBound(){//仅由所有pano构成的bound
  528. if(this.panos.length > 0){
  529. let minSize = new THREE.Vector3(1,1,1)
  530. this.panosBound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize)
  531. }else{
  532. this.panosBound = null
  533. }
  534. }
  535. getUnrotBoundPoint(type){//获取没有旋转的tightBounding的水平四个点
  536. //如果alighment支持任意轴旋转,水平面上看到的可能就是六边形了,失去意义,到时候不能用这个。也可以若只绕z旋转, 使用tightBoundingBox,否则bound
  537. let bound = this.pcoGeometry.tightBoundingBox
  538. if(type == 'all'){
  539. return [new THREE.Vector3(bound.min.x, bound.min.y, bound.min.z),
  540. new THREE.Vector3(bound.max.x, bound.min.y, bound.min.z),
  541. new THREE.Vector3(bound.max.x, bound.max.y,bound.min.z),
  542. new THREE.Vector3(bound.min.x, bound.max.y,bound.min.z),
  543. new THREE.Vector3(bound.min.x, bound.min.y, bound.max.z),
  544. new THREE.Vector3(bound.max.x, bound.min.y, bound.max.z),
  545. new THREE.Vector3(bound.max.x, bound.max.y,bound.max.z),
  546. new THREE.Vector3(bound.min.x, bound.max.y,bound.max.z),
  547. ].map(e=>e.applyMatrix4(this.matrixWorld))
  548. }else
  549. return [new THREE.Vector3(bound.min.x, bound.min.y,0),
  550. new THREE.Vector3(bound.max.x, bound.min.y,0),
  551. new THREE.Vector3(bound.max.x, bound.max.y,0),
  552. new THREE.Vector3(bound.min.x, bound.max.y,0),
  553. ].map(e=>e.applyMatrix4(this.matrixWorld))
  554. }
  555. getVolume(){
  556. /* var points = this.getUnrotBoundPoint() -----在只绕z轴旋转时这么写也行
  557. var area = Math.abs(math.getArea(points))
  558. return area * (this.bound.max.z - this.bound.min.z) */
  559. let bound = this.pcoGeometry.tightBoundingBox.clone()
  560. let size = bound.getSize(new THREE.Vector3)
  561. return size.x * size.y * size.z
  562. }
  563. ifContainsPoint(pos){//pos是否坐落于tightBound内
  564. /* if(!this.bound || !this.bound.containsPoint(pos))return ---这样写也行
  565. var points = this.getUnrotBoundPoint()
  566. return math.isPointInArea(points, null, pos) */
  567. //要把tightBoundingBox想象成一个volumeBox
  568. let box = this.pcoGeometry.tightBoundingBox
  569. let center = box.getCenter(new THREE.Vector3)
  570. let size = box.getSize(new THREE.Vector3)
  571. let boxMatrix = new THREE.Matrix4().setPosition(center.x,center.y,center.z);
  572. boxMatrix.scale(size);
  573. boxMatrix.premultiply(this.matrixWorld)
  574. return Potree.Utils.isIntersectBox(pos, boxMatrix)
  575. }
  576. intersectBox(boxWorldMatrix){
  577. let boxM = boxWorldMatrix.clone().premultiply(this.matrixWorld.clone().invert()); //box乘上点云逆矩阵 (因为点云的忽略掉其matrixWorld, 为了保持相对位置不变,box要左乘matrixWorld的逆)(因第一个参数bound不好变形,第二个参数box可以)
  578. return Potree.Utils.isIntersectBox(this.pcoGeometry.tightBoundingBox, boxM)
  579. }
  580. updateAttrAuto(){//add
  581. let attributes = this.root.geometryNode?.geometry.attributes || this.root.geometry?.attributes
  582. if(!attributes){
  583. console.log('updateAttrAuto unready, delay')
  584. return setTimeout(()=>{
  585. this.updateAttrAuto()
  586. },300)
  587. }
  588. this.material.activeAttributeName =
  589. Potree.settings.showClass && attributes.classification ? 'classification' :
  590. Potree.settings.showHotTemp && attributes.temp ? 'temp' :
  591. Potree.settings.showHotIr && attributes.ir ? 'ir' :
  592. Potree.settings.cloudAttributeName || 'rgba'
  593. }
  594. }