PointCloudOctree.js 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195
  1. import * as THREE from "../libs/three.js/build/three.module.js";
  2. import {PointCloudTree, PointCloudTreeNode} from "./PointCloudTree.js";
  3. import {PointCloudOctreeGeometryNode} from "./PointCloudOctreeGeometry.js";
  4. import {Utils} from "./utils.js";
  5. import {PointCloudMaterial} from "./materials/PointCloudMaterial.js";
  6. export class PointCloudOctreeNode extends PointCloudTreeNode {
  7. constructor () {
  8. super();
  9. //this.children = {};
  10. this.children = [];
  11. this.sceneNode = null;
  12. this.octree = null;
  13. }
  14. getNumPoints () {
  15. return this.geometryNode.numPoints;
  16. }
  17. isLoaded () {
  18. return true;
  19. }
  20. isTreeNode () {
  21. return true;
  22. }
  23. isGeometryNode () {
  24. return false;
  25. }
  26. getLevel () {
  27. return this.geometryNode.level;
  28. }
  29. getBoundingSphere () {
  30. return this.geometryNode.boundingSphere;
  31. }
  32. getBoundingBox () {
  33. return this.geometryNode.boundingBox;
  34. }
  35. getChildren () {
  36. let children = [];
  37. for (let i = 0; i < 8; i++) {
  38. if (this.children[i]) {
  39. children.push(this.children[i]);
  40. }
  41. }
  42. return children;
  43. }
  44. getPointsInBox(boxNode){
  45. if(!this.sceneNode){
  46. return null;
  47. }
  48. let buffer = this.geometryNode.buffer;
  49. let posOffset = buffer.offset("position");
  50. let stride = buffer.stride;
  51. let view = new DataView(buffer.data);
  52. let worldToBox = boxNode.matrixWorld.clone().invert();
  53. let objectToBox = new THREE.Matrix4().multiplyMatrices(worldToBox, this.sceneNode.matrixWorld);
  54. let inBox = [];
  55. let pos = new THREE.Vector4();
  56. for(let i = 0; i < buffer.numElements; i++){
  57. let x = view.getFloat32(i * stride + posOffset + 0, true);
  58. let y = view.getFloat32(i * stride + posOffset + 4, true);
  59. let z = view.getFloat32(i * stride + posOffset + 8, true);
  60. pos.set(x, y, z, 1);
  61. pos.applyMatrix4(objectToBox);
  62. if(-0.5 < pos.x && pos.x < 0.5){
  63. if(-0.5 < pos.y && pos.y < 0.5){
  64. if(-0.5 < pos.z && pos.z < 0.5){
  65. pos.set(x, y, z, 1).applyMatrix4(this.sceneNode.matrixWorld);
  66. inBox.push(new THREE.Vector3(pos.x, pos.y, pos.z));
  67. }
  68. }
  69. }
  70. }
  71. return inBox;
  72. }
  73. get name () {
  74. return this.geometryNode.name;
  75. }
  76. };
  77. export class PointCloudOctree extends PointCloudTree {
  78. constructor (geometry, material) {
  79. super();
  80. this.pointBudget = Infinity;
  81. this.pcoGeometry = geometry;
  82. this.boundingBox = this.pcoGeometry.boundingBox;
  83. this.boundingSphere = this.boundingBox.getBoundingSphere(new THREE.Sphere());
  84. this.material = material || new PointCloudMaterial();
  85. this.visiblePointsTarget = 2 * 1000 * 1000;
  86. this.minimumNodePixelSize = 150;
  87. this.level = 0;
  88. this.position.copy(geometry.offset);
  89. this.updateMatrix();
  90. {
  91. let priorityQueue = ["rgba", "rgb", "intensity", "classification"];
  92. let selected = "rgba";
  93. for(let attributeName of priorityQueue){
  94. let attribute = this.pcoGeometry.pointAttributes.attributes.find(a => a.name === attributeName);
  95. if(!attribute){
  96. continue;
  97. }
  98. let min = attribute.range[0].constructor.name === "Array" ? attribute.range[0] : [attribute.range[0]];
  99. let max = attribute.range[1].constructor.name === "Array" ? attribute.range[1] : [attribute.range[1]];
  100. let range_min = new THREE.Vector3(...min);
  101. let range_max = new THREE.Vector3(...max);
  102. let range = range_min.distanceTo(range_max);
  103. if(range === 0){
  104. continue;
  105. }
  106. selected = attributeName;
  107. break;
  108. }
  109. this.material.activeAttributeName = selected;
  110. }
  111. this.showBoundingBox = false;
  112. this.boundingBoxNodes = [];
  113. this.loadQueue = [];
  114. this.visibleBounds = new THREE.Box3();
  115. this.visibleNodes = [];
  116. this.visibleGeometry = [];
  117. this.generateDEM = false;
  118. this.profileRequests = [];
  119. this.name = '';
  120. this._visible = true;
  121. //this._isVisible = true//add
  122. this.unvisibleReasons = []
  123. {
  124. let box = [this.pcoGeometry.tightBoundingBox, this.getBoundingBoxWorld()]
  125. .find(v => v !== undefined);
  126. this.updateMatrixWorld(true);
  127. box = Utils.computeTransformedBoundingBox(box, this.matrixWorld);
  128. let bMin = box.min.z;
  129. let bMax = box.max.z;
  130. this.material.heightMin = bMin;
  131. this.material.heightMax = bMax;
  132. }
  133. // TODO read projection from file instead
  134. this.projection = geometry.projection;
  135. this.fallbackProjection = geometry.fallbackProjection;
  136. this.root = this.pcoGeometry.root;
  137. }
  138. setName (name) {
  139. if (this.name !== name) {
  140. this.name = name;
  141. this.dispatchEvent({type: 'name_changed', name: name, pointcloud: this});
  142. }
  143. }
  144. getName () {
  145. return this.name;
  146. }
  147. getAttribute(name){
  148. const attribute = this.pcoGeometry.pointAttributes.attributes.find(a => a.name === name);
  149. if(attribute){
  150. return attribute;
  151. }else{
  152. return null;
  153. }
  154. }
  155. getAttributes(){
  156. return this.pcoGeometry.pointAttributes;
  157. }
  158. toTreeNode (geometryNode, parent) {
  159. let node = new PointCloudOctreeNode();
  160. // if(geometryNode.name === "r40206"){
  161. // console.log("creating node for r40206");
  162. // }
  163. let sceneNode = new THREE.Points(geometryNode.geometry, this.material);
  164. sceneNode.name = geometryNode.name;
  165. sceneNode.position.copy(geometryNode.boundingBox.min);
  166. sceneNode.frustumCulled = false;
  167. sceneNode.onBeforeRender = (_this, scene, camera, geometry, material, group) => {
  168. if (material.program) {
  169. _this.getContext().useProgram(material.program.program);
  170. if (material.program.getUniforms().map.level) {
  171. let level = geometryNode.getLevel();
  172. material.uniforms.level.value = level;
  173. material.program.getUniforms().map.level.setValue(_this.getContext(), level);
  174. }
  175. if (this.visibleNodeTextureOffsets && material.program.getUniforms().map.vnStart) {
  176. let vnStart = this.visibleNodeTextureOffsets.get(node);
  177. material.uniforms.vnStart.value = vnStart;
  178. material.program.getUniforms().map.vnStart.setValue(_this.getContext(), vnStart);
  179. }
  180. if (material.program.getUniforms().map.pcIndex) {
  181. let i = node.pcIndex ? node.pcIndex : this.visibleNodes.indexOf(node);
  182. material.uniforms.pcIndex.value = i;
  183. material.program.getUniforms().map.pcIndex.setValue(_this.getContext(), i);
  184. }
  185. }
  186. };
  187. // { // DEBUG
  188. // let sg = new THREE.SphereGeometry(1, 16, 16);
  189. // let sm = new THREE.MeshNormalMaterial();
  190. // let s = new THREE.Mesh(sg, sm);
  191. // s.scale.set(5, 5, 5);
  192. // s.position.copy(geometryNode.mean)
  193. // .add(this.position)
  194. // .add(geometryNode.boundingBox.min);
  195. //
  196. // viewer.scene.scene.add(s);
  197. // }
  198. node.geometryNode = geometryNode;
  199. node.sceneNode = sceneNode;
  200. node.pointcloud = this;
  201. node.children = [];
  202. //for (let key in geometryNode.children) {
  203. // node.children[key] = geometryNode.children[key];
  204. //}
  205. for(let i = 0; i < 8; i++){
  206. node.children[i] = geometryNode.children[i];
  207. }
  208. if (!parent) {
  209. this.root = node;
  210. this.add(sceneNode);
  211. } else {
  212. let childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]);
  213. parent.sceneNode.add(sceneNode);
  214. parent.children[childIndex] = node;
  215. }
  216. let disposeListener = function () {
  217. let childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]);
  218. parent.sceneNode.remove(node.sceneNode);
  219. parent.children[childIndex] = geometryNode;
  220. };
  221. geometryNode.oneTimeDisposeHandlers.push(disposeListener);
  222. return node;
  223. }
  224. updateVisibleBounds () {
  225. let leafNodes = [];
  226. for (let i = 0; i < this.visibleNodes.length; i++) {
  227. let node = this.visibleNodes[i];
  228. let isLeaf = true;
  229. for (let j = 0; j < node.children.length; j++) {
  230. let child = node.children[j];
  231. if (child instanceof PointCloudOctreeNode) {
  232. isLeaf = isLeaf && !child.sceneNode.visible;
  233. } else if (child instanceof PointCloudOctreeGeometryNode) {
  234. isLeaf = true;
  235. }
  236. }
  237. if (isLeaf) {
  238. leafNodes.push(node);
  239. }
  240. }
  241. this.visibleBounds.min = new THREE.Vector3(Infinity, Infinity, Infinity);
  242. this.visibleBounds.max = new THREE.Vector3(-Infinity, -Infinity, -Infinity);
  243. for (let i = 0; i < leafNodes.length; i++) {
  244. let node = leafNodes[i];
  245. this.visibleBounds.expandByPoint(node.getBoundingBox().min);
  246. this.visibleBounds.expandByPoint(node.getBoundingBox().max);
  247. }
  248. }
  249. updateMaterial (material, visibleNodes, camera, renderer) {
  250. material.fov = camera.fov * (Math.PI / 180);
  251. material.screenWidth = renderer.domElement.clientWidth;
  252. material.screenHeight = renderer.domElement.clientHeight;
  253. material.spacing = this.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z);
  254. material.near = camera.near;
  255. material.far = camera.far;
  256. material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.getSize(new THREE.Vector3()).x;
  257. }
  258. computeVisibilityTextureData(nodes, camera){
  259. if(Potree.measureTimings) performance.mark("computeVisibilityTextureData-start");
  260. let data = new Uint8Array(nodes.length * 4);
  261. let visibleNodeTextureOffsets = new Map();
  262. // copy array
  263. nodes = nodes.slice();
  264. // sort by level and index, e.g. r, r0, r3, r4, r01, r07, r30, ...
  265. let sort = function (a, b) {
  266. let na = a.geometryNode.name;
  267. let nb = b.geometryNode.name;
  268. if (na.length !== nb.length) return na.length - nb.length;
  269. if (na < nb) return -1;
  270. if (na > nb) return 1;
  271. return 0;
  272. };
  273. nodes.sort(sort);
  274. let worldDir = new THREE.Vector3();
  275. let nodeMap = new Map();
  276. let offsetsToChild = new Array(nodes.length).fill(Infinity);
  277. for(let i = 0; i < nodes.length; i++){
  278. let node = nodes[i];
  279. nodeMap.set(node.name, node);
  280. visibleNodeTextureOffsets.set(node, i);
  281. if(i > 0){
  282. let index = parseInt(node.name.slice(-1));
  283. let parentName = node.name.slice(0, -1);
  284. let parent = nodeMap.get(parentName);
  285. let parentOffset = visibleNodeTextureOffsets.get(parent);
  286. let parentOffsetToChild = (i - parentOffset);
  287. offsetsToChild[parentOffset] = Math.min(offsetsToChild[parentOffset], parentOffsetToChild);
  288. data[parentOffset * 4 + 0] = data[parentOffset * 4 + 0] | (1 << index);
  289. data[parentOffset * 4 + 1] = (offsetsToChild[parentOffset] >> 8);
  290. data[parentOffset * 4 + 2] = (offsetsToChild[parentOffset] % 256);
  291. }
  292. let density = node.geometryNode.density;
  293. if(typeof density === "number"){
  294. let lodOffset = Math.log2(density) / 2 - 1.5;
  295. let offsetUint8 = (lodOffset + 10) * 10;
  296. data[i * 4 + 3] = offsetUint8;
  297. }else{
  298. data[i * 4 + 3] = 100;
  299. }
  300. }
  301. if(Potree.measureTimings){
  302. performance.mark("computeVisibilityTextureData-end");
  303. performance.measure("render.computeVisibilityTextureData", "computeVisibilityTextureData-start", "computeVisibilityTextureData-end");
  304. }
  305. return {
  306. data: data,
  307. offsets: visibleNodeTextureOffsets
  308. };
  309. }
  310. nodeIntersectsProfile (node, profile) {
  311. let bbWorld = node.boundingBox.clone().applyMatrix4(this.matrixWorld);
  312. let bsWorld = bbWorld.getBoundingSphere(new THREE.Sphere());
  313. let intersects = false;
  314. for (let i = 0; i < profile.points.length - 1; i++) {
  315. let start = new THREE.Vector3(profile.points[i + 0].x, profile.points[i + 0].y, bsWorld.center.z);
  316. let end = new THREE.Vector3(profile.points[i + 1].x, profile.points[i + 1].y, bsWorld.center.z);
  317. let closest = new THREE.Line3(start, end).closestPointToPoint(bsWorld.center, true, new THREE.Vector3());
  318. let distance = closest.distanceTo(bsWorld.center);
  319. intersects = intersects || (distance < (bsWorld.radius + profile.width));
  320. }
  321. //console.log(`${node.name}: ${intersects}`);
  322. return intersects;
  323. }
  324. deepestNodeAt(position){
  325. const toObjectSpace = this.matrixWorld.clone().invert();
  326. const objPos = position.clone().applyMatrix4(toObjectSpace);
  327. let current = this.root;
  328. while(true){
  329. let containingChild = null;
  330. for(const child of current.children){
  331. if(child !== undefined){
  332. if(child.getBoundingBox().containsPoint(objPos)){
  333. containingChild = child;
  334. }
  335. }
  336. }
  337. if(containingChild !== null && containingChild instanceof PointCloudOctreeNode){
  338. current = containingChild;
  339. }else{
  340. break;
  341. }
  342. }
  343. const deepest = current;
  344. return deepest;
  345. }
  346. nodesOnRay (nodes, ray) {
  347. let nodesOnRay = [];
  348. let _ray = ray.clone();
  349. for (let i = 0; i < nodes.length; i++) {
  350. let node = nodes[i];
  351. let sphere = node.getBoundingSphere().clone().applyMatrix4(this.matrixWorld);
  352. if (_ray.intersectsSphere(sphere)) {
  353. nodesOnRay.push(node);
  354. }
  355. }
  356. return nodesOnRay;
  357. }
  358. updateMatrixWorld (force) {
  359. if (this.matrixAutoUpdate === true) this.updateMatrix();
  360. if (this.matrixWorldNeedsUpdate === true || force === true) {
  361. if (!this.parent) {
  362. this.matrixWorld.copy(this.matrix);
  363. } else {
  364. this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
  365. }
  366. this.matrixWorldNeedsUpdate = false;
  367. force = true;
  368. }
  369. }
  370. hideDescendants (object) {
  371. let stack = [];
  372. for (let i = 0; i < object.children.length; i++) {
  373. let child = object.children[i];
  374. if (child.visible) {
  375. stack.push(child);
  376. }
  377. }
  378. while (stack.length > 0) {
  379. let object = stack.shift();
  380. object.visible = false;
  381. for (let i = 0; i < object.children.length; i++) {
  382. let child = object.children[i];
  383. if (child.visible) {
  384. stack.push(child);
  385. }
  386. }
  387. }
  388. }
  389. moveToOrigin () {
  390. this.position.set(0, 0, 0);
  391. this.updateMatrixWorld(true);
  392. let box = this.boundingBox;
  393. let transform = this.matrixWorld;
  394. let tBox = Utils.computeTransformedBoundingBox(box, transform);
  395. this.position.set(0, 0, 0).sub(tBox.getCenter(new THREE.Vector3()));
  396. };
  397. moveToGroundPlane () {
  398. this.updateMatrixWorld(true);
  399. let box = this.boundingBox;
  400. let transform = this.matrixWorld;
  401. let tBox = Utils.computeTransformedBoundingBox(box, transform);
  402. this.position.y += -tBox.min.y;
  403. };
  404. getBoundingBoxWorld () {
  405. this.updateMatrixWorld(true);
  406. let box = this.boundingBox;
  407. let transform = this.matrixWorld;
  408. let tBox = Utils.computeTransformedBoundingBox(box, transform);
  409. return tBox;
  410. };
  411. /**
  412. * returns points inside the profile points
  413. *
  414. * maxDepth: search points up to the given octree depth
  415. *
  416. *
  417. * The return value is an array with all segments of the profile path
  418. * let segment = {
  419. * start: THREE.Vector3,
  420. * end: THREE.Vector3,
  421. * points: {}
  422. * project: function()
  423. * };
  424. *
  425. * The project() function inside each segment can be used to transform
  426. * that segments point coordinates to line up along the x-axis.
  427. *
  428. *
  429. */
  430. getPointsInProfile (profile, maxDepth, callback) {
  431. if (callback) {
  432. let request = new Potree.ProfileRequest(this, profile, maxDepth, callback);
  433. this.profileRequests.push(request);
  434. return request;
  435. }
  436. let points = {
  437. segments: [],
  438. boundingBox: new THREE.Box3(),
  439. projectedBoundingBox: new THREE.Box2()
  440. };
  441. // evaluate segments
  442. for (let i = 0; i < profile.points.length - 1; i++) {
  443. let start = profile.points[i];
  444. let end = profile.points[i + 1];
  445. let ps = this.getProfile(start, end, profile.width, maxDepth);
  446. let segment = {
  447. start: start,
  448. end: end,
  449. points: ps,
  450. project: null
  451. };
  452. points.segments.push(segment);
  453. points.boundingBox.expandByPoint(ps.boundingBox.min);
  454. points.boundingBox.expandByPoint(ps.boundingBox.max);
  455. }
  456. // add projection functions to the segments
  457. let mileage = new THREE.Vector3();
  458. for (let i = 0; i < points.segments.length; i++) {
  459. let segment = points.segments[i];
  460. let start = segment.start;
  461. let end = segment.end;
  462. let project = (function (_start, _end, _mileage, _boundingBox) {
  463. let start = _start;
  464. let end = _end;
  465. let mileage = _mileage;
  466. let boundingBox = _boundingBox;
  467. let xAxis = new THREE.Vector3(1, 0, 0);
  468. let dir = new THREE.Vector3().subVectors(end, start);
  469. dir.y = 0;
  470. dir.normalize();
  471. let alpha = Math.acos(xAxis.dot(dir));
  472. if (dir.z > 0) {
  473. alpha = -alpha;
  474. }
  475. return function (position) {
  476. let toOrigin = new THREE.Matrix4().makeTranslation(-start.x, -boundingBox.min.y, -start.z);
  477. let alignWithX = new THREE.Matrix4().makeRotationY(-alpha);
  478. let applyMileage = new THREE.Matrix4().makeTranslation(mileage.x, 0, 0);
  479. let pos = position.clone();
  480. pos.applyMatrix4(toOrigin);
  481. pos.applyMatrix4(alignWithX);
  482. pos.applyMatrix4(applyMileage);
  483. return pos;
  484. };
  485. }(start, end, mileage.clone(), points.boundingBox.clone()));
  486. segment.project = project;
  487. mileage.x += new THREE.Vector3(start.x, 0, start.z).distanceTo(new THREE.Vector3(end.x, 0, end.z));
  488. mileage.y += end.y - start.y;
  489. }
  490. points.projectedBoundingBox.min.x = 0;
  491. points.projectedBoundingBox.min.y = points.boundingBox.min.y;
  492. points.projectedBoundingBox.max.x = mileage.x;
  493. points.projectedBoundingBox.max.y = points.boundingBox.max.y;
  494. return points;
  495. }
  496. /**
  497. * returns points inside the given profile bounds.
  498. *
  499. * start:
  500. * end:
  501. * width:
  502. * depth: search points up to the given octree depth
  503. * callback: if specified, points are loaded before searching
  504. *
  505. *
  506. */
  507. getProfile (start, end, width, depth, callback) {
  508. let request = new Potree.ProfileRequest(start, end, width, depth, callback);
  509. this.profileRequests.push(request);
  510. };
  511. getVisibleExtent () {
  512. return this.visibleBounds.applyMatrix4(this.matrixWorld);
  513. };
  514. intersectsPoint(position){
  515. let rootAvailable = this.pcoGeometry.root && this.pcoGeometry.root.geometry;
  516. if(!rootAvailable){
  517. return false;
  518. }
  519. if(typeof this.signedDistanceField === "undefined"){
  520. const resolution = 32;
  521. const field = new Float32Array(resolution ** 3).fill(Infinity);
  522. const positions = this.pcoGeometry.root.geometry.attributes.position;
  523. const boundingBox = this.boundingBox;
  524. const n = positions.count;
  525. for(let i = 0; i < n; i = i + 3){
  526. const x = positions.array[3 * i + 0];
  527. const y = positions.array[3 * i + 1];
  528. const z = positions.array[3 * i + 2];
  529. const ix = parseInt(Math.min(resolution * (x / boundingBox.max.x), resolution - 1));
  530. const iy = parseInt(Math.min(resolution * (y / boundingBox.max.y), resolution - 1));
  531. const iz = parseInt(Math.min(resolution * (z / boundingBox.max.z), resolution - 1));
  532. const index = ix + iy * resolution + iz * resolution * resolution;
  533. field[index] = 0;
  534. }
  535. const sdf = {
  536. resolution: resolution,
  537. field: field,
  538. };
  539. this.signedDistanceField = sdf;
  540. }
  541. {
  542. const sdf = this.signedDistanceField;
  543. const boundingBox = this.boundingBox;
  544. const toObjectSpace = this.matrixWorld.clone().invert();
  545. const objPos = position.clone().applyMatrix4(toObjectSpace);
  546. const resolution = sdf.resolution;
  547. const ix = parseInt(resolution * (objPos.x / boundingBox.max.x));
  548. const iy = parseInt(resolution * (objPos.y / boundingBox.max.y));
  549. const iz = parseInt(resolution * (objPos.z / boundingBox.max.z));
  550. if(ix < 0 || iy < 0 || iz < 0){
  551. return false;
  552. }
  553. if(ix >= resolution || iy >= resolution || iz >= resolution){
  554. return false;
  555. }
  556. const index = ix + iy * resolution + iz * resolution * resolution;
  557. const value = sdf.field[index];
  558. if(value === 0){
  559. return true;
  560. }
  561. }
  562. return false;
  563. }
  564. /**
  565. *
  566. *
  567. *
  568. * params.pickWindowSize: Look for points inside a pixel window of this size.
  569. * Use odd values: 1, 3, 5, ...
  570. *
  571. *
  572. * TODO: only draw pixels that are actually read with readPixels().
  573. *
  574. */
  575. pick(viewer, camera, ray, params = {}){
  576. let renderer = viewer.renderer;
  577. let pRenderer = viewer.pRenderer;
  578. performance.mark("pick-start");
  579. let getVal = (a, b) => a !== undefined ? a : b;
  580. let pickWindowSize = getVal(params.pickWindowSize, 65); //拾取像素边长
  581. let pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false);
  582. let size = renderer.getSize(new THREE.Vector2());
  583. let width = Math.ceil(getVal(params.width, size.width)); //renderTarget大小。影响识别精度
  584. let height = Math.ceil(getVal(params.height, size.height));
  585. let pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType);
  586. let pointSize = getVal(params.pointSize, this.material.size);
  587. let nodes = this.nodesOnRay(this.visibleNodes, ray);
  588. if (nodes.length === 0) {
  589. return null;
  590. }
  591. if (!this.pickState) {
  592. let scene = new THREE.Scene();
  593. let material = new Potree.PointCloudMaterial();
  594. material.activeAttributeName = "indices";
  595. let renderTarget = new THREE.WebGLRenderTarget(
  596. 1, 1,
  597. { minFilter: THREE.LinearFilter,
  598. magFilter: THREE.NearestFilter,
  599. format: THREE.RGBAFormat }
  600. );
  601. this.pickState = {
  602. renderTarget: renderTarget,
  603. material: material,
  604. scene: scene
  605. };
  606. };
  607. let pickState = this.pickState;
  608. let pickMaterial = pickState.material;
  609. { // update pick material
  610. pickMaterial.pointSizeType = pointSizeType;
  611. //pickMaterial.shape = this.material.shape;
  612. pickMaterial.shape = Potree.PointShape.PARABOLOID;
  613. pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value;
  614. pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value;
  615. pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value;
  616. pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value;
  617. pickMaterial.activeAttributeName = "indices";
  618. pickMaterial.size = pointSize;
  619. pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
  620. pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
  621. pickMaterial.classification = this.material.classification;
  622. pickMaterial.recomputeClassification();
  623. if(params.pickClipped){
  624. pickMaterial.clipBoxes = this.material.clipBoxes;
  625. pickMaterial.uniforms.clipBoxes = this.material.uniforms.clipBoxes;
  626. if(this.material.clipTask === Potree.ClipTask.HIGHLIGHT){
  627. pickMaterial.clipTask = Potree.ClipTask.NONE;
  628. }else{
  629. pickMaterial.clipTask = this.material.clipTask;
  630. }
  631. pickMaterial.clipMethod = this.material.clipMethod;
  632. }else{
  633. pickMaterial.clipBoxes = [];
  634. }
  635. this.updateMaterial(pickMaterial, nodes, camera, renderer);
  636. }
  637. pickState.renderTarget.setSize(width, height);
  638. let pixelPos = new THREE.Vector2(params.x, params.y);
  639. let gl = renderer.getContext();
  640. gl.enable(gl.SCISSOR_TEST);
  641. gl.scissor(
  642. parseInt(pixelPos.x - (pickWindowSize - 1) / 2),
  643. parseInt(pixelPos.y - (pickWindowSize - 1) / 2),
  644. parseInt(pickWindowSize), parseInt(pickWindowSize));
  645. renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
  646. renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
  647. renderer.state.setBlending(THREE.NoBlending);
  648. { // RENDER
  649. renderer.setRenderTarget(pickState.renderTarget);
  650. gl.clearColor(0, 0, 0, 0);
  651. renderer.clear(true, true, true);
  652. let tmp = this.material;
  653. this.material = pickMaterial;
  654. pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
  655. this.material = tmp;
  656. }
  657. let clamp = (number, min, max) => Math.min(Math.max(min, number), max);
  658. let x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width));
  659. let y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height));
  660. /* let w = parseInt(Math.min(x + pickWindowSize, width) - x);
  661. let h = parseInt(Math.min(y + pickWindowSize, height) - y); */
  662. let pixelCount = pickWindowSize * pickWindowSize//w * h;
  663. let buffer = new Uint8Array(4 * pixelCount);
  664. //w<pickWindowSize会报错
  665. gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
  666. renderer.setRenderTarget(null);
  667. renderer.state.reset();
  668. renderer.setScissorTest(false);
  669. gl.disable(gl.SCISSOR_TEST);
  670. let pixels = buffer;
  671. let ibuffer = new Uint32Array(buffer.buffer);
  672. // find closest hit inside pixelWindow boundaries
  673. let min = Number.MAX_VALUE;
  674. let hits = [];
  675. for (let u = 0; u < pickWindowSize; u++) {
  676. for (let v = 0; v < pickWindowSize; v++) {
  677. let offset = (u + v * pickWindowSize);
  678. let distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2);
  679. let pcIndex = pixels[4 * offset + 3];
  680. pixels[4 * offset + 3] = 0;
  681. let pIndex = ibuffer[offset];
  682. if(!(pcIndex === 0 && pIndex === 0) && (pcIndex !== undefined) && (pIndex !== undefined)){
  683. let hit = {
  684. pIndex: pIndex,
  685. pcIndex: pcIndex,
  686. distanceToCenter: distance
  687. };
  688. if(params.all){
  689. hits.push(hit);
  690. }else{
  691. if(hits.length > 0){
  692. if(distance < hits[0].distanceToCenter){
  693. hits[0] = hit;
  694. }
  695. }else{
  696. hits.push(hit);
  697. }
  698. }
  699. }
  700. }
  701. }
  702. // { // DEBUG: show panel with pick image
  703. // let img = Utils.pixelsArrayToImage(buffer, w, h);
  704. // let screenshot = img.src;
  705. // if(!this.debugDIV){
  706. // this.debugDIV = $(`
  707. // <div id="pickDebug"
  708. // style="position: absolute;
  709. // right: 400px; width: 300px;
  710. // bottom: 44px; width: 300px;
  711. // z-index: 1000;
  712. // "></div>`);
  713. // $(document.body).append(this.debugDIV);
  714. // }
  715. // this.debugDIV.empty();
  716. // this.debugDIV.append($(`<img src="${screenshot}"
  717. // style="transform: scaleY(-1); width: 300px"/>`));
  718. // //$(this.debugWindow.document).append($(`<img src="${screenshot}"/>`));
  719. // //this.debugWindow.document.write('<img src="'+screenshot+'"/>');
  720. // }
  721. for(let hit of hits){
  722. let point = {};
  723. if (!nodes[hit.pcIndex]) {
  724. return null;
  725. }
  726. let node = nodes[hit.pcIndex];
  727. let pc = node.sceneNode;
  728. let geometry = node.geometryNode.geometry;
  729. for(let attributeName in geometry.attributes){
  730. let attribute = geometry.attributes[attributeName];
  731. if (attributeName === 'position') {
  732. let x = attribute.array[3 * hit.pIndex + 0];
  733. let y = attribute.array[3 * hit.pIndex + 1];
  734. let z = attribute.array[3 * hit.pIndex + 2];
  735. let position = new THREE.Vector3(x, y, z);
  736. position.applyMatrix4(pc.matrixWorld);
  737. point[attributeName] = position;
  738. } else if (attributeName === 'indices') {
  739. } else {
  740. let values = attribute.array.slice(attribute.itemSize * hit.pIndex, attribute.itemSize * (hit.pIndex + 1)) ;
  741. if(attribute.potree){
  742. const {scale, offset} = attribute.potree;
  743. values = values.map(v => v / scale + offset);
  744. }
  745. point[attributeName] = values;
  746. //debugger;
  747. //if (values.itemSize === 1) {
  748. // point[attribute.name] = values.array[hit.pIndex];
  749. //} else {
  750. // let value = [];
  751. // for (let j = 0; j < values.itemSize; j++) {
  752. // value.push(values.array[values.itemSize * hit.pIndex + j]);
  753. // }
  754. // point[attribute.name] = value;
  755. //}
  756. }
  757. }
  758. hit.point = point;
  759. }
  760. performance.mark("pick-end");
  761. performance.measure("pick", "pick-start", "pick-end");
  762. if(params.all){
  763. return hits.map(hit => hit.point);
  764. }else{
  765. if(hits.length === 0){
  766. return null;
  767. }else{
  768. return hits[0].point;
  769. //let sorted = hits.sort( (a, b) => a.distanceToCenter - b.distanceToCenter);
  770. //return sorted[0].point;
  771. }
  772. }
  773. };
  774. * getFittedBoxGen(boxNode){
  775. let start = performance.now();
  776. let shrinkedLocalBounds = new THREE.Box3();
  777. let worldToBox = boxNode.matrixWorld.clone().invert();
  778. for(let node of this.visibleNodes){
  779. if(!node.sceneNode){
  780. continue;
  781. }
  782. let buffer = node.geometryNode.buffer;
  783. let posOffset = buffer.offset("position");
  784. let stride = buffer.stride;
  785. let view = new DataView(buffer.data);
  786. let objectToBox = new THREE.Matrix4().multiplyMatrices(worldToBox, node.sceneNode.matrixWorld);
  787. let pos = new THREE.Vector4();
  788. for(let i = 0; i < buffer.numElements; i++){
  789. let x = view.getFloat32(i * stride + posOffset + 0, true);
  790. let y = view.getFloat32(i * stride + posOffset + 4, true);
  791. let z = view.getFloat32(i * stride + posOffset + 8, true);
  792. pos.set(x, y, z, 1);
  793. pos.applyMatrix4(objectToBox);
  794. if(-0.5 < pos.x && pos.x < 0.5){
  795. if(-0.5 < pos.y && pos.y < 0.5){
  796. if(-0.5 < pos.z && pos.z < 0.5){
  797. shrinkedLocalBounds.expandByPoint(pos);
  798. }
  799. }
  800. }
  801. }
  802. yield;
  803. }
  804. let fittedPosition = shrinkedLocalBounds.getCenter(new THREE.Vector3()).applyMatrix4(boxNode.matrixWorld);
  805. let fitted = new THREE.Object3D();
  806. fitted.position.copy(fittedPosition);
  807. fitted.scale.copy(boxNode.scale);
  808. fitted.rotation.copy(boxNode.rotation);
  809. let ds = new THREE.Vector3().subVectors(shrinkedLocalBounds.max, shrinkedLocalBounds.min);
  810. fitted.scale.multiply(ds);
  811. let duration = performance.now() - start;
  812. console.log("duration: ", duration);
  813. yield fitted;
  814. }
  815. getFittedBox(boxNode, maxLevel = Infinity){
  816. maxLevel = Infinity;
  817. let start = performance.now();
  818. let shrinkedLocalBounds = new THREE.Box3();
  819. let worldToBox = boxNode.matrixWorld.clone().invert();
  820. for(let node of this.visibleNodes){
  821. if(!node.sceneNode || node.getLevel() > maxLevel){
  822. continue;
  823. }
  824. let buffer = node.geometryNode.buffer;
  825. let posOffset = buffer.offset("position");
  826. let stride = buffer.stride;
  827. let view = new DataView(buffer.data);
  828. let objectToBox = new THREE.Matrix4().multiplyMatrices(worldToBox, node.sceneNode.matrixWorld);
  829. let pos = new THREE.Vector4();
  830. for(let i = 0; i < buffer.numElements; i++){
  831. let x = view.getFloat32(i * stride + posOffset + 0, true);
  832. let y = view.getFloat32(i * stride + posOffset + 4, true);
  833. let z = view.getFloat32(i * stride + posOffset + 8, true);
  834. pos.set(x, y, z, 1);
  835. pos.applyMatrix4(objectToBox);
  836. if(-0.5 < pos.x && pos.x < 0.5){
  837. if(-0.5 < pos.y && pos.y < 0.5){
  838. if(-0.5 < pos.z && pos.z < 0.5){
  839. shrinkedLocalBounds.expandByPoint(pos);
  840. }
  841. }
  842. }
  843. }
  844. }
  845. let fittedPosition = shrinkedLocalBounds.getCenter(new THREE.Vector3()).applyMatrix4(boxNode.matrixWorld);
  846. let fitted = new THREE.Object3D();
  847. fitted.position.copy(fittedPosition);
  848. fitted.scale.copy(boxNode.scale);
  849. fitted.rotation.copy(boxNode.rotation);
  850. let ds = new THREE.Vector3().subVectors(shrinkedLocalBounds.max, shrinkedLocalBounds.min);
  851. fitted.scale.multiply(ds);
  852. let duration = performance.now() - start;
  853. console.log("duration: ", duration);
  854. return fitted;
  855. }
  856. get progress () {
  857. return this.visibleNodes.length / this.visibleGeometry.length;
  858. }
  859. find(name){
  860. let node = null;
  861. for(let char of name){
  862. if(char === "r"){
  863. node = this.root;
  864. }else{
  865. node = node.children[char];
  866. }
  867. }
  868. return node;
  869. }
  870. get visible(){
  871. return this._visible;
  872. }
  873. set visible(value){
  874. if(value !== this._visible){
  875. this._visible = value;
  876. this.dispatchEvent({type: 'visibility_changed', pointcloud: this});
  877. }
  878. }
  879. updateVisible(reason, ifShow){//当所有加入的条件都不为false时才显示
  880. if(ifShow){
  881. var index = this.unvisibleReasons.indexOf(reason);
  882. index > -1 && this.unvisibleReasons.splice(index, 1);
  883. if(this.unvisibleReasons.length == 0)this.visible = true;
  884. }else {
  885. if(!this.unvisibleReasons.includes(reason)) this.unvisibleReasons.push(reason);
  886. this.visible = false;
  887. }
  888. }
  889. getVisible(reason){//获取在某条件下是否可见. 注: 用户在数据集选择可不可见为"datasetSelection"
  890. if(this.visible)return true
  891. else{
  892. return !this.unvisibleReasons.includes(reason)
  893. }
  894. }
  895. /* get isVisible(){//add 手动在数据集选择是否显示(和是否全景图、隐藏点云无关)
  896. return this._isVisible
  897. }
  898. set isVisible(visi){
  899. if(!visi)this.visible = false
  900. this._isVisible = visi
  901. } */
  902. }