|
@@ -626,7 +626,7 @@ Utils.datasetRotTransform = function(o={}){
|
|
|
|
|
|
}
|
|
|
|
|
|
-Utils.isInsideFrustum = function(bounding, camera){// boundingBox在视野范围内有可见部分
|
|
|
+Utils.isInsideFrustum = function(bounding, camera){// bounding是否在视野范围内有可见部分(视野就是一个锥状box)
|
|
|
let frustumMatrix = new THREE.Matrix4
|
|
|
frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
|
|
|
|
|
@@ -640,22 +640,52 @@ Utils.isInsideFrustum = function(bounding, camera){// boundingBox在视野范围
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-Utils.isInsideBox = function(object, boxMatrixInverse){//object可以是点或者bounding, box原为1*1*1,但可能形变
|
|
|
- let frustum = new THREE.Frustum();
|
|
|
- frustum.setFromProjectionMatrix(boxMatrixInverse)
|
|
|
+Utils.isIntersectBox = function(object, boxMatrix){//object是否有在box中的部分。 object可以是点或者bounding, box原为1*1*1,但可能形变
|
|
|
+ //let frustum = new THREE.Frustum();
|
|
|
+ //frustum.setFromProjectionMatrix(boxMatrixInverse) --错
|
|
|
+
|
|
|
+ let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(boxMatrix);
|
|
|
+ let nx = new THREE.Vector3(-0.5, 0, 0).applyMatrix4(boxMatrix);
|
|
|
+ let py = new THREE.Vector3(0, +0.5, 0).applyMatrix4(boxMatrix);
|
|
|
+ let ny = new THREE.Vector3(0, -0.5, 0).applyMatrix4(boxMatrix);
|
|
|
+ let pz = new THREE.Vector3(0, 0, +0.5).applyMatrix4(boxMatrix);
|
|
|
+ let nz = new THREE.Vector3(0, 0, -0.5).applyMatrix4(boxMatrix);
|
|
|
+
|
|
|
+ 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);
|
|
|
+ let frustum = new THREE.Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
if(object instanceof THREE.Box3){
|
|
|
- return frustum.intersectsBox(object)
|
|
|
- }else if(object instanceof Array){//点合集,先求Sphere setFromPoints
|
|
|
+ var boxBound = new THREE.Box3(
|
|
|
+ new THREE.Vector3(-0.5,-0.5,-0.5), new THREE.Vector3(0.5,0.5,0.5),
|
|
|
+ ).applyMatrix4(boxMatrix) //large boundingbox
|
|
|
+ if(!object.intersectsBox(boxBound))return
|
|
|
+ return frustum.intersectsBox(object) //根据该函数, 若存在某个plane在box上的对应点都在plane背面,则不相交. 可得知在box构成的frustum倾斜时不准确,不相交也判断为相交,甚至不如bound相交准确。所以前面加步骤排除下,但仍不完全准确。(可在裁剪中将box放置到数据集上方旋转下校验)
|
|
|
+ }else if(object instanceof Array){//点合集, 只能粗略计算下
|
|
|
let sphere = new THREE.Sphere()
|
|
|
sphere.setFromPoints(object)
|
|
|
- return this.isInsideBox(sphere, boxMatrixInverse)
|
|
|
-
|
|
|
+ return this.isIntersectBox(sphere, boxMatrix)
|
|
|
}else if(object instanceof THREE.Sphere){
|
|
|
return frustum.intersectsSphere(object)
|
|
|
}else if(object instanceof THREE.Vector3){
|
|
|
return frustum.containsPoint(object)
|
|
|
- }
|
|
|
+ }else if(object instanceof THREE.Matrix4){//第一个参数如果和第二个参数一样都是box的worldMatrix
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
/* containsPoint: ƒ containsPoint( point )
|
|
|
intersectsBox: ƒ intersectsBox( box )
|
|
@@ -895,7 +925,11 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
|
|
|
let domWidth = areaSize.x; //renderer.domElement.clientWidth;
|
|
|
let domHeight = areaSize.y;//renderer.domElement.clientHeight;
|
|
|
-
|
|
|
+ let fov = (camera.fov * Math.PI) / 180;
|
|
|
+ let slope = Math.tan(fov / 2);
|
|
|
+ let projFactor0 = (0.5 * domHeight) / slope ;
|
|
|
+
|
|
|
+
|
|
|
// check if pointcloud has been transformed
|
|
|
// some code will only be executed if changes have been detected
|
|
|
if(!Potree._pointcloudTransformVersion){
|
|
@@ -957,12 +991,12 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
//visible = visible || node.getLevel() <= 2;
|
|
|
|
|
|
|
|
|
- let insideBox = (clipBox)=>{
|
|
|
+ let intersectBox = (clipBox)=>{
|
|
|
|
|
|
let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
|
|
|
- let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld);
|
|
|
+ let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld); //box乘上点云逆矩阵
|
|
|
|
|
|
- let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse);
|
|
|
+ /* 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);
|
|
@@ -1003,7 +1037,12 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
let intersects = frustum.intersectsBox(box); //node的bounding
|
|
|
|
|
|
|
|
|
- return !!intersects
|
|
|
+ return !!intersects */
|
|
|
+
|
|
|
+
|
|
|
+ return Potree.Utils.isIntersectBox(box, pcWorldInverse)
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
|
|
|
//改 总共两种box : 可见和不可见(都是并集)
|
|
@@ -1015,7 +1054,7 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
let bigClipInBox = pointcloud.material.bigClipInBox
|
|
|
|
|
|
if(visible && bigClipInBox){//不在剪裁下载的框内
|
|
|
- if(!insideBox(bigClipInBox)){
|
|
|
+ if(!intersectBox(bigClipInBox)){
|
|
|
visible = false;
|
|
|
}
|
|
|
}
|
|
@@ -1024,7 +1063,7 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
if(visible && clipBoxes_in.length > 0){//当有可见box时,需要在任一可见box内才可见
|
|
|
let visi = false;
|
|
|
for(let i = 0, length=clipBoxes_in.length; i < length; i++){
|
|
|
- if(insideBox(clipBoxes_in[i])){
|
|
|
+ if(intersectBox(clipBoxes_in[i])){
|
|
|
visi = true;
|
|
|
break;
|
|
|
}
|
|
@@ -1035,11 +1074,11 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
}
|
|
|
|
|
|
|
|
|
- //outside不做处理。因为node必须完全在clipBox内才能完全隐藏,而这里的intersect只能识别出部分在clipBox内
|
|
|
+ //outside不做处理。因为node必须完全在clipBox内才能完全隐藏,而这里的intersect只能识别出部分在clipBox内。因而只能说明不在任意一个box内绝对可见,没有意义,这里需要找出不可见的。
|
|
|
/* if(visible && clipBoxes_out.length > 0){ //当有不可见box时,不在所有不可见box内才可见
|
|
|
let visi = true;
|
|
|
for(let i = 0,length=clipBoxes_out.length; i < length; i++){
|
|
|
- if(insideBox(clipBoxes_out[i])){
|
|
|
+ if(intersectBox(clipBoxes_out[i])){
|
|
|
visi = false;
|
|
|
break;
|
|
|
}
|
|
@@ -1127,7 +1166,7 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
// }
|
|
|
}
|
|
|
|
|
|
- // add child nodes to priorityQueue
|
|
|
+ // add child nodes to priorityQueue 由近及远、由大及小逐渐加载
|
|
|
let children = node.getChildren();
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
|
let child = children[i];
|
|
@@ -1136,28 +1175,27 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
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 dd = sphere.center.distanceToSquared(camObjPos);
|
|
|
+
|
|
|
+ let addPow = 0.25 //0-0.5 数字越大近处加载越快。但会造成远处加载慢甚至因pointBudge限制不加载
|
|
|
+ let distance = Math.pow(dd,0.5+addPow)//Math.sqrt(dd); //提高距离权重,为了提高近处加载速度。 某些场景近处加载慢优化明显,如SS-t-cqCAL6rJ5i
|
|
|
|
|
|
+ //let attenuateDis = 10;//add
|
|
|
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 projFactor = projFactor0 / distance
|
|
|
let screenPixelRadius = radius * projFactor;
|
|
|
+
|
|
|
+ /* if(distance > attenuateDis){
|
|
|
+ screenPixelRadius -= (distance - attenuateDis) * Math.sqrt(radius) * projFactor0 * 0.002
|
|
|
+ } */
|
|
|
+
|
|
|
//screenPixelRadius 和 domHeight 成正比,所以手机横屏后screenPixelRadius会变小。这是正常的,因为vhov不变,相同物体高度在横屏后高度变小,所需要的密度不需要那么高了。但hfov横屏后扩大,所以可见的node范围变大,又增加了一些可见node;只是总体的可见node还是减少了。
|
|
|
//使用hfov和domWidth计算结果相同。
|
|
|
- if(screenPixelRadius < pointcloud.minimumNodePixelSize){
|
|
|
+ if(screenPixelRadius < pointcloud.minimumNodePixelSize / Math.pow(dd,addPow)){
|
|
|
continue;
|
|
|
}
|
|
|
-
|
|
|
+ //如果能得到该方向上的密度,也就是node数量,密度大的远处少加载,因为被遮挡了显示也没有意义,就好了。
|
|
|
weight = screenPixelRadius;
|
|
|
|
|
|
if(distance - radius < 0){
|
|
@@ -1189,7 +1227,11 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
for (let i = 0; i < Math.min(Potree.maxNodesLoading, unloadedGeometry.length); i++) {
|
|
|
unloadedGeometry[i].node.load(unloadedGeometry[i].pointcloud.pcoGeometry);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ //add:
|
|
|
+ Potree.numVisiblePoints = numVisiblePoints
|
|
|
+
|
|
|
+
|
|
|
return {
|
|
|
visibleNodes: visibleNodes,
|
|
|
numVisiblePoints: numVisiblePoints,
|
|
@@ -1198,10 +1240,20 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
|
|
|
};
|
|
|
/*
|
|
|
note:
|
|
|
- 缓存中的点数 Potree.lru.numPoints 会 大于 每个点云显示的numVisiblePoints之和。
|
|
|
+ 缓存中的点数 Potree.lru.numPoints 一般会 大于 每个点云显示点总数的numVisiblePoints
|
|
|
当超出缓冲区最大点云数时,加载的点云节点会被dispose彻底消除;否则,隐藏的节点就会等待再次被使用显示
|
|
|
- 要降低卡顿,就只需要降低Potree.pointBudget即可。为了level显示均匀,还能另外设置整体maxLevel
|
|
|
+ 由于加载按照由近及远、由大及小的顺序,要降低卡顿,就只需要降低Potree.pointBudget即可。但目前只设置了三个层次;另外提供maxLevel细节调节,能显示更均匀. 最好多一个调节pointBudge的滑动条
|
|
|
+
|
|
|
+ Potree.lru.numPoints
|
|
|
+ Potree.numVisiblePoints
|
|
|
+ viewer.scene.pointclouds[0].visibleNodes.length
|
|
|
*/
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
{//HQSplatRenderer
|
|
|
let oldInit = HQSplatRenderer.prototype.init;
|
|
|
HQSplatRenderer.prototype.init = function(){
|
|
@@ -1605,11 +1657,11 @@ LRU.prototype.freeMemory = function(){
|
|
|
if (this.elements <= 1) {
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+ let memoryRatio = browser.isMobile ? 2 : 3;
|
|
|
//改成navvis的,使用pointBudget,否则四屏点云闪烁。 (似乎要比updateVisiblede的node时限制要宽些,作为缓存继续存着。否则会闪烁)
|
|
|
- let max = viewer.viewports.length * 2 * Potree.pointBudget// : 1000 // this.pageVisible ?
|
|
|
+ let max = viewer.viewports.length * memoryRatio * Potree.pointBudget
|
|
|
|
|
|
- for (; this.numPoints > max; ) {//应该是从子node删除
|
|
|
+ for (; this.numPoints > max; ) {
|
|
|
var node = this.getLRUItem();
|
|
|
node && this.disposeDescendants(node); //this.disposeSubtree(node)
|
|
|
}
|
|
@@ -1636,7 +1688,7 @@ VolumeTool.prototype.startInsertion = function(args = {}){
|
|
|
if(args.type){
|
|
|
volume = new args.type();
|
|
|
}else{
|
|
|
- volume = new Potree.BoxVolume({clip:args.clip, clipTask:ClipTask.SHOW_OUTSIDE});
|
|
|
+ volume = new Potree.BoxVolume(args/* {clip:args.clip, clipTask:ClipTask.SHOW_OUTSIDE} */);
|
|
|
}
|
|
|
|
|
|
volume.name = args.name || 'Volume';
|
|
@@ -1662,13 +1714,13 @@ VolumeTool.prototype.startInsertion = function(args = {}){
|
|
|
volume: volume
|
|
|
});
|
|
|
|
|
|
- if(viewer.scene.volumes.length>0){
|
|
|
+ /* if(viewer.scene.volumes.length>0){
|
|
|
volume.rotation.copy(viewer.scene.volumes[viewer.scene.volumes.length-1].rotation); //使用上一个的旋转值
|
|
|
- }
|
|
|
+ } */
|
|
|
updateScale()
|
|
|
this.viewer.scene.addVolume(volume);
|
|
|
this.scene.add(volume);
|
|
|
- viewer.transformObject(volume)
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -1701,6 +1753,7 @@ VolumeTool.prototype.startInsertion = function(args = {}){
|
|
|
volume.removeEventListener('drag', drag);
|
|
|
volume.removeEventListener('drop', end);
|
|
|
this.viewer.removeEventListener('cancel_insertions', cancel);
|
|
|
+ viewer.transformObject(volume)
|
|
|
};
|
|
|
|
|
|
volume.addEventListener('drag', drag);
|