diff --git a/modules/3d-tiles/src/parsers/helpers/normalize-3d-tile-positions.js b/modules/3d-tiles/src/parsers/helpers/normalize-3d-tile-positions.js index 464a90c48f..3947ec612b 100644 --- a/modules/3d-tiles/src/parsers/helpers/normalize-3d-tile-positions.js +++ b/modules/3d-tiles/src/parsers/helpers/normalize-3d-tile-positions.js @@ -1,24 +1,45 @@ import {Vector3} from 'math.gl'; +import {GL} from '@loaders.gl/math'; -const scratchPosition = new Vector3(); +// Prepare attribute for positions +export function normalize3DTilePositionAttribute(tile, positions, options) { + if (!tile.isQuantized) { + return positions; + } + + // For quantized posititions, either expand to Float32Array or return custom accessor + // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/TileFormats/Instanced3DModel/README.md#quantized-positions -export function normalize3DTilePositionAttribute(tile, positions) { - if (tile.isQuantized) { - // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/TileFormats/Instanced3DModel/README.md#quantized-positions - // POSITION_QUANTIZED * QUANTIZED_VOLUME_SCALE / 65535.0 + QUANTIZED_VOLUME_OFFSET - const decodedArray = new Float32Array(tile.pointCount * 3); - for (let i = 0; i < tile.pointCount; i++) { - scratchPosition - .set(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]) - .multiply(tile.quantizedVolumeScale) - .scale(1 / tile.quantizedRange) - .add(tile.quantizedVolumeOffset); + // Optionally decodes quantized positions on GPU, for simpler renderers that don't accept normalized attributes + if (options.decodeQuantizedPositions) { + tile.isQuantized = false; + return decodeQuantizedPositions(tile, positions); + } + + // Default: Use normalized shorts directly, no copying/processing. + // NOTE: The "missing" offset/scaling operations are automatically added to modelMatrix if `tile.isQuantized === true` + return { + type: GL.UNSIGNED_SHORT, + value: positions, + size: 3, + normalized: true + }; +} - scratchPosition.toArray(decodedArray, i * 3); - } +// Pre-scale quantized positions on CPU +function decodeQuantizedPositions(tile, positions) { + const scratchPosition = new Vector3(); + const decodedArray = new Float32Array(tile.pointCount * 3); - return decodedArray; + for (let i = 0; i < tile.pointCount; i++) { + // POSITION = POSITION_QUANTIZED / 65535.0 * QUANTIZED_VOLUME_SCALE + QUANTIZED_VOLUME_OFFSET + scratchPosition + .set(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]) + .scale(1 / tile.quantizedRange) + .multiply(tile.quantizedVolumeScale) + .add(tile.quantizedVolumeOffset) + .toArray(decodedArray, i * 3); } - return positions; + return decodedArray; } diff --git a/modules/3d-tiles/src/parsers/parse-3d-tile-point-cloud.js b/modules/3d-tiles/src/parsers/parse-3d-tile-point-cloud.js index d65a9d4bd8..1383a5add4 100644 --- a/modules/3d-tiles/src/parsers/parse-3d-tile-point-cloud.js +++ b/modules/3d-tiles/src/parsers/parse-3d-tile-point-cloud.js @@ -29,7 +29,7 @@ export function parsePointCloud3DTileSync(tile, arrayBuffer, byteOffset, options byteOffset = parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset, options); byteOffset = parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options); - extractPointCloudSync(tile); + extractPointCloudSync(tile, options); return byteOffset; } @@ -42,19 +42,19 @@ async function extractPointCloud(tile, options) { await parseDraco(tile, featureTable, batchTable, options); - parsePositions(tile, featureTable); - parseColors(tile, featureTable, batchTable); - parseNormals(tile, featureTable); + parsePositions(tile, featureTable, options); + parseColors(tile, featureTable, batchTable, options); + parseNormals(tile, featureTable, options); } -function extractPointCloudSync(tile) { +function extractPointCloudSync(tile, options) { initializeTile(tile); const {featureTable} = parsePointCloudTables(tile); - parsePositions(tile, featureTable); - parseColors(tile, featureTable); - parseNormals(tile, featureTable); + parsePositions(tile, featureTable, options); + parseColors(tile, featureTable, options); + parseNormals(tile, featureTable, options); } function initializeTile(tile) { @@ -91,7 +91,7 @@ function parsePointCloudTables(tile) { return {featureTable, batchTable}; } -function parsePositions(tile, featureTable) { +function parsePositions(tile, featureTable, options) { if (!tile.attributes.positions) { if (featureTable.hasProperty('POSITION')) { tile.attributes.positions = featureTable.getPropertyArray('POSITION', GL.FLOAT, 3); @@ -119,7 +119,7 @@ function parsePositions(tile, featureTable) { throw new Error('QUANTIZED_VOLUME_OFFSET must be defined for quantized positions.'); } - tile.attributes.positions = normalize3DTilePositionAttribute(tile, positions); + tile.attributes.positions = normalize3DTilePositionAttribute(tile, positions, options); } } diff --git a/modules/3d-tiles/src/tileset/helpers/transform-utils.js b/modules/3d-tiles/src/tileset/helpers/transform-utils.js index cd91dbc499..0b9df0c0a0 100644 --- a/modules/3d-tiles/src/tileset/helpers/transform-utils.js +++ b/modules/3d-tiles/src/tileset/helpers/transform-utils.js @@ -25,6 +25,10 @@ export function calculateTransformProps(tileHeader, tile) { modelMatrix.translate(rtcCenter); } + if (tile.isQuantized) { + modelMatrix.translate(tile.quantizedVolumeOffset).scale(tile.quantizedVolumeScale); + } + tile.cartesianOrigin = cartesianOrigin; tile.cartographicOrigin = cartographicOrigin; tile.modelMatrix = modelMatrix;