Browse Source

[extensions][gltf] - 3DTILES_content_gltf extension support

[Extensions] - 3DTILES_content_gltf extension support
[types] - incorrect types for existing loaders which now have async parse
[Loaders] - LoaderBase to remove some of the duplicated .load functionality used by the example pages

[fixup] - correct path for gltf extension base
Dave Buchhofer 3 năm trước cách đây
mục cha
commit
90c61babe7

+ 3 - 32
src/base/B3DMLoaderBase.js

@@ -2,42 +2,13 @@
 // https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/Batched3DModel/README.md
 
 import { FeatureTable, BatchTable } from '../utilities/FeatureTable.js';
+import { LoaderBase } from './LoaderBase.js';
 
-export class B3DMLoaderBase {
+export class B3DMLoaderBase extends LoaderBase {
 
 	constructor() {
 
-		this.fetchOptions = {};
-		this.workingPath = '';
-
-	}
-
-	load( url ) {
-
-		return fetch( url, this.fetchOptions )
-			.then( res => {
-
-				if ( ! res.ok ) {
-
-					throw new Error( `Failed to load file "${ url }" with status ${ res.status } : ${ res.statusText }` );
-
-				}
-				return res.arrayBuffer();
-
-			} )
-			.then( buffer => {
-
-				if ( this.workingPath === '' ) {
-
-					const splits = url.split( /\\\//g );
-					splits.pop();
-					this.workingPath = splits.join( '/' );
-
-				}
-
-				return this.parse( buffer );
-
-			} );
+		super();
 
 	}
 

+ 3 - 32
src/base/CMPTLoaderBase.js

@@ -1,41 +1,12 @@
 // CMPT File Format
 // https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/Composite/README.md
+import { LoaderBase } from './LoaderBase.js';
 
-export class CMPTLoaderBase {
+export class CMPTLoaderBase extends LoaderBase {
 
 	constructor() {
 
-		this.fetchOptions = {};
-		this.workingPath = '';
-
-	}
-
-	load( url ) {
-
-		return fetch( url, this.fetchOptions )
-			.then( res => {
-
-				if ( ! res.ok ) {
-
-					throw new Error( `Failed to load file "${ url }" with status ${ res.status } : ${ res.statusText }` );
-
-				}
-				return res.arrayBuffer();
-
-			} )
-			.then( buffer => {
-
-				if ( this.workingPath === '' ) {
-
-					const splits = url.split( /\\\//g );
-					splits.pop();
-					this.workingPath = splits.join( '/' );
-
-				}
-
-				return this.parse( buffer );
-
-			} );
+		super();
 
 	}
 

+ 3 - 46
src/base/I3DMLoaderBase.js

@@ -3,56 +3,13 @@
 
 import { FeatureTable, BatchTable } from '../utilities/FeatureTable.js';
 import { arrayToString } from '../utilities/arrayToString.js';
+import { LoaderBase } from './LoaderBase.js';
 
-export class I3DMLoaderBase {
+export class I3DMLoaderBase extends LoaderBase {
 
 	constructor() {
 
-		this.fetchOptions = {};
-		this.workingPath = '';
-
-	}
-
-	resolveExternalURL( url ) {
-
-		if ( /^[^\\/]/.test( url ) ) {
-
-			return this.workingPath + '/' + url;
-
-		} else {
-
-			return url;
-
-		}
-
-	}
-
-	load( url ) {
-
-		return fetch( url, this.fetchOptions )
-			.then( res => {
-
-				if ( ! res.ok ) {
-
-					throw new Error( `Failed to load file "${ url }" with status ${ res.status } : ${ res.statusText }` );
-
-				}
-				return res.arrayBuffer();
-
-			} )
-			.then( buffer => {
-
-				if ( this.workingPath === '' ) {
-
-					const splits = url.split( /\\\//g );
-					splits.pop();
-					this.workingPath = splits.join( '/' );
-
-				}
-
-				return this.parse( buffer );
-
-			} );
+		super();
 
 	}
 

+ 57 - 0
src/base/LoaderBase.js

@@ -0,0 +1,57 @@
+export class LoaderBase {
+
+	constructor() {
+
+		this.fetchOptions = {};
+		this.workingPath = '';
+
+	}
+
+	load( url ) {
+
+		return fetch( url, this.fetchOptions )
+			.then( res => {
+
+				if ( ! res.ok ) {
+
+					throw new Error( `Failed to load file "${ url }" with status ${ res.status } : ${ res.statusText }` );
+
+				}
+				return res.arrayBuffer();
+
+			} )
+			.then( buffer => {
+
+				if ( this.workingPath === '' ) {
+
+					const splits = url.split( /\\\//g );
+					splits.pop();
+					this.workingPath = splits.join( '/' );
+
+				}
+
+				return this.parse( buffer );
+
+			} );
+
+	}
+
+	resolveExternalURL( url ) {
+
+		if ( /^[^\\/]/.test( url ) ) {
+
+			return this.workingPath + '/' + url;
+
+		} else {
+
+			return url;
+
+		}
+
+	}
+
+	parse( buffer ) {
+
+	}
+
+}

+ 1 - 1
src/base/PNTSLoaderBase.d.ts

@@ -11,6 +11,6 @@ export interface PNTSBaseResult {
 export class PNTSLoaderBase {
 
 	load( url : string ) : Promise< PNTSBaseResult >;
-	parse( buffer : ArrayBuffer ) : PNTSBaseResult;
+	parse( buffer : ArrayBuffer ) : Promise< PNTSBaseResult >;
 
 }

+ 7 - 21
src/base/PNTSLoaderBase.js

@@ -2,29 +2,13 @@
 // https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/PointCloud/README.md
 
 import { FeatureTable, BatchTable } from "../utilities/FeatureTable.js";
+import { LoaderBase } from './LoaderBase.js';
 
-export class PNTSLoaderBase {
+export class PNTSLoaderBase extends LoaderBase {
 
 	constructor() {
 
-		this.fetchOptions = {};
-
-	}
-
-	load( url ) {
-
-		return fetch( url, this.fetchOptions )
-			.then( res => {
-
-				if ( ! res.ok ) {
-
-					throw new Error( `Failed to load file "${ url }" with status ${ res.status } : ${ res.statusText }` );
-
-				}
-				return res.arrayBuffer();
-
-			} )
-			.then( buffer => this.parse( buffer ) );
+		super();
 
 	}
 
@@ -92,11 +76,13 @@ export class PNTSLoaderBase {
 			batchTableBinaryByteLength,
 		);
 
-		return {
+		return Promise.resolve( {
+
 			version,
 			featureTable,
 			batchTable,
-		};
+
+		} );
 
 	}
 

+ 1 - 1
src/three/B3DMLoader.d.ts

@@ -20,6 +20,6 @@ export class B3DMLoader {
 
 	constructor( manager : LoadingManager );
 	load( url : String ) : Promise< B3DMResult >;
-	parse( buffer : ArrayBuffer ) : B3DMResult;
+	parse( buffer : ArrayBuffer ) : Promise < B3DMResult >;
 
 }

+ 1 - 1
src/three/CMPTLoader.d.ts

@@ -14,6 +14,6 @@ export class CMPTLoader {
 	
 	constructor( manager : LoadingManager );
 	load( url : String ) : Promise< CMPTResult >;
-	parse( buffer : ArrayBuffer ) : CMPTResult;
+	parse( buffer : ArrayBuffer ) : Promise< CMPTResult >;
 
 }

+ 17 - 2
src/three/CMPTLoader.js

@@ -3,6 +3,7 @@ import { CMPTLoaderBase } from '../base/CMPTLoaderBase.js';
 import { B3DMLoader } from './B3DMLoader.js';
 import { PNTSLoader } from './PNTSLoader.js';
 import { I3DMLoader } from './I3DMLoader.js';
+import { GLTFExtensionLoader } from './GLTFExtensionLoader.js';
 
 export class CMPTLoader extends CMPTLoaderBase {
 
@@ -40,8 +41,10 @@ export class CMPTLoader extends CMPTLoaderBase {
 				case 'pnts': {
 
 					const slicedBuffer = buffer.slice();
-					const pointsResult = new PNTSLoader( manager ).parse( slicedBuffer.buffer );
-					const promise = Promise.resolve( pointsResult );
+					const loader = new PNTSLoader( manager );
+					loader.workingPath = this.workingPath;
+					loader.fetchOptions = this.fetchOptions;
+					const promise = loader.parse( slicedBuffer.buffer );
 					promises.push( promise );
 					break;
 
@@ -60,6 +63,18 @@ export class CMPTLoader extends CMPTLoaderBase {
 
 				}
 
+				// 3DTILES_content_gltf
+				case 'glb':
+				case 'gltf':
+					const slicedBuffer = buffer.slice();
+					const loader = new GLTFExtensionLoader( manager );
+					loader.workingPath = this.workingPath;
+					loader.fetchOptions = this.fetchOptions;
+
+					const promise = loader.parse( slicedBuffer.buffer );
+					promises.push( promise );
+					break;
+
 			}
 
 		}

+ 10 - 0
src/three/GLTFExtensionLoader.d.ts

@@ -0,0 +1,10 @@
+
+import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { LoadingManager } from 'three';
+
+export class GLTFExtensionLoader {
+
+	constructor( manager : LoadingManager );
+	parse( buffer : ArrayBuffer ) : Promise< GLTF >;
+
+}

+ 39 - 0
src/three/GLTFExtensionLoader.js

@@ -0,0 +1,39 @@
+import { DefaultLoadingManager } from 'three';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { LoaderBase } from '../base/LoaderBase.js';
+
+export class GLTFExtensionLoader extends LoaderBase {
+
+	constructor( manager = DefaultLoadingManager ) {
+
+		super();
+		this.manager = manager;
+
+	}
+
+	parse( buffer ) {
+
+		return new Promise( ( resolve, reject ) => {
+
+			const manager = this.manager;
+			const loader = manager.getHandler( 'path.gltf' ) || manager.getHandler( 'path.glb' ) || new GLTFLoader( manager );
+
+			// GLTFLoader assumes the working path ends in a slash
+			let workingPath = this.workingPath;
+			if ( ! /[\\/]$/.test( workingPath ) ) {
+
+				workingPath += '/';
+
+			}
+
+			loader.parse( buffer, workingPath, model => {
+
+				resolve( model );
+
+			}, reject );
+
+		} );
+
+	}
+
+}

+ 1 - 1
src/three/I3DMLoader.d.ts

@@ -20,6 +20,6 @@ export class I3DMLoader {
 
 	constructor( manager : LoadingManager );
 	load( url : String ) : Promise< I3DMResult >;
-	parse( buffer : ArrayBuffer ) : I3DMResult;
+	parse( buffer : ArrayBuffer ) : Promise< I3DMResult >;
 	
 }

+ 3 - 3
src/three/PNTSLoader.d.ts

@@ -1,4 +1,4 @@
-import { PNTSBaseResult } from '../base/PNTSLoaderBase';
+import { PNTSBaseResult, PNTSLoaderBase } from '../base/PNTSLoaderBase';
 import { FeatureTable } from '../utilities/FeatureTable';
 import { Points, LoadingManager } from 'three';
 
@@ -14,10 +14,10 @@ export interface PNTSResult extends PNTSBaseResult {
 
 }
 
-export class PNTSLoader {
+export class PNTSLoader extends PNTSLoaderBase {
 
 	constructor( manager : LoadingManager );
 	load( url : String ) : Promise< PNTSResult >;
-	parse( buffer : ArrayBuffer ) : PNTSResult;
+	parse( buffer : ArrayBuffer ) : Promise< PNTSResult >;
 
 }

+ 45 - 40
src/three/PNTSLoader.js

@@ -12,63 +12,68 @@ export class PNTSLoader extends PNTSLoaderBase {
 
 	parse( buffer ) {
 
-		const result = super.parse( buffer );
-		const { featureTable } = result;
+		return super
+			.parse( buffer )
+			.then( 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 { featureTable } = result;
 
-		[
-			'RTC_CENTER',
-			'QUANTIZED_VOLUME_OFFSET',
-			'QUANTIZED_VOLUME_SCALE',
-			'CONSTANT_RGBA',
-			'BATCH_LENGTH',
-			'POSITION_QUANTIZED',
-			'RGBA',
-			'RGB565',
-			'NORMAL',
-			'NORMAL_OCT16P',
-		].forEach( feature => {
+				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' );
 
-			if ( feature in featureTable.header ) {
+				[
+					'RTC_CENTER',
+					'QUANTIZED_VOLUME_OFFSET',
+					'QUANTIZED_VOLUME_SCALE',
+					'CONSTANT_RGBA',
+					'BATCH_LENGTH',
+					'POSITION_QUANTIZED',
+					'RGBA',
+					'RGB565',
+					'NORMAL',
+					'NORMAL_OCT16P',
+				].forEach( feature => {
 
-				console.warn( `PNTSLoader: Unsupported FeatureTable feature "${ feature }" detected.` );
+					if ( feature in featureTable.header ) {
 
-			}
+						console.warn( `PNTSLoader: Unsupported FeatureTable feature "${ feature }" detected.` );
 
-		} );
+					}
 
-		const geometry = new BufferGeometry();
-		geometry.setAttribute( 'position', new BufferAttribute( POSITION, 3, false ) );
+				} );
 
-		const material = new PointsMaterial();
-		material.size = 2;
-		material.sizeAttenuation = false;
+				const geometry = new BufferGeometry();
+				geometry.setAttribute( 'position', new BufferAttribute( POSITION, 3, false ) );
 
-		if ( RGB !== null ) {
+				const material = new PointsMaterial();
+				material.size = 2;
+				material.sizeAttenuation = false;
 
-			geometry.setAttribute( 'color', new BufferAttribute( RGB, 3, true ) );
-			material.vertexColors = true;
+				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' );
+				const object = new Points( geometry, material );
+				result.scene = object;
+				result.scene.featureTable = featureTable;
 
-		if ( rtcCenter ) {
+				const rtcCenter = featureTable.getData( 'RTC_CENTER' );
 
-			result.scene.position.x += rtcCenter[ 0 ];
-			result.scene.position.y += rtcCenter[ 1 ];
-			result.scene.position.z += rtcCenter[ 2 ];
+				if ( rtcCenter ) {
 
-		}
+					result.scene.position.x += rtcCenter[ 0 ];
+					result.scene.position.y += rtcCenter[ 1 ];
+					result.scene.position.z += rtcCenter[ 2 ];
 
-		return result;
+				}
+
+				return result;
+
+			} );
 
 	}
 

+ 1 - 2
src/three/TilesRenderer.d.ts

@@ -13,7 +13,7 @@ export class TilesRenderer extends TilesRendererBase {
 
 	group : TilesGroup;
 
-	getBoundsTransform(target: Matrix4) : Boolean;
+	getBoundsTransform( target: Matrix4 ) : Boolean;
 
 	getBounds( box : Box3 ) : Boolean;
 
@@ -31,5 +31,4 @@ export class TilesRenderer extends TilesRendererBase {
 	onLoadModel : ( ( scene : Object3D, tile : Tile ) => void ) | null;
 	onDisposeModel : ( ( scene : Object3D, tile : Tile ) => void ) | null;
 
-
 }

+ 26 - 5
src/three/TilesRenderer.js

@@ -3,6 +3,7 @@ import { B3DMLoader } from './B3DMLoader.js';
 import { PNTSLoader } from './PNTSLoader.js';
 import { I3DMLoader } from './I3DMLoader.js';
 import { CMPTLoader } from './CMPTLoader.js';
+import { GLTFExtensionLoader } from './GLTFExtensionLoader.js';
 import { TilesGroup } from './TilesGroup.js';
 import {
 	Matrix4,
@@ -581,7 +582,8 @@ export class TilesRenderer extends TilesRendererBase {
 
 		switch ( extension ) {
 
-			case 'b3dm':
+			case 'b3dm': {
+
 				const loader = new B3DMLoader( manager );
 				loader.workingPath = workingPath;
 				loader.fetchOptions = fetchOptions;
@@ -590,10 +592,20 @@ export class TilesRenderer extends TilesRendererBase {
 					.then( res => res.scene );
 				break;
 
-			case 'pnts':
-				promise = Promise.resolve( new PNTSLoader( manager ).parse( buffer ).scene );
+			}
+
+			case 'pnts': {
+
+				const loader = new PNTSLoader( manager );
+				loader.workingPath = workingPath;
+				loader.fetchOptions = fetchOptions;
+				promise = loader
+					.parse( buffer )
+					.then( res => res.scene );
 				break;
 
+			}
+
 			case 'i3dm': {
 
 				const loader = new I3DMLoader( manager );
@@ -602,7 +614,6 @@ export class TilesRenderer extends TilesRendererBase {
 				promise = loader
 					.parse( buffer )
 					.then( res => res.scene );
-
 				break;
 
 			}
@@ -615,11 +626,21 @@ export class TilesRenderer extends TilesRendererBase {
 				promise = loader
 					.parse( buffer )
 					.then( res => res.scene	);
-
 				break;
 
 			}
 
+			// 3DTILES_content_gltf
+			case 'gltf':
+			case 'glb':
+				const loader = new GLTFExtensionLoader( manager );
+				loader.workingPath = workingPath;
+				loader.fetchOptions = fetchOptions;
+				promise = loader
+					.parse( buffer )
+					.then( res => res.scene	);
+				break;
+
 			default:
 				console.warn( `TilesRenderer: Content type "${ extension }" not supported.` );
 				promise = Promise.resolve( null );