|
@@ -24,9 +24,12 @@ THREE.GLTFLoader = ( function () {
|
|
|
var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url );
|
|
|
|
|
|
var loader = new THREE.FileLoader( scope.manager );
|
|
|
- loader.load( url, function ( text ) {
|
|
|
|
|
|
- scope.parse( JSON.parse( text ), onLoad, path );
|
|
|
+ loader.setResponseType( 'arraybuffer' );
|
|
|
+
|
|
|
+ loader.load( url, function ( data ) {
|
|
|
+
|
|
|
+ scope.parse( data , onLoad, path );
|
|
|
|
|
|
}, onProgress, onError );
|
|
|
|
|
@@ -44,11 +47,37 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
},
|
|
|
|
|
|
- parse: function ( json, callback, path ) {
|
|
|
+ parse: function ( data, callback, path ) {
|
|
|
+
|
|
|
+ var json;
|
|
|
+
|
|
|
+ var extensions = {};
|
|
|
+
|
|
|
+ var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) );
|
|
|
+
|
|
|
+ if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) {
|
|
|
+
|
|
|
+ extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
|
|
|
+
|
|
|
+ json = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ json = convertUint8ArrayToString( new Uint8Array( data ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ json = JSON.parse( json );
|
|
|
+
|
|
|
+ if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) {
|
|
|
+
|
|
|
+ extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
console.time( 'GLTFLoader' );
|
|
|
|
|
|
- var parser = new GLTFParser( json, {
|
|
|
+ var parser = new GLTFParser( json, extensions, {
|
|
|
|
|
|
path: path || this.path,
|
|
|
crossOrigin: this.crossOrigin
|
|
@@ -255,6 +284,126 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
+ /*********************************/
|
|
|
+ /********** EXTENSIONS ***********/
|
|
|
+ /*********************************/
|
|
|
+
|
|
|
+ var EXTENSIONS = {
|
|
|
+ KHR_BINARY_GLTF: 'KHR_binary_glTF',
|
|
|
+ KHR_MATERIALS_COMMON: 'KHR_materials_common'
|
|
|
+ };
|
|
|
+
|
|
|
+ /* MATERIALS COMMON EXTENSION */
|
|
|
+
|
|
|
+ function GLTFMaterialsCommonExtension ( json ) {
|
|
|
+
|
|
|
+ this.name = EXTENSIONS.KHR_MATERIALS_COMMON;
|
|
|
+
|
|
|
+ this.lights = {};
|
|
|
+
|
|
|
+ var lights = json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights;
|
|
|
+
|
|
|
+ for ( var lightId in lights ) {
|
|
|
+
|
|
|
+ var light = lights[ lightId ];
|
|
|
+ var lightNode;
|
|
|
+
|
|
|
+ var lightParams = light[ light.type ];
|
|
|
+ var color = new THREE.Color().fromArray( lightParams.color );
|
|
|
+
|
|
|
+ switch ( light.type ) {
|
|
|
+
|
|
|
+ case "directional":
|
|
|
+ lightNode = new THREE.DirectionalLight( color );
|
|
|
+ lightNode.position.set( 0, 0, 1 );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "point":
|
|
|
+ lightNode = new THREE.PointLight( color );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "spot":
|
|
|
+ lightNode = new THREE.SpotLight( color );
|
|
|
+ lightNode.position.set( 0, 0, 1 );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "ambient":
|
|
|
+ lightNode = new THREE.AmbientLight( color );
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( lightNode ) {
|
|
|
+
|
|
|
+ this.lights[ lightId ] = lightNode;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ function GLTFBinaryExtension ( data ) {
|
|
|
+
|
|
|
+ this.name = EXTENSIONS.KHR_BINARY_GLTF;
|
|
|
+
|
|
|
+ var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH );
|
|
|
+
|
|
|
+ var 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 )
|
|
|
+ };
|
|
|
+
|
|
|
+ for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) {
|
|
|
+
|
|
|
+ var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ];
|
|
|
+
|
|
|
+ if ( header[ key ] !== value ) {
|
|
|
+
|
|
|
+ throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength );
|
|
|
+
|
|
|
+ this.header = header;
|
|
|
+ this.content = convertUint8ArrayToString( contentArray );
|
|
|
+ this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) {
|
|
|
+
|
|
|
+ var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ];
|
|
|
+ var array = new Uint8Array( bufferView );
|
|
|
+
|
|
|
+ return convertUint8ArrayToString( array );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) {
|
|
|
+
|
|
|
+ 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 );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
/*********************************/
|
|
|
/********** INTERNALS ************/
|
|
|
/*********************************/
|
|
@@ -471,6 +620,20 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Avoid the String.fromCharCode.apply(null, array) shortcut, which
|
|
|
+ // throws a "maximum call stack size exceeded" error for large arrays.
|
|
|
+ function convertUint8ArrayToString ( array ) {
|
|
|
+ var s = '';
|
|
|
+
|
|
|
+ for ( var i = 0; i < array.length; i++ ) {
|
|
|
+
|
|
|
+ s += String.fromCharCode( array[ i ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return s;
|
|
|
+ }
|
|
|
+
|
|
|
// Three.js seems too dependent on attribute names so globally
|
|
|
// replace those in the shader code
|
|
|
function replaceTHREEShaderAttributes( shaderText, technique ) {
|
|
@@ -614,9 +777,10 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
/* GLTF PARSER */
|
|
|
|
|
|
- function GLTFParser( json, options ) {
|
|
|
+ function GLTFParser( json, extensions, options ) {
|
|
|
|
|
|
this.json = json || {};
|
|
|
+ this.extensions = extensions || {};
|
|
|
this.options = options || {};
|
|
|
|
|
|
// loader object cache
|
|
@@ -710,17 +874,32 @@ THREE.GLTFLoader = ( function () {
|
|
|
GLTFParser.prototype.loadShaders = function () {
|
|
|
|
|
|
var json = this.json;
|
|
|
+ var extensions = this.extensions;
|
|
|
var options = this.options;
|
|
|
|
|
|
- return _each( json.shaders, function ( shader ) {
|
|
|
+ return this._withDependencies( [
|
|
|
+
|
|
|
+ "bufferViews"
|
|
|
+
|
|
|
+ ] ).then( function ( dependencies ) {
|
|
|
+
|
|
|
+ return _each( json.shaders, function ( shader ) {
|
|
|
+
|
|
|
+ if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) {
|
|
|
+
|
|
|
+ return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return new Promise( function ( resolve ) {
|
|
|
|
|
|
- return new Promise( function ( resolve ) {
|
|
|
+ var loader = new THREE.FileLoader();
|
|
|
+ loader.setResponseType( 'text' );
|
|
|
+ loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) {
|
|
|
|
|
|
- var loader = new THREE.FileLoader();
|
|
|
- loader.setResponseType( 'text' );
|
|
|
- loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) {
|
|
|
+ resolve( shaderText );
|
|
|
|
|
|
- resolve( shaderText );
|
|
|
+ } );
|
|
|
|
|
|
} );
|
|
|
|
|
@@ -733,9 +912,16 @@ THREE.GLTFLoader = ( function () {
|
|
|
GLTFParser.prototype.loadBuffers = function () {
|
|
|
|
|
|
var json = this.json;
|
|
|
+ var extensions = this.extensions;
|
|
|
var options = this.options;
|
|
|
|
|
|
- return _each( json.buffers, function ( buffer ) {
|
|
|
+ return _each( json.buffers, function ( buffer, name ) {
|
|
|
+
|
|
|
+ if ( name === BINARY_EXTENSION_BUFFER_NAME ) {
|
|
|
+
|
|
|
+ return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) {
|
|
|
|
|
@@ -831,52 +1017,68 @@ THREE.GLTFLoader = ( function () {
|
|
|
GLTFParser.prototype.loadTextures = function () {
|
|
|
|
|
|
var json = this.json;
|
|
|
+ var extensions = this.extensions;
|
|
|
var options = this.options;
|
|
|
|
|
|
- return _each( json.textures, function ( texture ) {
|
|
|
-
|
|
|
- if ( texture.source ) {
|
|
|
+ return this._withDependencies( [
|
|
|
|
|
|
- return new Promise( function ( resolve ) {
|
|
|
+ "bufferViews"
|
|
|
|
|
|
- var source = json.images[ texture.source ];
|
|
|
+ ] ).then( function ( dependencies ) {
|
|
|
|
|
|
- var textureLoader = THREE.Loader.Handlers.get( source.uri );
|
|
|
+ return _each( json.textures, function ( texture ) {
|
|
|
|
|
|
- if ( textureLoader === null ) {
|
|
|
+ if ( texture.source ) {
|
|
|
|
|
|
- textureLoader = new THREE.TextureLoader();
|
|
|
+ return new Promise( function ( resolve ) {
|
|
|
|
|
|
- }
|
|
|
+ var source = json.images[ texture.source ];
|
|
|
+ var sourceUri = source.uri;
|
|
|
|
|
|
- textureLoader.setCrossOrigin( options.crossOrigin );
|
|
|
+ if (source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]) {
|
|
|
|
|
|
- textureLoader.load( resolveURL( source.uri, options.path ), function ( _texture ) {
|
|
|
+ sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews );
|
|
|
|
|
|
- _texture.flipY = false;
|
|
|
+ }
|
|
|
|
|
|
- if ( texture.sampler ) {
|
|
|
+ var textureLoader = THREE.Loader.Handlers.get( sourceUri );
|
|
|
|
|
|
- var sampler = json.samplers[ texture.sampler ];
|
|
|
+ if ( textureLoader === null ) {
|
|
|
|
|
|
- _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ];
|
|
|
- _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ];
|
|
|
- _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ];
|
|
|
- _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ];
|
|
|
+ textureLoader = new THREE.TextureLoader();
|
|
|
|
|
|
}
|
|
|
|
|
|
- resolve( _texture );
|
|
|
+ textureLoader.setCrossOrigin( options.crossOrigin );
|
|
|
+
|
|
|
+ textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) {
|
|
|
+
|
|
|
+ _texture.flipY = false;
|
|
|
+
|
|
|
+ if ( texture.sampler ) {
|
|
|
+
|
|
|
+ var sampler = json.samplers[ texture.sampler ];
|
|
|
+
|
|
|
+ _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ];
|
|
|
+ _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ];
|
|
|
+ _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ];
|
|
|
+ _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ resolve( _texture );
|
|
|
+
|
|
|
+ }, undefined, function () {
|
|
|
|
|
|
- }, undefined, function () {
|
|
|
+ resolve();
|
|
|
|
|
|
- resolve();
|
|
|
+ } );
|
|
|
|
|
|
} );
|
|
|
|
|
|
- } );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ } );
|
|
|
|
|
|
} );
|
|
|
|
|
@@ -901,13 +1103,13 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
var khr_material;
|
|
|
|
|
|
- if ( material.extensions && material.extensions.KHR_materials_common ) {
|
|
|
+ if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) {
|
|
|
|
|
|
- khr_material = material.extensions.KHR_materials_common;
|
|
|
+ khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ];
|
|
|
|
|
|
- } else if ( json.extensions && json.extensions.KHR_materials_common ) {
|
|
|
+ } else if ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) {
|
|
|
|
|
|
- khr_material = json.extensions.KHR_materials_common;
|
|
|
+ khr_material = json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ];
|
|
|
|
|
|
}
|
|
|
|
|
@@ -1413,7 +1615,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
case 'POSITION':
|
|
|
geometry.addAttribute( 'position', bufferAttribute );
|
|
|
break;
|
|
|
-
|
|
|
+
|
|
|
case 'COLOR_0':
|
|
|
case 'COLOR0':
|
|
|
case 'COLOR':
|
|
@@ -1599,6 +1801,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
GLTFParser.prototype.loadNodes = function () {
|
|
|
|
|
|
var json = this.json;
|
|
|
+ var extensions = this.extensions;
|
|
|
var scope = this;
|
|
|
|
|
|
return _each( json.nodes, function ( node ) {
|
|
@@ -1657,8 +1860,7 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
"meshes",
|
|
|
"skins",
|
|
|
- "cameras",
|
|
|
- "extensions"
|
|
|
+ "cameras"
|
|
|
|
|
|
] ).then( function ( dependencies ) {
|
|
|
|
|
@@ -1827,9 +2029,12 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( node.extensions && node.extensions.KHR_materials_common && node.extensions.KHR_materials_common.light ) {
|
|
|
+ if ( node.extensions
|
|
|
+ && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]
|
|
|
+ && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) {
|
|
|
|
|
|
- var light = dependencies.extensions.KHR_materials_common.lights[ node.extensions.KHR_materials_common.light ];
|
|
|
+ var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights;
|
|
|
+ var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ];
|
|
|
|
|
|
_node.add( light );
|
|
|
|
|
@@ -1845,70 +2050,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
- GLTFParser.prototype.loadExtensions = function () {
|
|
|
-
|
|
|
- var json = this.json;
|
|
|
-
|
|
|
- return _each( json.extensions, function ( extension, extensionId ) {
|
|
|
-
|
|
|
- switch ( extensionId ) {
|
|
|
-
|
|
|
- case "KHR_materials_common":
|
|
|
-
|
|
|
- var extensionNode = {
|
|
|
- lights: {}
|
|
|
- };
|
|
|
-
|
|
|
- var lights = extension.lights;
|
|
|
-
|
|
|
- for ( var lightId in lights ) {
|
|
|
-
|
|
|
- var light = lights[ lightId ];
|
|
|
- var lightNode;
|
|
|
-
|
|
|
- var lightParams = light[ light.type ];
|
|
|
- var color = new THREE.Color().fromArray( lightParams.color );
|
|
|
-
|
|
|
- switch ( light.type ) {
|
|
|
-
|
|
|
- case "directional":
|
|
|
- lightNode = new THREE.DirectionalLight( color );
|
|
|
- lightNode.position.set( 0, 0, 1 );
|
|
|
- break;
|
|
|
-
|
|
|
- case "point":
|
|
|
- lightNode = new THREE.PointLight( color );
|
|
|
- break;
|
|
|
-
|
|
|
- case "spot":
|
|
|
- lightNode = new THREE.SpotLight( color );
|
|
|
- lightNode.position.set( 0, 0, 1 );
|
|
|
- break;
|
|
|
-
|
|
|
- case "ambient":
|
|
|
- lightNode = new THREE.AmbientLight( color );
|
|
|
- break;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( lightNode ) {
|
|
|
-
|
|
|
- extensionNode.lights[ lightId ] = lightNode;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return extensionNode;
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
GLTFParser.prototype.loadScenes = function () {
|
|
|
|
|
|
var json = this.json;
|