Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Draco PNTS proposal alternative #329

Merged
merged 9 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
132 changes: 95 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,115 @@ 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 ) {
const mappedKey = DRACO_ATTRIBUTE_MAP[ key ];
if ( mappedKey in attributeIds ) {
gkjohnson marked this conversation as resolved.
Show resolved Hide resolved

console.warn( `PNTSLoader: Unsupported FeatureTable feature "${ feature }" detected.` );
attributeIDs[ mappedKey ] = properties[ key ];

}

} );
}

// decode the geometry
const taskConfig = {
attributeIDs,
attributeTypes: {
position: 'Float32Array',
color: 'Uint8Array',
},
useUniqueIDs: true,
};

const buffer = featureTable.getDracoBuffer( byteOffset, byteLength );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getDracoBuffer -> getBuffer

const geometry = await dracoLoader.decodeGeometry( buffer, taskConfig );
geometry.copy( dracoGeometry );
gkjohnson marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This copy isn't required anymore

if ( geometry.attributes.color ) {

const geometry = new BufferGeometry();
geometry.setAttribute( 'position', new BufferAttribute( POSITION, 3, false ) );
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