浏览代码

Merge pull request #10964 from donmccurdy/feat-gltf2-binary

[glTF] Update binary (.GLB) parsing for glTF 2.0.
Mr.doob 8 年之前
父节点
当前提交
2f587e670d
共有 1 个文件被更改,包括 70 次插入42 次删除
  1. 70 42
      examples/js/loaders/GLTF2Loader.js

+ 70 - 42
examples/js/loaders/GLTF2Loader.js

@@ -55,7 +55,7 @@ THREE.GLTF2Loader = ( function () {
 
 			var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) );
 
-			if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) {
+			if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) {
 
 				extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
 				content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
@@ -320,63 +320,70 @@ THREE.GLTF2Loader = ( function () {
 	/* BINARY EXTENSION */
 
 	var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF';
-
-	var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 };
-
-	var BINARY_EXTENSION_HEADER_LENGTH = 20;
+	var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
+	var BINARY_EXTENSION_HEADER_LENGTH = 12;
+	var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
 
 	function GLTFBinaryExtension( data ) {
 
 		this.name = EXTENSIONS.KHR_BINARY_GLTF;
+		this.content = null;
+		this.body = null;
 
 		var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH );
 
-		var header = {
+		this.header = {
 			magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ),
 			version: headerView.getUint32( 4, true ),
-			length: headerView.getUint32( 8, true ),
-			contentLength: headerView.getUint32( 12, true ),
-			contentFormat: headerView.getUint32( 16, true )
+			length: headerView.getUint32( 8, true )
 		};
 
-		for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) {
+		if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) {
 
-			var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ];
+			throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' );
 
-			if ( header[ key ] !== value ) {
+		} else if ( this.header.version < 2.0 ) {
 
-				throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value );
-
-			}
+			throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' );
 
 		}
 
-		var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength );
+		var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH );
+		var chunkIndex = 0;
 
-		this.header = header;
-		this.content = convertUint8ArrayToString( contentArray );
-		this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length );
+		while ( chunkIndex < chunkView.byteLength ) {
 
-	}
+			var chunkLength = chunkView.getUint32( chunkIndex, true );
+			chunkIndex += 4;
 
-	GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) {
+			var chunkType = chunkView.getUint32( chunkIndex, true );
+			chunkIndex += 4;
 
-		var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ];
-		var array = new Uint8Array( bufferView );
+			if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) {
 
-		return convertUint8ArrayToString( array );
+				var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength );
+				this.content = convertUint8ArrayToString( contentArray );
 
-	};
+			} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) {
 
-	GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) {
+				var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
+				this.body = data.slice( byteOffset, byteOffset + chunkLength );
 
-		var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ];
-		var bufferView = bufferViews[ metadata.bufferView ];
-		var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) );
+			}
 
-		return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData );
+			// Clients must ignore chunks with unknown types.
 
-	};
+			chunkIndex += chunkLength;
+
+		}
+
+		if ( this.content === null ) {
+
+			throw new Error( 'GLTF2Loader: JSON content not found.' );
+
+		}
+
+	}
 
 	/*********************************/
 	/********** INTERNALS ************/
@@ -633,6 +640,13 @@ THREE.GLTF2Loader = ( function () {
 
 		}
 
+		// Blob URL
+		if ( /^blob:.*$/i.test( url ) ) {
+
+			return url;
+
+		}
+
 		// Relative URL
 		return ( path || '' ) + url;
 
@@ -894,7 +908,6 @@ THREE.GLTF2Loader = ( function () {
 	GLTFParser.prototype.loadShaders = function () {
 
 		var json = this.json;
-		var extensions = this.extensions;
 		var options = this.options;
 
 		return this._withDependencies( [
@@ -905,9 +918,11 @@ THREE.GLTF2Loader = ( function () {
 
 			return _each( json.shaders, function ( shader ) {
 
-				if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) {
+				if ( shader.bufferView !== undefined ) {
 
-					return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews );
+					var bufferView = dependencies.bufferViews[ shader.bufferView ];
+					var array = new Uint8Array( bufferView );
+					return convertUint8ArrayToString( array );
 
 				}
 
@@ -937,13 +952,14 @@ THREE.GLTF2Loader = ( function () {
 
 		return _each( json.buffers, function ( buffer, name ) {
 
-			if ( name === BINARY_EXTENSION_BUFFER_NAME ) {
+			if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) {
 
-				return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body;
+				// If present, GLB container is required to be the first buffer.
+				if ( buffer.uri === undefined && name === 0 ) {
 
-			}
+					return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body;
 
-			if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) {
+				}
 
 				return new Promise( function ( resolve ) {
 
@@ -1011,11 +1027,13 @@ THREE.GLTF2Loader = ( function () {
 				var elementBytes = TypedArray.BYTES_PER_ELEMENT;
 				var itemBytes = elementBytes * itemSize;
 
+				var array;
+
 				// The buffer is not interleaved if the stride is the item size in bytes.
 				if ( accessor.byteStride && accessor.byteStride !== itemBytes ) {
 
 					// Use the full buffer if it's interleaved.
-					var array = new TypedArray( arraybuffer );
+					array = new TypedArray( arraybuffer );
 
 					// Integer parameters to IB/IBA are in array elements, not bytes.
 					var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes );
@@ -1039,7 +1057,6 @@ THREE.GLTF2Loader = ( function () {
 	GLTFParser.prototype.loadTextures = function () {
 
 		var json = this.json;
-		var extensions = this.extensions;
 		var options = this.options;
 
 		return this._withDependencies( [
@@ -1057,9 +1074,14 @@ THREE.GLTF2Loader = ( function () {
 						var source = json.images[ texture.source ];
 						var sourceUri = source.uri;
 
-						if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) {
+						var urlCreator;
+
+						if ( source.bufferView !== undefined ) {
 
-							sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews );
+							var bufferView = dependencies.bufferViews[ source.bufferView ];
+							var blob = new Blob( [ bufferView ], { type: source.mimeType } );
+							urlCreator = window.URL || window.webkitURL;
+							sourceUri = urlCreator.createObjectURL( blob );
 
 						}
 
@@ -1075,6 +1097,12 @@ THREE.GLTF2Loader = ( function () {
 
 						textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) {
 
+							if ( urlCreator !== undefined ) {
+
+								urlCreator.revokeObjectURL( sourceUri );
+
+							}
+
 							_texture.flipY = false;
 
 							if ( texture.name !== undefined ) _texture.name = texture.name;