Skip to content

Commit

Permalink
Feature: Draco PNTS proposal alternative (#329)
Browse files Browse the repository at this point in the history
* support draco

* update readme

* fix lint

* clearer name

* Consolidate draco changes to PNTSLoader

* Update dts

* Fixes

* Final fixes?

---------

Co-authored-by: George Madges <[email protected]>
  • Loading branch information
gkjohnson and Gmadges authored May 12, 2023
1 parent b832f92 commit dbc3c20
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 37 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]/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.
Expand Down
131 changes: 94 additions & 37 deletions src/three/PNTSLoader.js
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -12,68 +23,114 @@ 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 ) );
material.vertexColors = true;

}

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;

} );

}

}
11 changes: 11 additions & 0 deletions src/utilities/FeatureTable.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
interface FeatureTableHeader {

extensions?: Object;
extras?: any;

}

export class FeatureTable {

header: FeatureTableHeader;

constructor(
buffer : ArrayBuffer,
start : Number,
Expand All @@ -16,6 +25,8 @@ export class FeatureTable {
defaultType? : String | null
) : Number | String | ArrayBufferView;

getBuffer( byteOffset : Number, byteLength : Number ) : ArrayBuffer;

}

export class BatchTable {
Expand Down
7 changes: 7 additions & 0 deletions src/utilities/FeatureTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit dbc3c20

Please sign in to comment.