+ A loader for loading a .gltf resource in JSON format.
+
+ The glTF file format is a JSON file format to enable rapid delivery and loading of 3D content.
+
+
+
+
Constructor
+
+
[name]( [page:LoadingManager manager] )
+
+ [page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
+
+ [page:String url] — required
+ [page:Function onLoad] — Will be called when load completes. The argument will be the loaded JSON response returned from [page:Function parse].
+ [page:Function onProgress] — Will be called while load progresses. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.
+ [page:Function onError] — Will be called when load errors.
+
+
+ Begin loading from url and call the callback function with the parsed response content.
+
+
+
[method:null setPath]( [page:String path] )
+
+ [page:String path] — Base path for loading additional resources e.g. textures, GLSL shaders, .bin data.
+
+ [page:Object json] — JSON object to parse.
+ [page:Function callBack] — Will be called when parse completes.
+ [page:String path] — The base path from which to find subsequent glTF resources such as textures, GLSL shaders and .bin data files.
+
+
+ Parse a glTF-based JSON structure and fire [page:Function callback] when complete. The argument to [page:Function callback] will be an [page:object] that contains loaded parts: .[page:Scene scene], .[page:Array cameras], .[page:Array animations] and .[page:Array shaders]
+
+
+
+
Notes
+
+
+ When using custom shaders provided within a glTF file [page:THREE.GLTFLoader.Shaders] should be updated on each render loop. See [example:webgl_loader_gltf] demo source code for example usage.
+
+
+
+ This class is often used with [page:THREE.GLTFLoader.Animations THREE.GLTFLoader.Animations] to animate parsed animations. See [example:webgl_loader_gltf] demo source code for example usage.
+
+
+
Example
+
+
+ // instantiate a loader
+ var loader = new THREE.GLTFLoader();
+
+ // load a glTF resource
+ loader.load(
+ // resource URL
+ 'models/gltf/duck/duck.json',
+ // Function when resource is loaded
+ function ( object ) {
+ scene.add( object.scene );
+ }
+ );
+
+
+ [example:webgl_loader_gltf]
+
+
+
- [page:String url] — required
- [page:Function callback] — Will be called when load completes. The argument will be an [page:Object] containing the loaded .[page:Object3D scene], .[page:Array cameras] and .[page:Array animations].
-
-
- Begin loading from url and call the callback function with the parsed response content.
-
-
-
-
Notes
-
-
- This class is often used with [page:glTFAnimator THREE.glTFAnimator] to animate parsed animations.
-
-
-
-
Example
-
-
- // instantiate a loader
- var loader = new THREE.glTFLoader();
-
- // load a glTF resource
- loader.load(
- // resource URL
- 'models/gltf/duck/duck.json',
- // Function when resource is loaded
- function ( object ) {
- scene.add( object.scene );
- }
- );
-
-
- [example:webgl_loader_gltf]
-
-
-
Source
-
- [link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/gltf/glTFLoader.js examples/js/loaders/gltf/glTFLoader.js]
-
-
diff --git a/docs/list.js b/docs/list.js
index 2e5c06142e9207..8092a33c461a2b 100644
--- a/docs/list.js
+++ b/docs/list.js
@@ -93,7 +93,7 @@ var list = {
[ "BufferGeometryLoader", "api/loaders/BufferGeometryLoader" ],
[ "Cache", "api/loaders/Cache" ],
[ "ColladaLoader", "api/loaders/ColladaLoader" ],
- [ "glTFLoader", "api/loaders/glTFLoader" ],
+ [ "GLTFLoader", "api/loaders/GLTFLoader" ],
[ "ImageLoader", "api/loaders/ImageLoader" ],
[ "JSONLoader", "api/loaders/JSONLoader" ],
[ "Loader", "api/loaders/Loader" ],
diff --git a/editor/js/libs/tern-threejs/threejs.js b/editor/js/libs/tern-threejs/threejs.js
index b4cc967be2caa4..d0268ebb5a72e3 100644
--- a/editor/js/libs/tern-threejs/threejs.js
+++ b/editor/js/libs/tern-threejs/threejs.js
@@ -2544,8 +2544,8 @@
"!doc": "A low level class for loading resources with XmlHttpRequest, used internaly by most loaders.",
"!type": "fn(manager: +THREE.LoadingManager)"
},
- "glTFLoader": {
- "!url": "http://threejs.org/docs/#Reference/loaders/glTFLoader",
+ "GLTFLoader": {
+ "!url": "http://threejs.org/docs/#Reference/loaders/GLTFLoader",
"prototype": {
"!proto": "THREE.Loader.prototype",
"load": {
diff --git a/examples/js/loaders/GLTFLoader.js b/examples/js/loaders/GLTFLoader.js
index d2bcfa5489506a..715695beaa66a7 100644
--- a/examples/js/loaders/GLTFLoader.js
+++ b/examples/js/loaders/GLTFLoader.js
@@ -1,379 +1,1739 @@
/**
+ * @author Rich Tibbett / https://github.com/richtr
* @author mrdoob / http://mrdoob.com/
+ * @author Tony Parisi / http://www.tonyparisi.com/
*/
-THREE.GLTFLoader = function ( manager ) {
+(function() {
+
+THREE.GLTFLoader = function( manager ) {
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+ this.parser = GLTFParser;
+
};
THREE.GLTFLoader.prototype = {
constructor: THREE.GLTFLoader,
- load: function ( url, onLoad, onProgress, onError ) {
+ load: function( url, onLoad, onProgress, onError ) {
var scope = this;
+ var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url );
+
var loader = new THREE.XHRLoader( scope.manager );
- loader.load( url, function ( text ) {
+ loader.load( url, function( text ) {
- onLoad( scope.parse( JSON.parse( text ) ) );
+ scope.parse( JSON.parse( text ), onLoad, path );
}, onProgress, onError );
},
- setCrossOrigin: function ( value ) {
+ setCrossOrigin: function( value ) {
this.crossOrigin = value;
},
- parse: function ( json ) {
+ setPath: function( value ) {
- function stringToArrayBuffer( string ) {
+ this.path = value;
- var bytes = atob( string );
- var buffer = new ArrayBuffer( bytes.length );
- var bufferView = new Uint8Array( buffer );
+ },
- for ( var i = 0; i < bytes.length; i ++ ) {
+ parse: function( json, callback, path ) {
- bufferView[ i ] = bytes.charCodeAt( i );
+ console.time( 'GLTFLoader' );
- }
+ var glTFParser = new this.parser( json, {
+ path: path || this.path,
+ crossOrigin: !!this.crossOrigin
+ });
- return buffer;
+ glTFParser.parse( function( scene, cameras, animations ) {
- }
+ console.timeEnd( 'GLTFLoader' );
- console.time( 'GLTFLoader' );
+ var glTF = {
+ "scene": scene,
+ "cameras": cameras,
+ "animations": animations
+ };
+
+ callback( glTF );
+
+ });
+
+ // Developers should use `callback` argument for async notification on
+ // completion to prevent side effects.
+ // Function return is kept only for backward-compatability purposes.
+ return {
+ get scene() {
+
+ console.warn( "Synchronous glTF object access is deprecated." +
+ " Use the asynchronous 'callback' argument instead." );
+ return scene;
+
+ },
+ set scene( value ) {
+
+ console.warn( "Synchronous glTF object access is deprecated." +
+ " Use the asynchronous 'callback' argument instead." );
+ scene = value;
+
+ }
- var library = {
- buffers: {},
- bufferViews: {},
- accessors: {},
- textures: {},
- materials: {},
- meshes: {},
- nodes: {},
- scenes: {}
};
- // buffers
+ }
+
+};
+
+/* GLTFREGISTRY */
+
+var GLTFRegistry = function() {
+
+ var objects = {};
+
+ return {
+ get : function( key ) {
+
+ return objects[ key ];
+
+ },
+
+ add : function( key, object ) {
+
+ objects[ key ] = object;
+
+ },
+
+ remove: function( key ) {
+
+ delete objects[ key ];
+
+ },
- var buffers = json.buffers;
+ removeAll: function() {
- for ( var bufferId in buffers ) {
+ objects = {};
- var buffer = buffers[ bufferId ];
+ },
- if ( buffer.type === 'arraybuffer' ) {
+ update : function( scene, camera ) {
- var header = 'data:application/octet-stream;base64,';
+ _each( objects, function( object ) {
- if ( buffer.uri.indexOf( header ) === 0 ) {
+ if ( object.update ) {
- library.buffers[ bufferId ] = stringToArrayBuffer( buffer.uri.substr( header.length ) );
+ object.update( scene, camera );
}
- }
+ });
}
+ };
+};
+
+/* GLTFSHADERS */
+
+THREE.GLTFLoader.Shaders = new GLTFRegistry();
- // buffer views
+/* GLTFSHADER */
- var bufferViews = json.bufferViews;
+var GLTFShader = function( targetNode, allNodes ) {
- for ( var bufferViewId in bufferViews ) {
+ this.boundUniforms = {};
- var bufferView = bufferViews[ bufferViewId ];
- var arraybuffer = library.buffers[ bufferView.buffer ];
+ // bind each uniform to its source node
+ _each(targetNode.material.uniforms, function(uniform, uniformId) {
+
+ if (uniform.semantic) {
+
+ var sourceNodeRef = uniform.node;
+
+ var sourceNode = targetNode;
+ if ( sourceNodeRef ) {
+ sourceNode = allNodes[ sourceNodeRef ];
+ }
- library.bufferViews[ bufferViewId ] = arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength );
+ this.boundUniforms[ uniformId ] = {
+ semantic: uniform.semantic,
+ sourceNode: sourceNode,
+ targetNode: targetNode,
+ uniform: uniform
+ };
}
- // accessors
+ }.bind( this ));
- var COMPONENT_TYPES = {
- 5120: Int8Array,
- 5121: Uint8Array,
- 5122: Int16Array,
- 5123: Uint16Array,
- 5125: Uint32Array,
- 5126: Float32Array,
- };
+ this._m4 = new THREE.Matrix4();
- var TYPE_SIZES = {
- 'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, 'VEC4': 4,
- 'MAT2': 4, 'MAT3': 9, 'MAT4': 16
- };
+}
- var accessors = json.accessors;
+// Update - update all the uniform values
+GLTFShader.prototype.update = function( scene, camera ) {
- for ( var accessorId in accessors ) {
+ // update scene graph
- var accessor = accessors[ accessorId ];
+ scene.updateMatrixWorld();
- var arraybuffer = library.bufferViews[ accessor.bufferView ];
- var itemSize = TYPE_SIZES[ accessor.type ];
- var TypedArray = COMPONENT_TYPES[ accessor.componentType ];
+ // update camera matrices and frustum
- var array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize );
+ camera.updateMatrixWorld();
+ camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+ _each( this.boundUniforms, function( boundUniform ) {
+
+ switch (boundUniform.semantic) {
+
+ case "MODELVIEW":
+
+ var m4 = boundUniform.uniform.value;
+ m4.multiplyMatrices(camera.matrixWorldInverse,
+ boundUniform.sourceNode.matrixWorld);
+ break;
+
+ case "MODELVIEWINVERSETRANSPOSE":
- library.accessors[ accessorId ] = new THREE.BufferAttribute( array, itemSize );
+ var m3 = boundUniform.uniform.value;
+ this._m4.multiplyMatrices(camera.matrixWorldInverse,
+ boundUniform.sourceNode.matrixWorld);
+ m3.getNormalMatrix(this._m4);
+ break;
+
+ case "PROJECTION":
+
+ var m4 = boundUniform.uniform.value;
+ m4.copy(camera.projectionMatrix);
+ break;
+
+ case "JOINTMATRIX":
+
+ var m4v = boundUniform.uniform.value;
+ for (var mi = 0; mi < m4v.length; mi++) {
+ // So it goes like this:
+ // SkinnedMesh world matrix is already baked into MODELVIEW;
+ // ransform joints to local space,
+ // then transform using joint's inverse
+ m4v[mi]
+ .getInverse(boundUniform.sourceNode.matrixWorld)
+ .multiply(boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld)
+ .multiply(boundUniform.targetNode.skeleton.boneInverses[mi]);
+ }
+ break;
+
+ default :
+
+ console.warn("Unhandled shader semantic: " + boundUniform.semantic);
+ break;
}
- // textures
+ }.bind( this ));
- var FILTERS = {
- 9728: THREE.NearestFilter,
- 9729: THREE.LinearFilter,
- 9984: THREE.NearestMipMapNearestFilter,
- 9985: THREE.LinearMipMapNearestFilter,
- 9986: THREE.NearestMipMapLinearFilter,
- 9987: THREE.LinearMipMapLinearFilter
- };
+};
- var WRAPPINGS = {
- 33071: THREE.ClampToEdgeWrapping,
- 33648: THREE.MirroredRepeatWrapping,
- 10497: THREE.RepeatWrapping
- };
- var textures = json.textures;
+/* GLTFANIMATION */
- for ( var textureId in textures ) {
+THREE.GLTFLoader.Animations = new GLTFRegistry();
- var texture = textures[ textureId ];
+// Construction/initialization
+var GLTFAnimation = function( interps ) {
- var _texture = new THREE.Texture();
- _texture.flipY = false;
+ this.running = false;
+ this.loop = false;
+ this.duration = 0;
+ this.startTime = 0;
+ this.interps = [];
- if ( texture.source ) {
+ this.uuid = THREE.Math.generateUUID();
- var source = json.images[ texture.source ];
+ if ( interps ) {
- _texture.image = new Image();
- _texture.image.src = source.uri;
- _texture.needsUpdate = true;
+ this.createInterpolators( interps );
- }
+ }
- if ( texture.sampler ) {
+};
- var sampler = json.samplers[ texture.sampler ];
+GLTFAnimation.prototype.createInterpolators = function( interps ) {
- _texture.magFilter = FILTERS[ sampler.magFilter ];
- _texture.minFilter = FILTERS[ sampler.minFilter ];
- _texture.wrapS = WRAPPINGS[ sampler.wrapS ];
- _texture.wrapT = WRAPPINGS[ sampler.wrapT ];
+ for ( var i = 0, len = interps.length; i < len; i ++ ) {
- }
+ var interp = new GLTFInterpolator( interps[ i ] );
+ this.interps.push( interp );
+ this.duration = Math.max( this.duration, interp.duration );
- library.textures[ textureId ] = _texture;
+ }
- }
+}
- // materials
+// Start/stop
+GLTFAnimation.prototype.play = function() {
- var materials = json.materials;
+ if ( this.running )
+ return;
- for ( var materialId in materials ) {
+ this.startTime = Date.now();
+ this.running = true;
+ THREE.GLTFLoader.Animations.add( this.uuid, this );
- var material = materials[ materialId ];
+};
- var _material = new THREE.MeshPhongMaterial();
- _material.name = material.name;
+GLTFAnimation.prototype.stop = function() {
- var values = material.values;
+ this.running = false;
+ THREE.GLTFLoader.Animations.remove( this.uuid );
- if ( Array.isArray( values.diffuse ) ) {
+};
- _material.color.fromArray( values.diffuse );
+// Update - drive key frame evaluation
+GLTFAnimation.prototype.update = function() {
- } else if ( typeof( values.diffuse ) === 'string' ) {
+ if ( !this.running )
+ return;
- _material.map = library.textures[ values.diffuse ];
+ var now = Date.now();
+ var deltat = ( now - this.startTime ) / 1000;
+ var t = deltat % this.duration;
+ var nCycles = Math.floor( deltat / this.duration );
- }
+ if ( nCycles >= 1 && ! this.loop ) {
- if ( typeof( values.bump ) === 'string' ) {
+ this.running = false;
+ _each( this.interps, function( _, i ) {
- _material.bumpMap = library.textures[ values.bump ];
+ this.interps[ i ].interp( this.duration );
- }
+ }.bind( this ));
+ this.stop();
+ return;
- if ( Array.isArray( values.emission ) ) _material.emissive.fromArray( values.emission );
- if ( Array.isArray( values.specular ) ) _material.specular.fromArray( values.specular );
+ } else {
- if ( values.shininess !== undefined ) _material.shininess = values.shininess;
+ _each( this.interps, function( _, i ) {
+ this.interps[ i ].interp( t );
- library.materials[ materialId ] = _material;
+ }.bind( this ));
- }
+ }
- // meshes
+};
- var meshes = json.meshes;
+/* GLTFINTERPOLATOR */
- for ( var meshId in meshes ) {
+var GLTFInterpolator = function( param ) {
- var mesh = meshes[ meshId ];
+ this.keys = param.keys;
+ this.values = param.values;
+ this.count = param.count;
+ this.type = param.type;
+ this.path = param.path;
+ this.isRot = false;
- var group = new THREE.Group();
- group.name = mesh.name;
+ var node = param.target;
+ node.updateMatrix();
+ node.matrixAutoUpdate = true;
+ this.targetNode = node;
- var primitives = mesh.primitives;
+ switch ( param.path ) {
- for ( var i = 0; i < primitives.length; i ++ ) {
+ case "translation" :
- var primitive = primitives[ i ];
- var attributes = primitive.attributes;
+ this.target = node.position;
+ this.originalValue = node.position.clone();
+ break;
- var geometry = new THREE.BufferGeometry();
+ case "rotation" :
- if ( primitive.indices ) {
+ this.target = node.quaternion;
+ this.originalValue = node.quaternion.clone();
+ this.isRot = true;
+ break;
- geometry.setIndex( library.accessors[ primitive.indices ] );
+ case "scale" :
- }
+ this.target = node.scale;
+ this.originalValue = node.scale.clone();
+ break;
- for ( var attributeId in attributes ) {
+ }
- var attribute = attributes[ attributeId ];
- var bufferAttribute = library.accessors[ attribute ];
+ this.duration = this.keys[ this.count - 1 ];
- switch ( attributeId ) {
+ this.vec1 = new THREE.Vector3();
+ this.vec2 = new THREE.Vector3();
+ this.vec3 = new THREE.Vector3();
+ this.quat1 = new THREE.Quaternion();
+ this.quat2 = new THREE.Quaternion();
+ this.quat3 = new THREE.Quaternion();
- case 'POSITION':
- geometry.addAttribute( 'position', bufferAttribute );
- break;
+};
- case 'NORMAL':
- geometry.addAttribute( 'normal', bufferAttribute );
- break;
+//Interpolation and tweening methods
+GLTFInterpolator.prototype.interp = function( t ) {
- case 'TEXCOORD_0':
- geometry.addAttribute( 'uv', bufferAttribute );
- break;
+ if ( t == this.keys[ 0 ] ) {
- }
+ if ( this.isRot ) {
- }
+ this.quat3.fromArray( this.values );
- var material = library.materials[ primitive.material ];
+ } else {
- group.add( new THREE.Mesh( geometry, material ) );
+ this.vec3.fromArray( this.values );
- }
+ }
+
+ } else if ( t < this.keys[ 0 ] ) {
+
+ if ( this.isRot ) {
+
+ this.quat1.copy( this.originalValue );
+ this.quat2.fromArray( this.values );
+ THREE.Quaternion.slerp( this.quat1, this.quat2, this.quat3, t / this.keys[ 0 ] );
- library.meshes[ meshId ] = group;
+ } else {
+
+ this.vec3.copy( this.originalValue );
+ this.vec2.fromArray( this.values );
+ this.vec3.lerp( this.vec2, t / this.keys[ 0 ] );
}
- // nodes
+ } else if ( t >= this.keys[ this.count - 1 ] ) {
- var nodes = json.nodes;
- var matrix = new THREE.Matrix4();
+ if ( this.isRot ) {
- for ( var nodeId in nodes ) {
+ this.quat3.fromArray( this.values, ( this.count - 1 ) * 4 );
- var node = nodes[ nodeId ];
+ } else {
- var object = new THREE.Group();
- object.name = node.name;
+ this.vec3.fromArray( this.values, ( this.count - 1 ) * 3 );
- if ( node.translation !== undefined ) {
+ }
- object.position.fromArray( node.translation );
+ } else {
- }
+ for ( var i = 0; i < this.count - 1; i ++ ) {
- if ( node.rotation !== undefined ) {
+ var key1 = this.keys[ i ];
+ var key2 = this.keys[ i + 1 ];
- object.quaternion.fromArray( node.rotation );
+ if ( t >= key1 && t <= key2 ) {
- }
+ if ( this.isRot ) {
- if ( node.scale !== undefined ) {
+ this.quat1.fromArray( this.values, i * 4 );
+ this.quat2.fromArray( this.values, ( i + 1 ) * 4 );
+ THREE.Quaternion.slerp( this.quat1, this.quat2, this.quat3, ( t - key1 ) / ( key2 - key1 ) );
- object.scale.fromArray( node.scale );
+ } else {
+
+ this.vec3.fromArray( this.values, i * 3 );
+ this.vec2.fromArray( this.values, ( i + 1 ) * 3 );
+ this.vec3.lerp( this.vec2, ( t - key1 ) / ( key2 - key1 ) );
+
+ }
}
- if ( node.matrix !== undefined ) {
+ }
+
+ }
+
+ if ( this.target ) {
- matrix.fromArray( node.matrix );
- matrix.decompose( object.position, object.quaternion, object.scale );
+ if ( this.isRot ) {
- }
+ this.target.copy( this.quat3 );
+
+ } else {
+
+ this.target.copy( this.vec3 );
+
+ }
+
+ }
+
+};
+
+
+/*********************************/
+/********** INTERNALS ************/
+/*********************************/
+
+/* CONSTANTS */
+
+var WEBGL_CONSTANTS = {
+ FLOAT: 5126,
+ //FLOAT_MAT2: 35674,
+ FLOAT_MAT3: 35675,
+ FLOAT_MAT4: 35676,
+ FLOAT_VEC2: 35664,
+ FLOAT_VEC3: 35665,
+ FLOAT_VEC4: 35666,
+ LINEAR: 9729,
+ REPEAT: 10497,
+ SAMPLER_2D: 35678,
+ TRIANGLES: 4,
+ UNSIGNED_BYTE: 5121,
+ UNSIGNED_SHORT: 5123,
+
+ VERTEX_SHADER: 35633,
+ FRAGMENT_SHADER: 35632
+};
+
+var WEBGL_TYPE = {
+ 5126: Number,
+ //35674: THREE.Matrix2,
+ 35675: THREE.Matrix3,
+ 35676: THREE.Matrix4,
+ 35664: THREE.Vector2,
+ 35665: THREE.Vector3,
+ 35666: THREE.Vector4,
+ 35678: THREE.Texture
+};
+
+var WEBGL_COMPONENT_TYPES = {
+ 5120: Int8Array,
+ 5121: Uint8Array,
+ 5122: Int16Array,
+ 5123: Uint16Array,
+ 5125: Uint32Array,
+ 5126: Float32Array
+};
+
+var WEBGL_FILTERS = {
+ 9728: THREE.NearestFilter,
+ 9729: THREE.LinearFilter,
+ 9984: THREE.NearestMipMapNearestFilter,
+ 9985: THREE.LinearMipMapNearestFilter,
+ 9986: THREE.NearestMipMapLinearFilter,
+ 9987: THREE.LinearMipMapLinearFilter
+};
+
+var WEBGL_WRAPPINGS = {
+ 33071: THREE.ClampToEdgeWrapping,
+ 33648: THREE.MirroredRepeatWrapping,
+ 10497: THREE.RepeatWrapping
+};
+
+var WEBGL_TYPE_SIZES = {
+ 'SCALAR': 1,
+ 'VEC2': 2,
+ 'VEC3': 3,
+ 'VEC4': 4,
+ 'MAT2': 4,
+ 'MAT3': 9,
+ 'MAT4': 16
+};
+
+/* UTILITY FUNCTIONS */
- if ( node.meshes !== undefined ) {
+var _each = function( object, callback, thisObj ) {
+
+ if ( !object ) {
+ return Promise.resolve();
+ }
- for ( var i = 0; i < node.meshes.length; i ++ ) {
+ var results;
+ var fns = [];
- var meshId = node.meshes[ i ];
- var group = library.meshes[ meshId ];
+ if ( Object.prototype.toString.call( object ) === '[object Array]' ) {
- object.add( group.clone() );
+ results = [];
+ var length = object.length;
+ for ( var idx = 0; idx < length; idx ++ ) {
+ var value = callback.call( thisObj || this, object[ idx ], idx );
+ if ( value ) {
+ fns.push( value );
+ if ( value instanceof Promise ) {
+ value.then( function( key, value ) {
+ results[ idx ] = value;
+ }.bind( this, key ));
+ } else {
+ results[ idx ] = value;
}
+ }
+ }
+ } else {
+
+ results = {};
+
+ for ( var key in object ) {
+ if ( object.hasOwnProperty( key ) ) {
+ var value = callback.call( thisObj || this, object[ key ], key );
+ if ( value ) {
+ fns.push( value );
+ if ( value instanceof Promise ) {
+ value.then( function( key, value ) {
+ results[ key ] = value;
+ }.bind( this, key ));
+ } else {
+ results[ key ] = value;
+ }
+ }
}
+ }
+
+ }
+
+ return Promise.all( fns ).then( function() {
+ return results;
+ });
+
+};
+
+var resolveURL = function( url, path ) {
+
+ // Invalid URL
+ if ( typeof url !== 'string' || url === '' )
+ return '';
+
+ // Absolute URL
+ if ( /^https?:\/\//i.test( url ) ) {
+
+ return url;
+
+ }
+
+ // Data URI
+ if ( /^data:.*,.*$/i.test( url ) ) {
+
+ return url;
+
+ }
- library.nodes[ nodeId ] = object;
+ // Relative URL
+ return (path || '') + url;
+
+};
+
+// Three.js seems too dependent on attribute names so globally
+// replace those in the shader code
+var replaceTHREEShaderAttributes = function( shaderText, technique ) {
+
+ // Expected technique attributes
+ var attributes = {};
+
+ _each( technique.attributes, function( pname, attributeId ) {
+
+ var param = technique.parameters[ pname ];
+ var atype = param.type;
+ var semantic = param.semantic;
+
+ attributes[ attributeId ] = {
+ type : atype,
+ semantic : semantic
+ };
+
+ });
+
+ // Figure out which attributes to change in technique
+
+ var shaderParams = technique.parameters;
+ var shaderAttributes = technique.attributes;
+ var params = {};
+
+ _each( attributes, function( _, attributeId ) {
+
+ var pname = shaderAttributes[ attributeId ];
+ var shaderParam = shaderParams[ pname ];
+ var semantic = shaderParam.semantic;
+ if ( semantic ) {
+
+ params[ attributeId ] = shaderParam;
}
- for ( var nodeId in nodes ) {
+ });
- var node = nodes[ nodeId ];
+ _each( params, function( param, pname ) {
- for ( var i = 0; i < node.children.length; i ++ ) {
+ var semantic = param.semantic;
- var child = node.children[ i ];
+ var regEx = eval( "/" + pname + "/g" );
- library.nodes[ nodeId ].add( library.nodes[ child ] );
+ switch ( semantic ) {
- }
+ case "POSITION":
+
+ shaderText = shaderText.replace( regEx, 'position' );
+ break;
+
+ case "NORMAL":
+
+ shaderText = shaderText.replace( regEx, 'normal' );
+ break;
+
+ case 'TEXCOORD_0':
+ case 'TEXCOORD0':
+ case 'TEXCOORD':
+
+ shaderText = shaderText.replace( regEx, 'uv' );
+ break;
+
+ case "WEIGHT":
+
+ shaderText = shaderText.replace(regEx, 'skinWeight');
+ break;
+
+ case "JOINT":
+
+ shaderText = shaderText.replace(regEx, 'skinIndex');
+ break;
}
- // scenes
+ });
- var scenes = json.scenes;
+ return shaderText;
- for ( var sceneId in scenes ) {
+};
- var scene = scenes[ sceneId ];
- var container = new THREE.Scene();
+// Deferred constructor for RawShaderMaterial types
+var DeferredShaderMaterial = function( params ) {
- for ( var i = 0; i < scene.nodes.length; i ++ ) {
+ this.isDeferredShaderMaterial = true;
- var node = scene.nodes[ i ];
- container.add( library.nodes[ node ] );
+ this.params = params;
- }
+};
+
+DeferredShaderMaterial.prototype.create = function() {
- library.scenes[ sceneId ] = container;
+ var uniforms = THREE.UniformsUtils.clone( this.params.uniforms );
+
+ _each( this.params.uniforms, function( originalUniform, uniformId ) {
+
+ if ( originalUniform.value instanceof THREE.Texture ) {
+
+ uniforms[ uniformId ].value = originalUniform.value;
+ uniforms[ uniformId ].value.needsUpdate = true;
}
- console.timeEnd( 'GLTFLoader' );
+ uniforms[ uniformId ].semantic = originalUniform.semantic;
+ uniforms[ uniformId ].node = originalUniform.node;
- return {
+ });
- scene: library.scenes[ json.scene ]
+ this.params.uniforms = uniforms;
- };
+ return new THREE.RawShaderMaterial( this.params );
+
+};
+
+/* GLTF PARSER */
+
+var GLTFParser = function(json, options) {
+
+ this.json = json || {};
+ this.options = options || {};
+
+ // loader object cache
+ this.cache = new GLTFRegistry();
+
+};
+
+GLTFParser.prototype._withDependencies = function( dependencies ) {
+
+ var _dependencies = {};
+
+ for ( var i = 0; i < dependencies.length; i ++ ) {
+
+ var dependency = dependencies[ i ];
+ var fnName = "load" + dependency.charAt(0).toUpperCase() + dependency.slice(1);
+
+ var cached = this.cache.get( dependency );
+
+ if ( cached !== undefined ) {
+
+ _dependencies[ dependency ] = cached;
+
+ } else if ( this[ fnName ] ) {
+
+ var fn = this[ fnName ]();
+ this.cache.add( dependency, fn );
+
+ _dependencies[ dependency ] = fn;
+
+ }
}
+ return _each( _dependencies, function( dependency, dependencyId ) {
+
+ return dependency;
+
+ });
+
+};
+
+GLTFParser.prototype.parse = function( callback ) {
+
+ // Clear the loader cache
+ this.cache.removeAll();
+
+ // Fire the callback on complete
+ this._withDependencies([
+ "scenes",
+ "cameras",
+ "animations"
+ ]).then(function( dependencies ) {
+
+ var scene = dependencies.scenes[ this.json.scene ];
+
+ var cameras = [];
+ _each( dependencies.cameras, function( camera ) {
+
+ cameras.push( camera );
+
+ });
+
+ var animations = [];
+ _each( dependencies.animations, function( animation ) {
+
+ animations.push( animation );
+
+ });
+
+ callback( scene, cameras, animations );
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadShaders = function() {
+
+ return _each( this.json.shaders, function( shader, shaderId ) {
+
+ return new Promise( function( resolve ) {
+
+ var loader = new THREE.XHRLoader();
+ loader.responseType = 'text';
+ loader.load( resolveURL( shader.uri, this.options.path ), function( shaderText ) {
+
+ resolve( shaderText );
+
+ });
+
+ }.bind( this ));
+
+ }.bind( this ));
+
};
+
+GLTFParser.prototype.loadBuffers = function() {
+
+ return _each( this.json.buffers, function( buffer, bufferId ) {
+
+ if ( buffer.type === 'arraybuffer' ) {
+
+ return new Promise( function( resolve ) {
+
+ var loader = new THREE.XHRLoader();
+ loader.responseType = 'arraybuffer';
+ loader.load( resolveURL( buffer.uri, this.options.path ), function( buffer ) {
+
+ resolve( buffer );
+
+ } );
+
+ }.bind( this ));
+
+ }
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadBufferViews = function() {
+
+ return this._withDependencies([
+ "buffers"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.bufferViews, function( bufferView, bufferViewId ) {
+
+ var arraybuffer = dependencies.buffers[ bufferView.buffer ];
+
+ return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength );
+
+ });
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadAccessors = function() {
+
+ return this._withDependencies([
+ "bufferViews"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.accessors, function( accessor, accessorId ) {
+
+ var arraybuffer = dependencies.bufferViews[ accessor.bufferView ];
+ var itemSize = WEBGL_TYPE_SIZES[ accessor.type ];
+ var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ];
+
+ var array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize );
+
+ return new THREE.BufferAttribute( array, itemSize );
+
+ });
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadTextures = function() {
+
+ return _each( this.json.textures, function( texture, textureId ) {
+
+ if ( texture.source ) {
+
+ return new Promise( function( resolve ) {
+
+ var source = this.json.images[ texture.source ];
+
+ var textureLoader = THREE.Loader.Handlers.get( source.uri );
+ if ( textureLoader === null ) {
+
+ textureLoader = new THREE.TextureLoader();
+
+ }
+ textureLoader.crossOrigin = this.options.crossOrigin || false;
+
+ textureLoader.load( resolveURL( source.uri, this.options.path ), function( _texture ) {
+
+ _texture.flipY = false;
+
+ if ( texture.sampler ) {
+
+ var sampler = this.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 );
+
+ }.bind( this ));
+
+ }.bind( this ));
+
+ }
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadMaterials = function() {
+
+ return this._withDependencies([
+ "shaders",
+ "textures"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.materials, function( material, materialId ) {
+
+ var materialType;
+ var materialValues = {};
+ var materialParams = {};
+
+ var khr_material;
+
+ if ( material.extensions && material.extensions.KHR_materials_common ) {
+
+ khr_material = material.extensions.KHR_materials_common;
+
+ } else if ( this.json.extensions && this.json.extensions.KHR_materials_common ) {
+
+ khr_material = this.json.extensions.KHR_materials_common;
+
+ }
+
+ if ( khr_material ) {
+
+ switch ( khr_material.technique )
+ {
+ case 'BLINN' :
+ case 'PHONG' :
+ materialType = THREE.MeshPhongMaterial;
+ break;
+
+ case 'LAMBERT' :
+ materialType = THREE.MeshLambertMaterial;
+ break;
+
+ case 'CONSTANT' :
+ default :
+ materialType = THREE.MeshBasicMaterial;
+ break;
+ }
+
+ _each( khr_material.values, function( value, prop ) {
+
+ materialValues[ prop ] = value;
+
+ });
+
+ if ( khr_material.doubleSided || materialValues.doubleSided ) {
+
+ materialParams.side = THREE.DoubleSide;
+
+ }
+
+ if ( khr_material.transparent || materialValues.transparent ) {
+
+ materialParams.transparent = true;
+ materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1;
+
+ }
+
+ } else if ( material.technique === undefined ) {
+
+ materialType = THREE.MeshPhongMaterial;
+
+ _each( material.values, function( value, prop ) {
+
+ materialValues[ prop ] = value;
+
+ });
+
+ } else {
+
+ materialType = DeferredShaderMaterial;
+
+ var technique = this.json.techniques[ material.technique ];
+
+ materialParams.uniforms = {};
+
+ var program = this.json.programs[ technique.program ];
+
+ if ( program ) {
+
+ materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ];
+
+ if ( ! materialParams.fragmentShader ) {
+
+ console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader );
+ materialType = THREE.MeshPhongMaterial;
+
+ }
+
+ var vertexShader = dependencies.shaders[ program.vertexShader ];
+
+ if ( ! vertexShader ) {
+
+ console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader );
+ materialType = THREE.MeshPhongMaterial;
+
+ }
+
+ // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS
+ materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique );
+
+ var uniforms = technique.uniforms;
+
+ _each( uniforms, function( pname, uniformId ) {
+
+ var shaderParam = technique.parameters[ pname ];
+
+ var ptype = shaderParam.type;
+
+ if ( WEBGL_TYPE[ ptype ] ) {
+
+ var pcount = shaderParam.count;
+ var value = material.values[ pname ];
+
+ var uvalue = new WEBGL_TYPE[ ptype ]();
+ var usemantic = shaderParam.semantic;
+ var unode = shaderParam.node;
+
+ switch ( ptype ) {
+
+ case WEBGL_CONSTANTS.FLOAT:
+
+ uvalue = shaderParam.value;
+
+ if ( pname == "transparency" ) {
+
+ materialParams.transparent = true;
+
+ }
+
+ if ( value ) {
+
+ uvalue = value;
+
+ }
+
+ break;
+
+ case WEBGL_CONSTANTS.FLOAT_VEC2:
+ case WEBGL_CONSTANTS.FLOAT_VEC3:
+ case WEBGL_CONSTANTS.FLOAT_VEC4:
+ case WEBGL_CONSTANTS.FLOAT_MAT3:
+
+ if ( shaderParam && shaderParam.value ) {
+
+ uvalue.fromArray( shaderParam.value );
+
+ }
+
+ if ( value ) {
+
+ uvalue.fromArray( value );
+
+ }
+
+ break;
+
+ case WEBGL_CONSTANTS.FLOAT_MAT2:
+
+ // what to do?
+ console.warn("FLOAT_MAT2 is not a supported uniform type");
+ break;
+
+ case WEBGL_CONSTANTS.FLOAT_MAT4:
+
+ if ( pcount ) {
+
+ uvalue = new Array( pcount );
+
+ for ( var mi = 0; mi < pcount; mi ++ ) {
+
+ uvalue[ mi ] = new WEBGL_TYPE[ ptype ]();
+
+ }
+
+ if ( shaderParam && shaderParam.value ) {
+
+ var m4v = shaderParam.value;
+ uvalue.fromArray( m4v );
+
+ }
+
+ if ( value ) {
+
+ uvalue.fromArray( value );
+
+ }
+
+ } else {
+
+ if ( shaderParam && shaderParam.value ) {
+
+ var m4 = shaderParam.value;
+ uvalue.fromArray( m4 );
+
+ }
+
+ if ( value ) {
+
+ uvalue.fromArray( value );
+
+ }
+
+ }
+
+ break;
+
+ case WEBGL_CONSTANTS.SAMPLER_2D:
+
+ uvalue = value ? dependencies.textures[ value ] : null;
+
+ break;
+
+ }
+
+ materialParams.uniforms[ uniformId ] = {
+ value: uvalue,
+ semantic: usemantic,
+ node: unode
+ };
+
+ } else {
+
+ throw new Error( "Unknown shader uniform param type: " + ptype );
+
+ }
+
+ });
+
+ }
+
+ }
+
+ if ( Array.isArray( materialValues.diffuse ) ) {
+
+ materialParams.color = new THREE.Color().fromArray( materialValues.diffuse );
+
+ } else if ( typeof( materialValues.diffuse ) === 'string' ) {
+
+ materialParams.map = dependencies.textures[ materialValues.diffuse ];
+
+ }
+
+ delete materialParams.diffuse;
+
+ if ( typeof( materialValues.reflective ) === 'string' ) {
+
+ materialParams.envMap = dependencies.textures[ materialValues.reflective ];
+
+ }
+
+ if ( typeof( materialValues.bump ) === 'string' ) {
+
+ materialParams.bumpMap = dependencies.textures[ materialValues.bump ];
+
+ }
+
+ if ( Array.isArray( materialValues.emission ) ) {
+
+ materialParams.emissive = new THREE.Color().fromArray( materialValues.emission );
+
+ }
+
+ if ( Array.isArray( materialValues.specular ) ) {
+
+ materialParams.specular = new THREE.Color().fromArray( materialValues.specular );
+
+ }
+
+ if ( materialValues.shininess !== undefined ) {
+
+ materialParams.shininess = materialValues.shininess;
+
+ }
+
+ var _material = new materialType( materialParams );
+ _material.name = material.name;
+
+ return _material;
+
+ }.bind( this ));
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadMeshes = function() {
+
+ return this._withDependencies([
+ "accessors",
+ "materials"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.meshes, function( mesh, meshId ) {
+
+ var group = new THREE.Object3D();
+ group.name = mesh.name;
+
+ var primitives = mesh.primitives;
+
+ _each( primitives, function( primitive ) {
+
+ if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) {
+
+ var geometry = new THREE.BufferGeometry();
+
+ var attributes = primitive.attributes;
+
+ _each( attributes, function( attributeEntry, attributeId ) {
+
+ if ( !attributeEntry ) {
+
+ return;
+
+ }
+
+ var bufferAttribute = dependencies.accessors[ attributeEntry ];
+
+ switch ( attributeId ) {
+
+ case 'POSITION':
+ geometry.addAttribute( 'position', bufferAttribute );
+ break;
+
+ case 'NORMAL':
+ geometry.addAttribute( 'normal', bufferAttribute );
+ break;
+
+ case 'TEXCOORD_0':
+ case 'TEXCOORD0':
+ case 'TEXCOORD':
+ geometry.addAttribute( 'uv', bufferAttribute );
+ break;
+
+ case 'WEIGHT':
+ geometry.addAttribute( 'skinWeight', bufferAttribute );
+ break;
+
+ case 'JOINT':
+ geometry.addAttribute( 'skinIndex', bufferAttribute );
+ break;
+
+ }
+
+ });
+
+ if ( primitive.indices ) {
+
+ var indexArray = dependencies.accessors[ primitive.indices ];
+
+ geometry.setIndex( indexArray );
+
+ var offset = {
+ start: 0,
+ index: 0,
+ count: indexArray.count
+ };
+
+ geometry.groups.push( offset );
+
+ geometry.computeBoundingSphere();
+
+ }
+
+
+ var material = dependencies.materials[ primitive.material ];
+
+ var meshNode = new THREE.Mesh( geometry, material );
+ meshNode.castShadow = true;
+
+ group.add( meshNode );
+
+ } else {
+
+ console.warn("Non-triangular primitives are not supported");
+
+ }
+
+ });
+
+ return group;
+
+ });
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadCameras = function() {
+
+ return _each( this.json.cameras, function( camera, cameraId ) {
+
+ if ( camera.type == "perspective" && camera.perspective ) {
+
+ var yfov = camera.perspective.yfov;
+ var xfov = camera.perspective.xfov;
+ var aspect_ratio = camera.perspective.aspect_ratio || 1;
+
+ // According to COLLADA spec...
+ // aspect_ratio = xfov / yfov
+ xfov = ( xfov === undefined && yfov ) ? yfov * aspect_ratio : xfov;
+
+ // According to COLLADA spec...
+ // aspect_ratio = xfov / yfov
+ // yfov = ( yfov === undefined && xfov ) ? xfov / aspect_ratio : yfov;
+
+ var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspect_ratio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 );
+ _camera.name = camera.name;
+
+ return _camera;
+
+ } else if ( camera.type == "orthographic" && camera.orthographic ) {
+
+ var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar );
+ _camera.name = camera.name;
+
+ return _camera;
+
+ }
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadSkins = function() {
+
+ return this._withDependencies([
+ "accessors"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.skins, function( skin, skinId ) {
+
+ var _skin = {
+ bindShapeMatrix: new THREE.Matrix4().fromArray( skin.bindShapeMatrix ),
+ jointNames: skin.jointNames,
+ inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ]
+ };
+
+ return _skin;
+
+ });
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadAnimations = function() {
+
+ return this._withDependencies([
+ "accessors",
+ "nodes"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.animations, function( animation, animationId ) {
+
+ var interps = [];
+
+ _each( animation.channels, function( channel ) {
+
+ var sampler = animation.samplers[ channel.sampler ];
+
+ if (sampler && animation.parameters) {
+
+ var target = channel.target;
+ var name = target.id;
+ var input = animation.parameters[sampler.input];
+ var output = animation.parameters[sampler.output];
+
+ var inputAccessor = dependencies.accessors[ input ];
+ var outputAccessor = dependencies.accessors[ output ];
+
+ var node = dependencies.nodes[ name ];
+
+ if ( node ) {
+
+ var interp = {
+ keys : inputAccessor.array,
+ values : outputAccessor.array,
+ count : inputAccessor.count,
+ target : node,
+ path : target.path,
+ type : sampler.interpolation
+ };
+
+ interps.push( interp );
+
+ }
+
+ }
+
+ });
+
+ var _animation = new GLTFAnimation(interps);
+ _animation.name = "animation_" + animationId;
+
+ return _animation;
+
+ });
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadNodes = function() {
+
+ return _each( this.json.nodes, function( node, nodeId ) {
+
+ var matrix = new THREE.Matrix4();
+
+ var _node;
+
+ if ( node.jointName ) {
+
+ _node = new THREE.Bone();
+ _node.jointName = node.jointName;
+
+ } else {
+
+ _node = new THREE.Object3D()
+
+ }
+
+ _node.name = node.name;
+
+ _node.matrixAutoUpdate = false;
+
+ if ( node.matrix !== undefined ) {
+
+ matrix.fromArray( node.matrix );
+ _node.applyMatrix( matrix );
+
+ } else {
+
+ if ( node.translation !== undefined ) {
+
+ _node.position.fromArray( node.translation );
+
+ }
+
+ if ( node.rotation !== undefined ) {
+
+ _node.quaternion.fromArray( node.rotation );
+
+ }
+
+ if ( node.scale !== undefined ) {
+
+ _node.scale.fromArray( node.scale );
+
+ }
+
+ }
+
+ return _node;
+
+ }.bind( this )).then( function( __nodes ) {
+
+ return this._withDependencies([
+ "meshes",
+ "skins",
+ "cameras",
+ "extensions"
+ ]).then( function( dependencies ) {
+
+ return _each( __nodes, function( _node, nodeId ) {
+
+ var node = this.json.nodes[ nodeId ];
+
+ if ( node.meshes !== undefined ) {
+
+ _each( node.meshes, function( meshId ) {
+
+ var group = dependencies.meshes[ meshId ];
+
+ _each( group.children, function( mesh ) {
+
+ // clone Mesh to add to _node
+
+ var originalMaterial = mesh.material;
+ var originalGeometry = mesh.geometry;
+
+ var material;
+ if(originalMaterial.isDeferredShaderMaterial) {
+ originalMaterial = material = originalMaterial.create();
+ } else {
+ material = originalMaterial;
+ }
+
+ mesh = new THREE.Mesh( originalGeometry, material );
+ mesh.castShadow = true;
+
+ var skinEntry;
+ if ( node.skin ) {
+
+ skinEntry = dependencies.skins[ node.skin ];
+
+ }
+
+ // Replace Mesh with SkinnedMesh in library
+ if (skinEntry) {
+
+ var geometry = originalGeometry;
+ var material = originalMaterial;
+ material.skinning = true;
+
+ mesh = new THREE.SkinnedMesh( geometry, material, false );
+ mesh.castShadow = true;
+
+ var bones = [];
+ var boneInverses = [];
+
+ _each( skinEntry.jointNames, function( jointId, i ) {
+
+ var jointNode = __nodes[ jointId ];
+
+ if ( jointNode ) {
+
+ jointNode.skin = mesh;
+ bones.push(jointNode);
+
+ var m = skinEntry.inverseBindMatrices.array;
+ var mat = new THREE.Matrix4().fromArray( m, i * 16 );
+ boneInverses.push(mat);
+
+ } else {
+ console.warn( "WARNING: joint: ''" + jointId + "' could not be found" );
+ }
+
+ });
+
+ mesh.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix );
+
+ }
+
+ _node.add( mesh );
+
+ });
+
+ });
+
+ }
+
+ if ( node.camera !== undefined ) {
+
+ var camera = dependencies.cameras[ node.camera ];
+
+ _node.add( camera );
+
+ }
+
+ if (node.extensions && node.extensions.KHR_materials_common
+ && node.extensions.KHR_materials_common.light) {
+
+ var light = dependencies.extensions.KHR_materials_common.lights[ node.extensions.KHR_materials_common.light ];
+
+ _node.add(light);
+
+ }
+
+ return _node;
+
+ }.bind( this ));
+
+ }.bind( this ));
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadExtensions = function() {
+
+ return _each( this.json.extensions, function( extension, extensionId ) {
+
+ switch ( extensionId ) {
+
+ case "KHR_materials_common":
+
+ var extensionNode = {
+ lights: {}
+ };
+
+ var lights = extension.lights;
+
+ _each( lights, function( light, 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;
+
+ }
+
+ }.bind( this ));
+
+};
+
+GLTFParser.prototype.loadScenes = function() {
+
+ // scene node hierachy builder
+
+ var buildNodeHierachy = function( nodeId, parentObject, allNodes ) {
+
+ var _node = allNodes[ nodeId ];
+ parentObject.add( _node );
+
+ var node = this.json.nodes[ nodeId ];
+
+ if ( node.children ) {
+
+ _each( node.children, function( child ) {
+
+ buildNodeHierachy( child, _node, allNodes );
+
+ });
+
+ }
+
+ }.bind( this );
+
+ return this._withDependencies([
+ "nodes"
+ ]).then( function( dependencies ) {
+
+ return _each( this.json.scenes, function( scene, sceneId ) {
+
+ var _scene = new THREE.Scene();
+ _scene.name = scene.name;
+
+ _each( scene.nodes, function( nodeId ) {
+
+ buildNodeHierachy( nodeId, _scene, dependencies.nodes );
+
+ });
+
+ _scene.traverse( function( child ) {
+
+ // Register raw material meshes with GLTFLoader.Shaders
+ if (child.material && child.material.isRawShaderMaterial) {
+ var xshader = new GLTFShader( child, dependencies.nodes );
+ THREE.GLTFLoader.Shaders.add( child.uuid, xshader );
+ }
+
+ });
+
+ return _scene;
+
+ });
+
+ }.bind( this ));
+
+};
+
+})();
diff --git a/examples/js/loaders/gltf/glTF-parser.js b/examples/js/loaders/deprecated/gltf/glTF-parser.js
similarity index 100%
rename from examples/js/loaders/gltf/glTF-parser.js
rename to examples/js/loaders/deprecated/gltf/glTF-parser.js
diff --git a/examples/js/loaders/gltf/glTFAnimation.js b/examples/js/loaders/deprecated/gltf/glTFAnimation.js
similarity index 100%
rename from examples/js/loaders/gltf/glTFAnimation.js
rename to examples/js/loaders/deprecated/gltf/glTFAnimation.js
diff --git a/examples/js/loaders/gltf/glTFLoader.js b/examples/js/loaders/deprecated/gltf/glTFLoader.js
similarity index 100%
rename from examples/js/loaders/gltf/glTFLoader.js
rename to examples/js/loaders/deprecated/gltf/glTFLoader.js
diff --git a/examples/js/loaders/gltf/glTFLoaderUtils.js b/examples/js/loaders/deprecated/gltf/glTFLoaderUtils.js
similarity index 100%
rename from examples/js/loaders/gltf/glTFLoaderUtils.js
rename to examples/js/loaders/deprecated/gltf/glTFLoaderUtils.js
diff --git a/examples/js/loaders/gltf/glTFShaders.js b/examples/js/loaders/deprecated/gltf/glTFShaders.js
similarity index 100%
rename from examples/js/loaders/gltf/glTFShaders.js
rename to examples/js/loaders/deprecated/gltf/glTFShaders.js
diff --git a/examples/js/loaders/gltf/gltfUtilities.js b/examples/js/loaders/deprecated/gltf/gltfUtilities.js
similarity index 100%
rename from examples/js/loaders/gltf/gltfUtilities.js
rename to examples/js/loaders/deprecated/gltf/gltfUtilities.js
diff --git a/examples/webgl_loader_gltf.html b/examples/webgl_loader_gltf.html
index bfcc1fe36d171a..6b03bd7ed6496b 100644
--- a/examples/webgl_loader_gltf.html
+++ b/examples/webgl_loader_gltf.html
@@ -126,11 +126,7 @@
-
-
-
-
-
+