import * as THREE from "../libs/three.js/build/three.module.js"; import {ClipTask, ClipMethod} from "./defines.js"; import {Box3Helper} from "./utils/Box3Helper.js"; export function updatePointClouds(pointclouds,camera, areaSize /* renderer */){ for (let pointcloud of pointclouds) { let start = performance.now(); for (let profileRequest of pointcloud.profileRequests) { profileRequest.update(); let duration = performance.now() - start; if(duration > 5){ break; } } let duration = performance.now() - start; } let result = updateVisibility(pointclouds, camera, areaSize ); for (let pointcloud of pointclouds) { //pointcloud.updateMaterial(pointcloud.material, pointcloud.visibleNodes, camera, renderer);//转移到渲染时 pointcloud.updateVisibleBounds(); } exports.lru.freeMemory();//即Potree.lru 能看到所有在加载的node return result; }; export function updateVisibilityStructures(pointclouds, camera, areaSize) { let frustums = []; let camObjPositions = []; let priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; }); for (let i = 0; i < pointclouds.length; i++) { let pointcloud = pointclouds[i]; if (!pointcloud.initialized()) { continue; } pointcloud.numVisibleNodes = 0; pointcloud.numVisiblePoints = 0; pointcloud.deepestVisibleLevel = 0; pointcloud.visibleNodes = []; pointcloud.visibleGeometry = []; // frustum in object space camera.updateMatrixWorld(); let frustum = new THREE.Frustum(); let viewI = camera.matrixWorldInverse; let world = pointcloud.matrixWorld; // use close near plane for frustum intersection let frustumCam = camera.clone(); frustumCam.near = Math.min(camera.near, 0.1); frustumCam.updateProjectionMatrix(); let proj = camera.projectionMatrix; let fm = new THREE.Matrix4().multiply(proj).multiply(viewI).multiply(world); frustum.setFromProjectionMatrix(fm); frustums.push(frustum); // camera position in object space let view = camera.matrixWorld; let worldI = world.clone().invert(); let camMatrixObject = new THREE.Matrix4().multiply(worldI).multiply(view); let camObjPos = new THREE.Vector3().setFromMatrixPosition(camMatrixObject); camObjPositions.push(camObjPos); if ( viewer.getObjVisiByReason(pointcloud, 'datasetSelection') && pointcloud.root !== null) {//改 visible -> priorityQueue.push({pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE}); } // hide all previously visible nodes // if(pointcloud.root instanceof PointCloudOctreeNode){ // pointcloud.hideDescendants(pointcloud.root.sceneNode); // } if (pointcloud.root.isTreeNode()) { pointcloud.hideDescendants(pointcloud.root.sceneNode); } for (let j = 0; j < pointcloud.boundingBoxNodes.length; j++) { pointcloud.boundingBoxNodes[j].visible = false; } } return { 'frustums': frustums, 'camObjPositions': camObjPositions, 'priorityQueue': priorityQueue }; }; export function updateVisibility(pointclouds, camera, areaSize){ let numVisibleNodes = 0; let numVisiblePoints = 0; let numVisiblePointsInPointclouds = new Map(pointclouds.map(pc => [pc, 0])); let visibleNodes = []; let visibleGeometry = []; let unloadedGeometry = []; let lowestSpacing = Infinity; // calculate object space frustum and cam pos and setup priority queue let s = updateVisibilityStructures(pointclouds, camera, areaSize);//得到相机可见范围 let frustums = s.frustums; let camObjPositions = s.camObjPositions; let priorityQueue = s.priorityQueue; let loadedToGPUThisFrame = 0; let domWidth = areaSize.x; //renderer.domElement.clientWidth; let domHeight = areaSize.y;//renderer.domElement.clientHeight; // check if pointcloud has been transformed // some code will only be executed if changes have been detected if(!Potree._pointcloudTransformVersion){ Potree._pointcloudTransformVersion = new Map(); } let pointcloudTransformVersion = Potree._pointcloudTransformVersion; for(let pointcloud of pointclouds){ if(!viewer.getObjVisiByReason(pointcloud, 'datasetSelection')){//改 visible -> continue; } pointcloud.updateMatrixWorld(); if(!pointcloudTransformVersion.has(pointcloud)){ pointcloudTransformVersion.set(pointcloud, {number: 0, transform: pointcloud.matrixWorld.clone()}); }else{ let version = pointcloudTransformVersion.get(pointcloud); if(!version.transform.equals(pointcloud.matrixWorld)){ version.number++; version.transform.copy(pointcloud.matrixWorld); pointcloud.dispatchEvent({ type: "transformation_changed", target: pointcloud }); } } } while (priorityQueue.size() > 0) { let element = priorityQueue.pop();//其实是拿第一个, 再把最后一个放到前面 let node = element.node; let parent = element.parent; let pointcloud = pointclouds[element.pointcloud]; // { // restrict to certain nodes for debugging // let allowedNodes = ["r", "r0", "r4"]; // if(!allowedNodes.includes(node.name)){ // continue; // } // } let box = node.getBoundingBox(); let frustum = frustums[element.pointcloud]; let camObjPos = camObjPositions[element.pointcloud]; let insideFrustum = frustum.intersectsBox(box); let maxLevel = pointcloud.maxLevel == void 0 ? Infinity : pointcloud.maxLevel; let level = node.getLevel(); let visible = insideFrustum; visible = visible && !(numVisiblePoints + node.getNumPoints() > Potree.pointBudget); visible = visible && !(numVisiblePointsInPointclouds.get(pointcloud) + node.getNumPoints() > pointcloud.pointBudget); visible = visible && level <= maxLevel; //< 改为 <= //visible = visible || node.getLevel() <= 2; let clipBoxes = pointcloud.material.clipBoxes; if(true && clipBoxes.length > 0){ //node.debug = false; let numIntersecting = 0; let numIntersectionVolumes = 0; //if(node.name === "r60"){ // var a = 10; //} for(let clipBox of clipBoxes){ let pcWorldInverse = pointcloud.matrixWorld.clone().invert(); let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld); let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse); let nx = new THREE.Vector3(-0.5, 0, 0).applyMatrix4(pcWorldInverse); let py = new THREE.Vector3(0, +0.5, 0).applyMatrix4(pcWorldInverse); let ny = new THREE.Vector3(0, -0.5, 0).applyMatrix4(pcWorldInverse); let pz = new THREE.Vector3(0, 0, +0.5).applyMatrix4(pcWorldInverse); let nz = new THREE.Vector3(0, 0, -0.5).applyMatrix4(pcWorldInverse); let pxN = new THREE.Vector3().subVectors(nx, px).normalize(); let nxN = pxN.clone().multiplyScalar(-1); let pyN = new THREE.Vector3().subVectors(ny, py).normalize(); let nyN = pyN.clone().multiplyScalar(-1); let pzN = new THREE.Vector3().subVectors(nz, pz).normalize(); let nzN = pzN.clone().multiplyScalar(-1); let pxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pxN, px); let nxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nxN, nx); let pyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pyN, py); let nyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nyN, ny); let pzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pzN, pz); let nzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nzN, nz); //if(window.debugdraw !== undefined && window.debugdraw === true && node.name === "r60"){ // Potree.utils.debugPlane(viewer.scene.scene, pxPlane, 1, 0xFF0000); // Potree.utils.debugPlane(viewer.scene.scene, nxPlane, 1, 0x990000); // Potree.utils.debugPlane(viewer.scene.scene, pyPlane, 1, 0x00FF00); // Potree.utils.debugPlane(viewer.scene.scene, nyPlane, 1, 0x009900); // Potree.utils.debugPlane(viewer.scene.scene, pzPlane, 1, 0x0000FF); // Potree.utils.debugPlane(viewer.scene.scene, nzPlane, 1, 0x000099); // Potree.utils.debugBox(viewer.scene.scene, box, new THREE.Matrix4(), 0x00FF00); // Potree.utils.debugBox(viewer.scene.scene, box, pointcloud.matrixWorld, 0xFF0000); // Potree.utils.debugBox(viewer.scene.scene, clipBox.box.boundingBox, clipBox.box.matrixWorld, 0xFF0000); // window.debugdraw = false; //} let frustum = new THREE.Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane); let intersects = frustum.intersectsBox(box); if(intersects){ numIntersecting++; } numIntersectionVolumes++; } let insideAny = numIntersecting > 0; let insideAll = numIntersecting === numIntersectionVolumes; if(pointcloud.material.clipTask === ClipTask.SHOW_INSIDE){ if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ANY && insideAny){ //node.debug = true }else if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ALL && insideAll){ //node.debug = true; }else{ visible = false; } } else if(pointcloud.material.clipTask === ClipTask.SHOW_OUTSIDE){ //if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ANY && !insideAny){ // //visible = true; // let a = 10; //}else if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ALL && !insideAll){ // //visible = true; // let a = 20; //}else{ // visible = false; //} } } // visible = ["r", "r0", "r06", "r060"].includes(node.name); // visible = ["r"].includes(node.name); if (node.spacing) { lowestSpacing = Math.min(lowestSpacing, node.spacing); } else if (node.geometryNode && node.geometryNode.spacing) { lowestSpacing = Math.min(lowestSpacing, node.geometryNode.spacing); } if (numVisiblePoints + node.getNumPoints() > Potree.pointBudget) { break; } if (!visible) { continue; } // TODO: not used, same as the declaration? // numVisibleNodes++; numVisiblePoints += node.getNumPoints(); let numVisiblePointsInPointcloud = numVisiblePointsInPointclouds.get(pointcloud); numVisiblePointsInPointclouds.set(pointcloud, numVisiblePointsInPointcloud + node.getNumPoints()); pointcloud.numVisibleNodes++; pointcloud.numVisiblePoints += node.getNumPoints(); if (node.isGeometryNode() && (!parent || parent.isTreeNode())) { if (node.isLoaded() && loadedToGPUThisFrame < 2) { node = pointcloud.toTreeNode(node, parent); loadedToGPUThisFrame++; } else { unloadedGeometry.push({pointcloud,node}); visibleGeometry.push(node); } } if (node.isTreeNode()) { exports.lru.touch(node.geometryNode); node.sceneNode.visible = true; node.sceneNode.material = pointcloud.material; visibleNodes.push(node); pointcloud.visibleNodes.push(node); if(node._transformVersion === undefined){ node._transformVersion = -1; } let transformVersion = pointcloudTransformVersion.get(pointcloud); if(node._transformVersion !== transformVersion.number){ node.sceneNode.updateMatrix(); node.sceneNode.matrixWorld.multiplyMatrices(pointcloud.matrixWorld, node.sceneNode.matrix); node._transformVersion = transformVersion.number; } if (pointcloud.showBoundingBox && !node.boundingBoxNode && node.getBoundingBox) { let boxHelper = new Box3Helper(node.getBoundingBox()); boxHelper.matrixAutoUpdate = false; pointcloud.boundingBoxNodes.push(boxHelper); node.boundingBoxNode = boxHelper; node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld); } else if (pointcloud.showBoundingBox) { node.boundingBoxNode.visible = true; node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld); } else if (!pointcloud.showBoundingBox && node.boundingBoxNode) { node.boundingBoxNode.visible = false; } // if(node.boundingBoxNode !== undefined && exports.debug.allowedNodes !== undefined){ // if(!exports.debug.allowedNodes.includes(node.name)){ // node.boundingBoxNode.visible = false; // } // } } // add child nodes to priorityQueue let children = node.getChildren(); for (let i = 0; i < children.length; i++) { let child = children[i]; let weight = 0; if(camera.isPerspectiveCamera){ let sphere = child.getBoundingSphere(); let center = sphere.center; //let distance = sphere.center.distanceTo(camObjPos); let dx = camObjPos.x - center.x; let dy = camObjPos.y - center.y; let dz = camObjPos.z - center.z; let dd = dx * dx + dy * dy + dz * dz; let distance = Math.sqrt(dd); let radius = sphere.radius; let fov = (camera.fov * Math.PI) / 180; let slope = Math.tan(fov / 2); let projFactor = (0.5 * domHeight) / (slope * distance); let screenPixelRadius = radius * projFactor; if(screenPixelRadius < pointcloud.minimumNodePixelSize){ continue; } weight = screenPixelRadius; if(distance - radius < 0){ weight = Number.MAX_VALUE; } } else { // TODO ortho visibility let bb = child.getBoundingBox(); let distance = child.getBoundingSphere().center.distanceTo(camObjPos); let diagonal = bb.max.clone().sub(bb.min).length(); //weight = diagonal / distance; weight = diagonal; } priorityQueue.push({pointcloud: element.pointcloud, node: child, parent: node, weight: weight}); } }// end priority queue loop { // update DEM 这是什么 let maxDEMLevel = 4; let candidates = pointclouds.filter(p => (p.generateDEM && p.dem instanceof Potree.DEM)); for (let pointcloud of candidates) { let updatingNodes = pointcloud.visibleNodes.filter(n => n.getLevel() <= maxDEMLevel); pointcloud.dem.update(updatingNodes); } } //加载点云 for (let i = 0; i < Math.min(Potree.maxNodesLoading, unloadedGeometry.length); i++) { unloadedGeometry[i].node.load(unloadedGeometry[i].pointcloud.pcoGeometry); } return { visibleNodes: visibleNodes, numVisiblePoints: numVisiblePoints, lowestSpacing: lowestSpacing }; }; //console //viewer.scene.pointclouds[0].visibleNodes.map(e=> e && e.name ) //viewer.scene.pointclouds[0].visibleNodes.map(e=>e.children.map(e=>e && e.name))