Skip to content
Open
2 changes: 1 addition & 1 deletion examples/vpc_3d_loader.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
function onLayerReady() {
var lookAt = vpcLayer.root.origin;
var size = new THREE.Vector3();
vpcLayer.root.voxelOBB.box3D.getSize(size);
vpcLayer.root.clampOBB.box3D.getSize(size);

view.controls.lookAtCoordinate({
coord: lookAt,
Expand Down
2 changes: 1 addition & 1 deletion examples/vpc_simple_loader.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
var lookAt = vpcLayer.root.origin.toVector3();

var size = new THREE.Vector3();
vpcLayer.root.voxelOBB.box3D.getSize(size);
vpcLayer.root.clampOBB.box3D.getSize(size);
view.camera3D.far = 10.0 * size.length();

controls.groundLevel = vpcLayer.minElevationRange;
Expand Down
7 changes: 1 addition & 6 deletions packages/Main/src/Core/CopcNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@ class CopcNode extends PointCloudNode {
* @param {string} crs - The crs of the node.
*/
constructor(depth, x, y, z, entryOffset, entryLength, source, numPoints = 0, crs) {
super(numPoints, source);
super(depth, numPoints, source);
this.isCopcNode = true;

this.entryOffset = entryOffset;
this.entryLength = entryLength;

this.depth = depth;
this.x = x;
this.y = y;
this.z = z;
Expand All @@ -53,10 +52,6 @@ class CopcNode extends PointCloudNode {
this.crs = crs;
}

get octreeIsLoaded() {
return this.numPoints >= 0;
}

get id() {
return `${this.depth}${this.x}${this.y}${this.z}`;
}
Expand Down
7 changes: 1 addition & 6 deletions packages/Main/src/Core/EntwinePointTileNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ class EntwinePointTileNode extends PointCloudNode {
* @param {string} crs - The crs of the node.
*/
constructor(depth, x, y, z, source, numPoints = 0, crs) {
super(numPoints, source);
super(depth, numPoints, source);
this.isEntwinePointTileNode = true;

this.depth = depth;
this.x = x;
this.y = y;
this.z = z;
Expand All @@ -66,10 +65,6 @@ class EntwinePointTileNode extends PointCloudNode {
this.crs = crs;
}

get octreeIsLoaded() {
return this.numPoints >= 0;
}

get id() {
return `${this.depth}${this.x}${this.y}${this.z}`;
}
Expand Down
151 changes: 43 additions & 108 deletions packages/Main/src/Core/PointCloudNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ const translation = new THREE.Vector3();
* @property {number} sse - The sse of the node set at an nitial value of -1.
*/
class PointCloudNode extends THREE.EventDispatcher {
constructor(numPoints = 0, source) {
constructor(depth, numPoints = 0, source) {
super();

this.depth = depth;
this.numPoints = numPoints;

this.source = source;

this.children = [];
this.voxelOBB = new OBB();
this.clampOBB = new OBB();

this.sse = -1;
}

Expand All @@ -38,11 +38,39 @@ class PointCloudNode extends THREE.EventDispatcher {
throw new Error('In extended PointCloudNode, you have to implement the getter id!');
}

get octreeIsLoaded() {
return this.numPoints >= 0;
}

get voxelOBB() {
if (this._voxelOBB != undefined) { return this._voxelOBB; }
this._voxelOBB = new OBB();
if (this.depth === 0) {
this._voxelOBB.setFromArray(this.source.bounds).projOBB(this.source.crs, this.crs);
} else {
this.setVoxelOBBFromParent();
}
return this._voxelOBB;
}

get clampOBB() {
// Will be set from the boundingConforming metadata for the root node (if available)
// or from the Voxel OBB clamped to the zmin and zmax value.
if (this._clampOBB != undefined) { return this._clampOBB; }
this._clampOBB = new OBB();
if (this.depth === 0 && this.source.boundsConforming) {
this._clampOBB.setFromArray(this.source.boundsConforming).projOBB(this.source.crs, this.crs);
} else {
this._clampOBB.copy(this.voxelOBB).clampZ(this.source.zmin, this.source.zmax);
}
return this._clampOBB;
}

// get the center of the node i.e. the center of the bounding box.
get center() {
if (this._center != undefined) { return this._center; }
const centerBbox = new THREE.Vector3();
this.voxelOBB.box3D.getCenter(centerBbox);
this.clampOBB.box3D.getCenter(centerBbox);
this._center = new Coordinates(this.crs).setFromVector3(centerBbox.applyMatrix4(this.clampOBB.matrixWorld));
return this._center;
}
Expand All @@ -69,133 +97,40 @@ class PointCloudNode extends THREE.EventDispatcher {
return this._rotation;
}

setOBBes(min, max) {
const root = this;
const crs = {
in: root.source.crs,
out: this.crs,
};
const zmin = root.source.zmin;
const zmax = root.source.zmax;

let forward = (x => x);
if (crs.in !== crs.out) {
try {
forward = proj4(crs.in, crs.out).forward;
} catch (err) {
throw new Error(`${err} is not defined in proj4`);
}
}

const corners = [
...forward([max[0], max[1], max[2]]),
...forward([min[0], max[1], max[2]]),
...forward([min[0], min[1], max[2]]),
...forward([max[0], min[1], max[2]]),
...forward([max[0], max[1], min[2]]),
...forward([min[0], max[1], min[2]]),
...forward([min[0], min[1], min[2]]),
...forward([max[0], min[1], min[2]]),
];

// get center of box at altitude Z=0 and project it in view crs;
const origin = forward([(min[0] + max[0]) * 0.5, (min[1] + max[1]) * 0.5, 0]);

// get LocalRotation
const isGeocentric = proj4.defs(crs.out).projName === 'geocent';
let rotation = new THREE.Quaternion();
if (isGeocentric) {
const coordOrigin = new Coordinates(crs.out).setFromArray(origin);
rotation = OrientationUtils.quaternionFromCRSToCRS(crs.out, crs.in)(coordOrigin);
}

// project corners in local referentiel
const cornersLocal = [];
for (let i = 0; i < 24; i += 3) {
const cornerLocal = new THREE.Vector3(
corners[i] - origin[0],
corners[i + 1] - origin[1],
corners[i + 2] - origin[2],
);
cornerLocal.applyQuaternion(rotation);
cornersLocal.push(...cornerLocal.toArray());
}

// get the bbox containing all cornersLocal => the bboxLocal
root.voxelOBB.box3D.setFromArray(cornersLocal);
root.voxelOBB.position.set(...origin);
root.voxelOBB.quaternion.copy(rotation).invert();

root.voxelOBB.updateMatrix();
root.voxelOBB.updateMatrixWorld(true);

root.voxelOBB.matrixWorldInverse = root.voxelOBB.matrixWorld.clone().invert();

root.clampOBB.copy(root.voxelOBB);

const clampBBox = root.clampOBB.box3D;
if (clampBBox.min.z < zmax) {
clampBBox.max.z = Math.min(clampBBox.max.z, zmax);
}
if (clampBBox.max.z > zmin) {
clampBBox.min.z = Math.max(clampBBox.min.z, zmin);
}

root.clampOBB.matrixWorldInverse = root.voxelOBB.matrixWorldInverse;
}

add(node, indexChild) {
add(node) {
this.children.push(node);
node.parent = this;
this.createChildAABB(node, indexChild);
}

/**
* Create an (A)xis (A)ligned (B)ounding (B)ox for the given node given
* `this` is its parent.
* @param {CopcNode} childNode - The child node
* Set the voxelOBB (cubic (O)riented (B)ounding (B)ox for this node.
* It needs to have a parent.
*/
createChildAABB(childNode) {
setVoxelOBBFromParent() {
// initialize the child node obb
childNode.voxelOBB.copy(this.voxelOBB);
const voxelBBox = this.voxelOBB.box3D;
const childVoxelBBox = childNode.voxelOBB.box3D;
this._voxelOBB.copy(this.parent.voxelOBB);
const voxelBBox = this._voxelOBB.box3D;

// factor to apply, based on the depth difference (can be > 1)
const f = 2 ** (childNode.depth - this.depth);
const f = 2 ** (this.depth - this.parent.depth);

// size of the child node bbox (Vector3), based on the size of the
// parent node, and divided by the factor
voxelBBox.getSize(size).divideScalar(f);

// position of the parent node, if it was at the same depth as the
// child, found by multiplying the tree position by the factor
position.copy(this).multiplyScalar(f);
position.copy(this.parent).multiplyScalar(f);

// difference in position between the two nodes, at child depth, and
// scale it using the size
translation.subVectors(childNode, position).multiply(size);
translation.subVectors(this, position).multiply(size);

// apply the translation to the child node bbox
childVoxelBBox.min.add(translation);
voxelBBox.min.add(translation);

// use the size computed above to set the max
childVoxelBBox.max.copy(childVoxelBBox.min).add(size);

// get a clamped bbox from the voxel bbox
childNode.clampOBB.copy(childNode.voxelOBB);

const childClampBBox = childNode.clampOBB.box3D;

if (childClampBBox.min.z < this.source.zmax) {
childClampBBox.max.z = Math.min(childClampBBox.max.z, this.source.zmax);
}
if (childClampBBox.max.z > this.source.zmin) {
childClampBBox.min.z = Math.max(childClampBBox.min.z, this.source.zmin);
}

childNode.voxelOBB.matrixWorldInverse = this.voxelOBB.matrixWorldInverse;
childNode.clampOBB.matrixWorldInverse = this.clampOBB.matrixWorldInverse;
voxelBBox.max.copy(voxelBBox.min).add(size);
}

async loadOctree() {
Expand Down
9 changes: 4 additions & 5 deletions packages/Main/src/Core/Potree2Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ const NODE_TYPE = {
};

class Potree2Node extends PotreeNode {
constructor(numPoints = 0, childrenBitField = 0, source, crs) {
super(numPoints, childrenBitField, source, crs);
constructor(depth, index, numPoints = 0, childrenBitField = 0, source, crs) {
super(depth, index, numPoints, childrenBitField, source, crs);
}

get url() {
Expand Down Expand Up @@ -149,9 +149,8 @@ class Potree2Node extends PotreeNode {
continue;
}

const child = new Potree2Node(numPoints, childMask, this.source, this.crs);

current.add(child, childIndex);
const child = new Potree2Node(current.depth + 1, childIndex, numPoints, childMask, this.source, this.crs);
current.add(child);
stack.push(child);
}
}
Expand Down
Loading