diff --git a/README.md b/README.md index f95af6f3f..9327ce724 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,21 @@ const tilesRenderer = new TilesRenderer( './path/to/tileset.json' ); tilesRenderer.manager.addHandler( /\.gltf$/, loader ); ``` +Adding support for DRACO decompression within the PNTS files. + +```js + +// Note the DRACO compression files need to be supplied via an explicit source. +// We use unpkg here but in practice should be provided by the application. +const dracoLoader = new DRACOLoader(); +dracoLoader.setDecoderPath( 'https://unpkg.com/three@0.123.0/examples/js/libs/draco/gltf/' ); + + +const tilesRenderer = new TilesRenderer( './path/to/tileset.json' ); +tilesRenderer.manager.addHandler( /\.drc$/, loader ); +``` + + ## Loading from Cesium Ion Loading from Cesium Ion requires some extra fetching of the ion url endpoint, as well as a temporary bearer access token. A full example is found in the ionExample.js file in the examples folder. diff --git a/src/three/PNTSLoader.js b/src/three/PNTSLoader.js index 2c3a3dc33..4a47b62d4 100644 --- a/src/three/PNTSLoader.js +++ b/src/three/PNTSLoader.js @@ -1,5 +1,16 @@ import { PNTSLoaderBase } from '../base/PNTSLoaderBase.js'; -import { Points, PointsMaterial, BufferGeometry, BufferAttribute, DefaultLoadingManager } from 'three'; +import { + Points, + PointsMaterial, + BufferGeometry, + BufferAttribute, + DefaultLoadingManager, +} from 'three'; + +const DRACO_ATTRIBUTE_MAP = { + RGB: 'color', + POSITION: 'position', +}; export class PNTSLoader extends PNTSLoaderBase { @@ -12,43 +23,65 @@ export class PNTSLoader extends PNTSLoaderBase { parse( buffer ) { - return super - .parse( buffer ) - .then( result => { + return super.parse( buffer ).then( async ( result ) => { - const { featureTable } = result; + const { featureTable } = result; - const POINTS_LENGTH = featureTable.getData( 'POINTS_LENGTH' ); - const POSITION = featureTable.getData( 'POSITION', POINTS_LENGTH, 'FLOAT', 'VEC3' ); - const RGB = featureTable.getData( 'RGB', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC3' ); + const material = new PointsMaterial(); + const extensions = featureTable.header.extensions; + let geometry; + + // handle loading the draco data + if ( extensions && extensions[ '3DTILES_draco_point_compression' ] ) { + + const { byteOffset, byteLength, properties } = extensions[ '3DTILES_draco_point_compression' ]; + const dracoLoader = this.manager.getHandler( 'draco.drc' ); + if ( dracoLoader == null ) { + + throw new Error( 'PNTSLoader: dracoLoader not available.' ); + + } - [ - 'QUANTIZED_VOLUME_OFFSET', - 'QUANTIZED_VOLUME_SCALE', - 'CONSTANT_RGBA', - 'BATCH_LENGTH', - 'POSITION_QUANTIZED', - 'RGBA', - 'RGB565', - 'NORMAL', - 'NORMAL_OCT16P', - ].forEach( feature => { + // map PNTS keys to draco types + const attributeIDs = {}; + for ( const key in properties ) { - if ( feature in featureTable.header ) { + if ( key in DRACO_ATTRIBUTE_MAP && key in properties ) { - console.warn( `PNTSLoader: Unsupported FeatureTable feature "${ feature }" detected.` ); + const mappedKey = DRACO_ATTRIBUTE_MAP[ key ]; + attributeIDs[ mappedKey ] = properties[ key ]; } - } ); + } + + // decode the geometry + const taskConfig = { + attributeIDs, + attributeTypes: { + position: 'Float32Array', + color: 'Uint8Array', + }, + useUniqueIDs: true, + }; - const geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new BufferAttribute( POSITION, 3, false ) ); + const buffer = featureTable.getBuffer( byteOffset, byteLength ); + geometry = await dracoLoader.decodeGeometry( buffer, taskConfig ); + if ( geometry.attributes.color ) { + + material.vertexColors = true; + + } - const material = new PointsMaterial(); - material.size = 2; - material.sizeAttenuation = false; + } else { + // handle non compressed case + const POINTS_LENGTH = featureTable.getData( 'POINTS_LENGTH' ); + const POSITION = featureTable.getData( 'POSITION', POINTS_LENGTH, 'FLOAT', 'VEC3' ); + const RGB = featureTable.getData( 'RGB', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC3' ); + + geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new BufferAttribute( POSITION, 3, false ) ); if ( RGB !== null ) { geometry.setAttribute( 'color', new BufferAttribute( RGB, 3, true ) ); @@ -56,24 +89,48 @@ export class PNTSLoader extends PNTSLoaderBase { } - const object = new Points( geometry, material ); - result.scene = object; - result.scene.featureTable = featureTable; + } - const rtcCenter = featureTable.getData( 'RTC_CENTER' ); + [ + 'QUANTIZED_VOLUME_OFFSET', + 'QUANTIZED_VOLUME_SCALE', + 'CONSTANT_RGBA', + 'BATCH_LENGTH', + 'POSITION_QUANTIZED', + 'RGBA', + 'RGB565', + 'NORMAL', + 'NORMAL_OCT16P', + ].forEach( ( feature ) => { - if ( rtcCenter ) { + if ( feature in featureTable.header ) { - result.scene.position.x += rtcCenter[ 0 ]; - result.scene.position.y += rtcCenter[ 1 ]; - result.scene.position.z += rtcCenter[ 2 ]; + console.warn( + `PNTSLoader: Unsupported FeatureTable feature "${feature}" detected.` + ); } - return result; - } ); + const object = new Points( geometry, material ); + result.scene = object; + result.scene.featureTable = featureTable; + + const rtcCenter = featureTable.getData( 'RTC_CENTER' ); + + if ( rtcCenter ) { + + result.scene.position.x += rtcCenter[ 0 ]; + result.scene.position.y += rtcCenter[ 1 ]; + result.scene.position.z += rtcCenter[ 2 ]; + + } + + return result; + + } ); + } } diff --git a/src/utilities/FeatureTable.d.ts b/src/utilities/FeatureTable.d.ts index d91ec30d5..a827113df 100644 --- a/src/utilities/FeatureTable.d.ts +++ b/src/utilities/FeatureTable.d.ts @@ -1,5 +1,14 @@ +interface FeatureTableHeader { + + extensions?: Object; + extras?: any; + +} + export class FeatureTable { + header: FeatureTableHeader; + constructor( buffer : ArrayBuffer, start : Number, @@ -16,6 +25,8 @@ export class FeatureTable { defaultType? : String | null ) : Number | String | ArrayBufferView; + getBuffer( byteOffset : Number, byteLength : Number ) : ArrayBuffer; + } export class BatchTable { diff --git a/src/utilities/FeatureTable.js b/src/utilities/FeatureTable.js index aa8e34d2b..10b1e64ed 100644 --- a/src/utilities/FeatureTable.js +++ b/src/utilities/FeatureTable.js @@ -141,6 +141,13 @@ export class FeatureTable { } + getBuffer( byteOffset, byteLength ) { + + const { buffer, binOffset } = this; + return buffer.slice( binOffset + byteOffset, binOffset + byteOffset + byteLength ); + + } + } export class BatchTable extends FeatureTable {