Potree_update_visibility.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import * as THREE from "../libs/three.js/build/three.module.js";
  2. import {ClipTask, ClipMethod} from "./defines.js";
  3. import {Box3Helper} from "./utils/Box3Helper.js";
  4. export function updatePointClouds(pointclouds,camera, areaSize /* renderer */){
  5. for (let pointcloud of pointclouds) {
  6. let start = performance.now();
  7. for (let profileRequest of pointcloud.profileRequests) {
  8. profileRequest.update();
  9. let duration = performance.now() - start;
  10. if(duration > 5){
  11. break;
  12. }
  13. }
  14. let duration = performance.now() - start;
  15. }
  16. let result = updateVisibility(pointclouds, camera, areaSize );
  17. for (let pointcloud of pointclouds) {
  18. //pointcloud.updateMaterial(pointcloud.material, pointcloud.visibleNodes, camera, renderer);//转移到渲染时
  19. pointcloud.updateVisibleBounds();
  20. }
  21. exports.lru.freeMemory();
  22. return result;
  23. };
  24. export function updateVisibilityStructures(pointclouds, camera, areaSize) {
  25. let frustums = [];
  26. let camObjPositions = [];
  27. let priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; });
  28. for (let i = 0; i < pointclouds.length; i++) {
  29. let pointcloud = pointclouds[i];
  30. if (!pointcloud.initialized()) {
  31. continue;
  32. }
  33. pointcloud.numVisibleNodes = 0;
  34. pointcloud.numVisiblePoints = 0;
  35. pointcloud.deepestVisibleLevel = 0;
  36. pointcloud.visibleNodes = [];
  37. pointcloud.visibleGeometry = [];
  38. // frustum in object space
  39. camera.updateMatrixWorld();
  40. let frustum = new THREE.Frustum();
  41. let viewI = camera.matrixWorldInverse;
  42. let world = pointcloud.matrixWorld;
  43. // use close near plane for frustum intersection
  44. let frustumCam = camera.clone();
  45. frustumCam.near = Math.min(camera.near, 0.1);
  46. frustumCam.updateProjectionMatrix();
  47. let proj = camera.projectionMatrix;
  48. let fm = new THREE.Matrix4().multiply(proj).multiply(viewI).multiply(world);
  49. frustum.setFromProjectionMatrix(fm);
  50. frustums.push(frustum);
  51. // camera position in object space
  52. let view = camera.matrixWorld;
  53. let worldI = world.clone().invert();
  54. let camMatrixObject = new THREE.Matrix4().multiply(worldI).multiply(view);
  55. let camObjPos = new THREE.Vector3().setFromMatrixPosition(camMatrixObject);
  56. camObjPositions.push(camObjPos);
  57. if ( viewer.getObjVisiByReason(pointcloud, 'datasetSelection') && pointcloud.root !== null) {//改 visible ->
  58. priorityQueue.push({pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE});
  59. }
  60. // hide all previously visible nodes
  61. // if(pointcloud.root instanceof PointCloudOctreeNode){
  62. // pointcloud.hideDescendants(pointcloud.root.sceneNode);
  63. // }
  64. if (pointcloud.root.isTreeNode()) {
  65. pointcloud.hideDescendants(pointcloud.root.sceneNode);
  66. }
  67. for (let j = 0; j < pointcloud.boundingBoxNodes.length; j++) {
  68. pointcloud.boundingBoxNodes[j].visible = false;
  69. }
  70. }
  71. return {
  72. 'frustums': frustums,
  73. 'camObjPositions': camObjPositions,
  74. 'priorityQueue': priorityQueue
  75. };
  76. };
  77. export function updateVisibility(pointclouds, camera, areaSize){
  78. let numVisibleNodes = 0;
  79. let numVisiblePoints = 0;
  80. let numVisiblePointsInPointclouds = new Map(pointclouds.map(pc => [pc, 0]));
  81. let visibleNodes = [];
  82. let visibleGeometry = [];
  83. let unloadedGeometry = [];
  84. let lowestSpacing = Infinity;
  85. // calculate object space frustum and cam pos and setup priority queue
  86. let s = updateVisibilityStructures(pointclouds, camera, areaSize);//得到相机可见范围
  87. let frustums = s.frustums;
  88. let camObjPositions = s.camObjPositions;
  89. let priorityQueue = s.priorityQueue;
  90. let loadedToGPUThisFrame = 0;
  91. let domWidth = areaSize.x; //renderer.domElement.clientWidth;
  92. let domHeight = areaSize.y;//renderer.domElement.clientHeight;
  93. // check if pointcloud has been transformed
  94. // some code will only be executed if changes have been detected
  95. if(!Potree._pointcloudTransformVersion){
  96. Potree._pointcloudTransformVersion = new Map();
  97. }
  98. let pointcloudTransformVersion = Potree._pointcloudTransformVersion;
  99. for(let pointcloud of pointclouds){
  100. if(!viewer.getObjVisiByReason(pointcloud, 'datasetSelection')){//改 visible ->
  101. continue;
  102. }
  103. pointcloud.updateMatrixWorld();
  104. if(!pointcloudTransformVersion.has(pointcloud)){
  105. pointcloudTransformVersion.set(pointcloud, {number: 0, transform: pointcloud.matrixWorld.clone()});
  106. }else{
  107. let version = pointcloudTransformVersion.get(pointcloud);
  108. if(!version.transform.equals(pointcloud.matrixWorld)){
  109. version.number++;
  110. version.transform.copy(pointcloud.matrixWorld);
  111. pointcloud.dispatchEvent({
  112. type: "transformation_changed",
  113. target: pointcloud
  114. });
  115. }
  116. }
  117. }
  118. while (priorityQueue.size() > 0) {
  119. let element = priorityQueue.pop();
  120. let node = element.node;
  121. let parent = element.parent;
  122. let pointcloud = pointclouds[element.pointcloud];
  123. // { // restrict to certain nodes for debugging
  124. // let allowedNodes = ["r", "r0", "r4"];
  125. // if(!allowedNodes.includes(node.name)){
  126. // continue;
  127. // }
  128. // }
  129. let box = node.getBoundingBox();
  130. let frustum = frustums[element.pointcloud];
  131. let camObjPos = camObjPositions[element.pointcloud];
  132. let insideFrustum = frustum.intersectsBox(box);
  133. let maxLevel = pointcloud.maxLevel == void 0 ? Infinity : pointcloud.maxLevel;
  134. let level = node.getLevel();
  135. let visible = insideFrustum;
  136. visible = visible && !(numVisiblePoints + node.getNumPoints() > Potree.pointBudget);
  137. visible = visible && !(numVisiblePointsInPointclouds.get(pointcloud) + node.getNumPoints() > pointcloud.pointBudget);
  138. visible = visible && level <= maxLevel; //< 改为 <=
  139. //visible = visible || node.getLevel() <= 2;
  140. let clipBoxes = pointcloud.material.clipBoxes;
  141. if(true && clipBoxes.length > 0){
  142. //node.debug = false;
  143. let numIntersecting = 0;
  144. let numIntersectionVolumes = 0;
  145. //if(node.name === "r60"){
  146. // var a = 10;
  147. //}
  148. for(let clipBox of clipBoxes){
  149. let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
  150. let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld);
  151. let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse);
  152. let nx = new THREE.Vector3(-0.5, 0, 0).applyMatrix4(pcWorldInverse);
  153. let py = new THREE.Vector3(0, +0.5, 0).applyMatrix4(pcWorldInverse);
  154. let ny = new THREE.Vector3(0, -0.5, 0).applyMatrix4(pcWorldInverse);
  155. let pz = new THREE.Vector3(0, 0, +0.5).applyMatrix4(pcWorldInverse);
  156. let nz = new THREE.Vector3(0, 0, -0.5).applyMatrix4(pcWorldInverse);
  157. let pxN = new THREE.Vector3().subVectors(nx, px).normalize();
  158. let nxN = pxN.clone().multiplyScalar(-1);
  159. let pyN = new THREE.Vector3().subVectors(ny, py).normalize();
  160. let nyN = pyN.clone().multiplyScalar(-1);
  161. let pzN = new THREE.Vector3().subVectors(nz, pz).normalize();
  162. let nzN = pzN.clone().multiplyScalar(-1);
  163. let pxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pxN, px);
  164. let nxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nxN, nx);
  165. let pyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pyN, py);
  166. let nyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nyN, ny);
  167. let pzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pzN, pz);
  168. let nzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nzN, nz);
  169. //if(window.debugdraw !== undefined && window.debugdraw === true && node.name === "r60"){
  170. // Potree.utils.debugPlane(viewer.scene.scene, pxPlane, 1, 0xFF0000);
  171. // Potree.utils.debugPlane(viewer.scene.scene, nxPlane, 1, 0x990000);
  172. // Potree.utils.debugPlane(viewer.scene.scene, pyPlane, 1, 0x00FF00);
  173. // Potree.utils.debugPlane(viewer.scene.scene, nyPlane, 1, 0x009900);
  174. // Potree.utils.debugPlane(viewer.scene.scene, pzPlane, 1, 0x0000FF);
  175. // Potree.utils.debugPlane(viewer.scene.scene, nzPlane, 1, 0x000099);
  176. // Potree.utils.debugBox(viewer.scene.scene, box, new THREE.Matrix4(), 0x00FF00);
  177. // Potree.utils.debugBox(viewer.scene.scene, box, pointcloud.matrixWorld, 0xFF0000);
  178. // Potree.utils.debugBox(viewer.scene.scene, clipBox.box.boundingBox, clipBox.box.matrixWorld, 0xFF0000);
  179. // window.debugdraw = false;
  180. //}
  181. let frustum = new THREE.Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane);
  182. let intersects = frustum.intersectsBox(box);
  183. if(intersects){
  184. numIntersecting++;
  185. }
  186. numIntersectionVolumes++;
  187. }
  188. let insideAny = numIntersecting > 0;
  189. let insideAll = numIntersecting === numIntersectionVolumes;
  190. if(pointcloud.material.clipTask === ClipTask.SHOW_INSIDE){
  191. if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ANY && insideAny){
  192. //node.debug = true
  193. }else if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ALL && insideAll){
  194. //node.debug = true;
  195. }else{
  196. visible = false;
  197. }
  198. } else if(pointcloud.material.clipTask === ClipTask.SHOW_OUTSIDE){
  199. //if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ANY && !insideAny){
  200. // //visible = true;
  201. // let a = 10;
  202. //}else if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ALL && !insideAll){
  203. // //visible = true;
  204. // let a = 20;
  205. //}else{
  206. // visible = false;
  207. //}
  208. }
  209. }
  210. // visible = ["r", "r0", "r06", "r060"].includes(node.name);
  211. // visible = ["r"].includes(node.name);
  212. if (node.spacing) {
  213. lowestSpacing = Math.min(lowestSpacing, node.spacing);
  214. } else if (node.geometryNode && node.geometryNode.spacing) {
  215. lowestSpacing = Math.min(lowestSpacing, node.geometryNode.spacing);
  216. }
  217. if (numVisiblePoints + node.getNumPoints() > Potree.pointBudget) {
  218. break;
  219. }
  220. if (!visible) {
  221. continue;
  222. }
  223. // TODO: not used, same as the declaration?
  224. // numVisibleNodes++;
  225. numVisiblePoints += node.getNumPoints();
  226. let numVisiblePointsInPointcloud = numVisiblePointsInPointclouds.get(pointcloud);
  227. numVisiblePointsInPointclouds.set(pointcloud, numVisiblePointsInPointcloud + node.getNumPoints());
  228. pointcloud.numVisibleNodes++;
  229. pointcloud.numVisiblePoints += node.getNumPoints();
  230. if (node.isGeometryNode() && (!parent || parent.isTreeNode())) {
  231. if (node.isLoaded() && loadedToGPUThisFrame < 2) {
  232. node = pointcloud.toTreeNode(node, parent);
  233. loadedToGPUThisFrame++;
  234. } else {
  235. unloadedGeometry.push(node);
  236. visibleGeometry.push(node);
  237. }
  238. }
  239. if (node.isTreeNode()) {
  240. exports.lru.touch(node.geometryNode);
  241. node.sceneNode.visible = true;
  242. node.sceneNode.material = pointcloud.material;
  243. visibleNodes.push(node);
  244. pointcloud.visibleNodes.push(node);
  245. if(node._transformVersion === undefined){
  246. node._transformVersion = -1;
  247. }
  248. let transformVersion = pointcloudTransformVersion.get(pointcloud);
  249. if(node._transformVersion !== transformVersion.number){
  250. node.sceneNode.updateMatrix();
  251. node.sceneNode.matrixWorld.multiplyMatrices(pointcloud.matrixWorld, node.sceneNode.matrix);
  252. node._transformVersion = transformVersion.number;
  253. }
  254. if (pointcloud.showBoundingBox && !node.boundingBoxNode && node.getBoundingBox) {
  255. let boxHelper = new Box3Helper(node.getBoundingBox());
  256. boxHelper.matrixAutoUpdate = false;
  257. pointcloud.boundingBoxNodes.push(boxHelper);
  258. node.boundingBoxNode = boxHelper;
  259. node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld);
  260. } else if (pointcloud.showBoundingBox) {
  261. node.boundingBoxNode.visible = true;
  262. node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld);
  263. } else if (!pointcloud.showBoundingBox && node.boundingBoxNode) {
  264. node.boundingBoxNode.visible = false;
  265. }
  266. // if(node.boundingBoxNode !== undefined && exports.debug.allowedNodes !== undefined){
  267. // if(!exports.debug.allowedNodes.includes(node.name)){
  268. // node.boundingBoxNode.visible = false;
  269. // }
  270. // }
  271. }
  272. // add child nodes to priorityQueue
  273. let children = node.getChildren();
  274. for (let i = 0; i < children.length; i++) {
  275. let child = children[i];
  276. let weight = 0;
  277. if(camera.isPerspectiveCamera){
  278. let sphere = child.getBoundingSphere();
  279. let center = sphere.center;
  280. //let distance = sphere.center.distanceTo(camObjPos);
  281. let dx = camObjPos.x - center.x;
  282. let dy = camObjPos.y - center.y;
  283. let dz = camObjPos.z - center.z;
  284. let dd = dx * dx + dy * dy + dz * dz;
  285. let distance = Math.sqrt(dd);
  286. let radius = sphere.radius;
  287. let fov = (camera.fov * Math.PI) / 180;
  288. let slope = Math.tan(fov / 2);
  289. let projFactor = (0.5 * domHeight) / (slope * distance);
  290. let screenPixelRadius = radius * projFactor;
  291. if(screenPixelRadius < pointcloud.minimumNodePixelSize){
  292. continue;
  293. }
  294. weight = screenPixelRadius;
  295. if(distance - radius < 0){
  296. weight = Number.MAX_VALUE;
  297. }
  298. } else {
  299. // TODO ortho visibility
  300. let bb = child.getBoundingBox();
  301. let distance = child.getBoundingSphere().center.distanceTo(camObjPos);
  302. let diagonal = bb.max.clone().sub(bb.min).length();
  303. //weight = diagonal / distance;
  304. weight = diagonal;
  305. }
  306. priorityQueue.push({pointcloud: element.pointcloud, node: child, parent: node, weight: weight});
  307. }
  308. }// end priority queue loop
  309. { // update DEM
  310. let maxDEMLevel = 4;
  311. let candidates = pointclouds.filter(p => (p.generateDEM && p.dem instanceof Potree.DEM));
  312. for (let pointcloud of candidates) {
  313. let updatingNodes = pointcloud.visibleNodes.filter(n => n.getLevel() <= maxDEMLevel);
  314. pointcloud.dem.update(updatingNodes);
  315. }
  316. }
  317. for (let i = 0; i < Math.min(Potree.maxNodesLoading, unloadedGeometry.length); i++) {
  318. unloadedGeometry[i].load();
  319. }
  320. return {
  321. visibleNodes: visibleNodes,
  322. numVisiblePoints: numVisiblePoints,
  323. lowestSpacing: lowestSpacing
  324. };
  325. };
  326. //console
  327. //viewer.scene.pointclouds[0].visibleNodes.map(e=> e && e.name )
  328. //viewer.scene.pointclouds[0].visibleNodes.map(e=>e.children.map(e=>e && e.name))