diff --git a/src/Layer/EntwinePointTileLayer.js b/src/Layer/EntwinePointTileLayer.js index fc39afac1f..841bfb31aa 100644 --- a/src/Layer/EntwinePointTileLayer.js +++ b/src/Layer/EntwinePointTileLayer.js @@ -47,6 +47,7 @@ class EntwinePointTileLayer extends PointCloudLayer { constructor(id, config) { super(id, config); this.isEntwinePointTileLayer = true; + this.scale = new THREE.Vector3(1, 1, 1); const resolve = this.addInitializationStep(); this.whenReady = this.source.whenReady.then(() => { diff --git a/src/Parser/LASLoader.js b/src/Parser/LASLoader.js index 324f567856..c4ca244016 100644 --- a/src/Parser/LASLoader.js +++ b/src/Parser/LASLoader.js @@ -71,13 +71,16 @@ class LASLoader { */ const scanAngles = new Float32Array(view.pointCount); + // For precision we take the first point that will be use as origin for a local referentiel. + const origin = getPosition.map(f => f(0)).map(val => Math.floor(val)); + for (let i = 0; i < view.pointCount; i++) { // `getPosition` apply scale and offset transform to the X, Y, Z // values. See https://github.com/connormanning/copc.js/blob/master/src/las/extractor.ts. const [x, y, z] = getPosition.map(f => f(i)); - positions[i * 3] = x; - positions[i * 3 + 1] = y; - positions[i * 3 + 2] = z; + positions[i * 3] = x - origin[0]; + positions[i * 3 + 1] = y - origin[1]; + positions[i * 3 + 2] = z - origin[2]; intensities[i] = getIntensity(i); returnNumbers[i] = getReturnNumber(i); @@ -115,6 +118,7 @@ class LASLoader { pointSourceID: pointSourceIDs, color: colors, scanAngle: scanAngles, + origin, }; } diff --git a/src/Parser/LASParser.js b/src/Parser/LASParser.js index 33896cd077..12a6510e86 100644 --- a/src/Parser/LASParser.js +++ b/src/Parser/LASParser.js @@ -73,6 +73,7 @@ export default { geometry.setAttribute('scanAngle', scanAngle); geometry.computeBoundingBox(); + geometry.userData.origin = new THREE.Vector3().fromArray(attributes.origin); return geometry; }); }, diff --git a/src/Parser/PotreeBinParser.js b/src/Parser/PotreeBinParser.js index f1d4ee05e4..996ab8e4eb 100644 --- a/src/Parser/PotreeBinParser.js +++ b/src/Parser/PotreeBinParser.js @@ -2,7 +2,7 @@ import * as THREE from 'three'; // See the different constants holding ordinal, name, numElements, byteSize in PointAttributes.cpp in PotreeConverter // elementByteSize is byteSize / numElements -const POINT_ATTTRIBUTES = { +const POINT_ATTRIBUTES = { POSITION_CARTESIAN: { numElements: 3, arrayType: Float32Array, @@ -49,8 +49,8 @@ const POINT_ATTTRIBUTES = { }, }; -for (const potreeName of Object.keys(POINT_ATTTRIBUTES)) { - const attr = POINT_ATTTRIBUTES[potreeName]; +for (const potreeName of Object.keys(POINT_ATTRIBUTES)) { + const attr = POINT_ATTRIBUTES[potreeName]; attr.potreeName = potreeName; attr.numByte = attr.numByte || attr.arrayType.BYTES_PER_ELEMENT; attr.byteSize = attr.numElements * attr.numByte; @@ -81,7 +81,7 @@ export default { // Format: X1,Y1,Z1,R1,G1,B1,A1,[...],XN,YN,ZN,RN,GN,BN,AN let pointByteSize = 0; for (const potreeName of options.in.pointAttributes) { - pointByteSize += POINT_ATTTRIBUTES[potreeName].byteSize; + pointByteSize += POINT_ATTRIBUTES[potreeName].byteSize; } const numPoints = Math.floor(buffer.byteLength / pointByteSize); @@ -89,7 +89,7 @@ export default { let elemOffset = 0; let attrOffset = 0; for (const potreeName of options.in.pointAttributes) { - const attr = POINT_ATTTRIBUTES[potreeName]; + const attr = POINT_ATTRIBUTES[potreeName]; const arrayLength = attr.numElements * numPoints; const array = new attr.arrayType(arrayLength); for (let arrayOffset = 0; arrayOffset < arrayLength; arrayOffset += attr.numElements) { diff --git a/src/Provider/PointCloudProvider.js b/src/Provider/PointCloudProvider.js index c1788fbb53..57e24bcbf5 100644 --- a/src/Provider/PointCloudProvider.js +++ b/src/Provider/PointCloudProvider.js @@ -36,10 +36,8 @@ export default { addPickingAttribute(points); points.frustumCulled = false; points.matrixAutoUpdate = false; - if (!layer.isEntwinePointTileLayer) { - points.position.copy(node.bbox.min); - points.scale.copy(layer.scale); - } + points.position.copy(geometry.userData.origin || node.bbox.min); + points.scale.copy(layer.scale); points.updateMatrix(); points.tightbbox = geometry.boundingBox.applyMatrix4(points.matrix); points.layer = layer; diff --git a/test/unit/lasparser.js b/test/unit/lasparser.js index 4f87dbed08..a52332fd98 100644 --- a/test/unit/lasparser.js +++ b/test/unit/lasparser.js @@ -26,12 +26,12 @@ describe('LASParser', function () { assert.strictEqual(bufferGeometry.attributes.classification.count, bufferGeometry.userData.pointCount); assert.strictEqual(bufferGeometry.attributes.color, undefined); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.x, bufferGeometry.userData.min[0], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.y, bufferGeometry.userData.min[1], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.z, bufferGeometry.userData.min[2], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x, bufferGeometry.userData.max[0], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y, bufferGeometry.userData.max[1], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z, bufferGeometry.userData.max[2], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.x + bufferGeometry.userData.origin.x, bufferGeometry.userData.min[0], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.y + bufferGeometry.userData.origin.y, bufferGeometry.userData.min[1], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.z + bufferGeometry.userData.origin.z, bufferGeometry.userData.min[2], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x + bufferGeometry.userData.origin.x, bufferGeometry.userData.max[0], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y + bufferGeometry.userData.origin.y, bufferGeometry.userData.max[1], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z + bufferGeometry.userData.origin.z, bufferGeometry.userData.max[2], 0.1)); }); describe('parses a laz file to a THREE.BufferGeometry', function () { @@ -43,12 +43,12 @@ describe('LASParser', function () { assert.strictEqual(bufferGeometry.attributes.classification.count, bufferGeometry.userData.pointCount); assert.strictEqual(bufferGeometry.attributes.color, undefined); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.x, bufferGeometry.userData.min[0], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.y, bufferGeometry.userData.min[1], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.z, bufferGeometry.userData.min[2], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x, bufferGeometry.userData.max[0], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y, bufferGeometry.userData.max[1], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z, bufferGeometry.userData.max[2], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.x + bufferGeometry.userData.origin.x, bufferGeometry.userData.min[0], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.y + bufferGeometry.userData.origin.y, bufferGeometry.userData.min[1], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.z + bufferGeometry.userData.origin.z, bufferGeometry.userData.min[2], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x + bufferGeometry.userData.origin.x, bufferGeometry.userData.max[0], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y + bufferGeometry.userData.origin.y, bufferGeometry.userData.max[1], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z + bufferGeometry.userData.origin.z, bufferGeometry.userData.max[2], 0.1)); }); it('laz v1.4', async () => { @@ -59,12 +59,12 @@ describe('LASParser', function () { assert.strictEqual(bufferGeometry.attributes.classification.count, bufferGeometry.userData.pointCount); assert.strictEqual(bufferGeometry.attributes.color.count, bufferGeometry.userData.pointCount); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.x, bufferGeometry.userData.min[0], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.y, bufferGeometry.userData.min[1], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.z, bufferGeometry.userData.min[2], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x, bufferGeometry.userData.max[0], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y, bufferGeometry.userData.max[1], 0.1)); - assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z, bufferGeometry.userData.max[2], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.x + bufferGeometry.userData.origin.x, bufferGeometry.userData.min[0], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.y + bufferGeometry.userData.origin.y, bufferGeometry.userData.min[1], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.min.z + bufferGeometry.userData.origin.z, bufferGeometry.userData.min[2], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.x + bufferGeometry.userData.origin.x, bufferGeometry.userData.max[0], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.y + bufferGeometry.userData.origin.y, bufferGeometry.userData.max[1], 0.1)); + assert.ok(compareWithEpsilon(bufferGeometry.boundingBox.max.z + bufferGeometry.userData.origin.z, bufferGeometry.userData.max[2], 0.1)); }); }); });