From b11677255c746315814b8081ab384425b3f41650 Mon Sep 17 00:00:00 2001 From: Jerome Etienne Date: Sat, 1 Jul 2017 11:01:59 +0100 Subject: [PATCH] added gltf loaders --- .../examples/js/loaders/GLTF2Loader.js | 2872 +++++++++++++++++ .../examples/js/loaders/GLTFLoader.js | 2213 +++++++++++++ 2 files changed, 5085 insertions(+) create mode 100644 three.js/examples/vendor/three.js/examples/js/loaders/GLTF2Loader.js create mode 100644 three.js/examples/vendor/three.js/examples/js/loaders/GLTFLoader.js diff --git a/three.js/examples/vendor/three.js/examples/js/loaders/GLTF2Loader.js b/three.js/examples/vendor/three.js/examples/js/loaders/GLTF2Loader.js new file mode 100644 index 0000000..3efb86f --- /dev/null +++ b/three.js/examples/vendor/three.js/examples/js/loaders/GLTF2Loader.js @@ -0,0 +1,2872 @@ +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + * @author Don McCurdy / https://www.donmccurdy.com + */ + +THREE.GLTF2Loader = ( function () { + + function GLTF2Loader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + GLTF2Loader.prototype = { + + constructor: GLTF2Loader, + + 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.FileLoader( scope.manager ); + + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, onLoad, path ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + parse: function ( data, callback, path ) { + + var content; + var extensions = {}; + + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = convertUint8ArrayToString( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + if ( json.extensionsUsed ) { + + if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_LIGHTS ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_LIGHTS ] = new GLTFLightsExtension( json ); + + } + + if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + + } + + if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); + + } + + if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_TECHNIQUE_WEBGL ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] = new GLTFTechniqueWebglExtension( json ); + + } + + } + + console.time( 'GLTF2Loader' ); + + var parser = new GLTFParser( json, extensions, { + + path: path || this.path, + crossOrigin: this.crossOrigin + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + console.timeEnd( 'GLTF2Loader' ); + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + + }; + + /* GLTFREGISTRY */ + + function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + + } + + /* GLTFSHADER */ + + function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + + } + + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + 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; + // transform 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 ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_LIGHTS: 'KHR_lights', + KHR_MATERIALS_COMMON: 'KHR_materials_common', + KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', + KHR_TECHNIQUE_WEBGL: 'KHR_technique_webgl', + }; + + /** + * Lights Extension + * + * Specification: PENDING + */ + function GLTFLightsExtension( json ) { + + this.name = EXTENSIONS.KHR_LIGHTS; + + this.lights = {}; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS ] ) || {}; + var lights = extension.lights || {}; + + for ( var lightId in lights ) { + + var light = lights[ lightId ]; + var lightNode; + + var color = new THREE.Color().fromArray( light.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 ) { + + lightNode.name = light.name || ( 'light_' + lightId ); + this.lights[ lightId ] = lightNode; + + } + + } + + } + + /** + * Common Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common + */ + function GLTFMaterialsCommonExtension( json ) { + + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + + } + + GLTFMaterialsCommonExtension.prototype.getMaterialType = function ( material ) { + + var khrMaterial = material.extensions[ this.name ]; + + switch ( khrMaterial.type ) { + + case 'commonBlinn' : + case 'commonPhong' : + return THREE.MeshPhongMaterial; + + case 'commonLambert' : + return THREE.MeshLambertMaterial; + + case 'commonConstant' : + default : + return THREE.MeshBasicMaterial; + + } + + }; + + GLTFMaterialsCommonExtension.prototype.extendParams = function ( materialParams, material, dependencies ) { + + var khrMaterial = material.extensions[ this.name ]; + + var keys = []; + + // TODO: Currently ignored: 'ambientFactor', 'ambientTexture' + switch ( khrMaterial.type ) { + + case 'commonBlinn' : + case 'commonPhong' : + keys.push( 'diffuseFactor', 'diffuseTexture', 'specularFactor', 'specularTexture', 'shininessFactor' ); + break; + + case 'commonLambert' : + keys.push( 'diffuseFactor', 'diffuseTexture' ); + break; + + case 'commonConstant' : + default : + break; + + } + + var materialValues = {}; + + keys.forEach( function( v ) { + + if ( khrMaterial[ v ] !== undefined ) materialValues[ v ] = khrMaterial[ v ]; + + } ); + + if ( materialValues.diffuseFactor !== undefined ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.diffuseFactor ); + + } + + if ( materialValues.diffuseTexture !== undefined ) { + + materialParams.map = dependencies.textures[ materialValues.diffuseTexture.index ]; + + } + + if ( materialValues.specularFactor !== undefined ) { + + materialParams.specular = new THREE.Color().fromArray( materialValues.specularFactor ); + + } + + if ( materialValues.specularTexture !== undefined ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specularTexture.index ]; + + } + + if ( materialValues.shininessFactor !== undefined ) { + + materialParams.shininess = materialValues.shininessFactor; + + } + + }; + + /* BINARY EXTENSION */ + + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + 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 ); + + this.header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; + + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { + + throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); + + } else if ( this.header.version < 2.0 ) { + + throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); + + } + + var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); + var chunkIndex = 0; + + while ( chunkIndex < chunkView.byteLength ) { + + var chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + var chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { + + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); + this.content = convertUint8ArrayToString( contentArray ); + + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + + var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); + + } + + // Clients must ignore chunks with unknown types. + + chunkIndex += chunkLength; + + } + + if ( this.content === null ) { + + throw new Error( 'GLTF2Loader: JSON content not found.' ); + + } + + } + + /** + * WebGL Technique Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_technique_webgl + */ + function GLTFTechniqueWebglExtension( json ) { + + this.name = EXTENSIONS.KHR_TECHNIQUE_WEBGL; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] ) || {}; + + this.techniques = extension.techniques || {}; + this.programs = extension.programs || {}; + this.shaders = extension.shaders || {}; + + } + + GLTFTechniqueWebglExtension.prototype.getMaterialType = function () { + + return DeferredShaderMaterial; + + }; + + GLTFTechniqueWebglExtension.prototype.extendParams = function ( materialParams, material, dependencies ) { + + var extension = material[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ]; + var technique = dependencies.techniques[ extension.technique ]; + + materialParams.uniforms = {}; + + var program = dependencies.programs[ technique.program ]; + + if ( program === undefined ) { + + return; + + } + + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + + if ( ! materialParams.fragmentShader ) { + + throw new Error( 'ERROR: Missing fragment shader definition:', program.fragmentShader ); + + } + + var vertexShader = dependencies.shaders[ program.vertexShader ]; + + if ( ! vertexShader ) { + + throw new Error( 'ERROR: Missing vertex shader definition:', program.vertexShader ); + + } + + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + + var uniforms = technique.uniforms; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) 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 !== undefined ) { + + 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: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( 'Unknown shader uniform param type: ' + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + + } else { + + materialParams.side = THREE.DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + + } + + }; + + /** + * Specular-Glossiness Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness + */ + function GLTFMaterialsPbrSpecularGlossinessExtension() { + + return { + + name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS, + + getMaterialType: function () { + + return THREE.ShaderMaterial; + + }, + + extendParams: function ( params, material, dependencies ) { + + // specification + // https://github.com/sbtron/glTF/tree/KHRpbrSpecGloss/extensions/Khronos/KHR_materials_pbrSpecularGlossiness + + var pbrSpecularGlossiness = material.extensions[ this.name ]; + + var shader = THREE.ShaderLib[ 'standard' ]; + + var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + var specularMapParsFragmentChunk = [ + '#ifdef USE_SPECULARMAP', + ' uniform sampler2D specularMap;', + '#endif' + ].join( '\n' ); + + var glossinessMapParsFragmentChunk = [ + '#ifdef USE_GLOSSINESSMAP', + ' uniform sampler2D glossinessMap;', + '#endif' + ].join( '\n' ); + + var specularMapFragmentChunk = [ + 'vec3 specularFactor = specular;', + '#ifdef USE_SPECULARMAP', + ' vec4 texelSpecular = texture2D( specularMap, vUv );', + ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' specularFactor *= texelSpecular.rgb;', + '#endif' + ].join( '\n' ); + + var glossinessMapFragmentChunk = [ + 'float glossinessFactor = glossiness;', + '#ifdef USE_GLOSSINESSMAP', + ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', + ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' glossinessFactor *= texelGlossiness.a;', + '#endif' + ].join( '\n' ); + + var lightPhysicalFragmentChunk = [ + 'PhysicalMaterial material;', + 'material.diffuseColor = diffuseColor.rgb;', + 'material.specularRoughness = clamp( 1.0 - glossinessFactor, 0.04, 1.0 );', + 'material.specularColor = specularFactor.rgb;', + ].join( '\n' ); + + var fragmentShader = shader.fragmentShader + .replace( '#include ', '' ) + .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) + .replace( 'uniform float metalness;', 'uniform float glossiness;' ) + .replace( '#include ', specularMapParsFragmentChunk ) + .replace( '#include ', glossinessMapParsFragmentChunk ) + .replace( '#include ', specularMapFragmentChunk ) + .replace( '#include ', glossinessMapFragmentChunk ) + .replace( '#include ', lightPhysicalFragmentChunk ); + + delete uniforms.roughness; + delete uniforms.metalness; + delete uniforms.roughnessMap; + delete uniforms.metalnessMap; + + uniforms.specular = { value: new THREE.Color().setHex( 0x111111 ) }; + uniforms.glossiness = { value: 0.5 }; + uniforms.specularMap = { value: null }; + uniforms.glossinessMap = { value: null }; + + params.vertexShader = shader.vertexShader; + params.fragmentShader = fragmentShader; + params.uniforms = uniforms; + params.defines = { 'STANDARD': '' }; + + params.color = new THREE.Color( 1.0, 1.0, 1.0 ); + params.opacity = 1.0; + + if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { + + var array = pbrSpecularGlossiness.diffuseFactor; + + params.color.fromArray( array ); + params.opacity = array[ 3 ]; + + } + + if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { + + params.map = dependencies.textures[ pbrSpecularGlossiness.diffuseTexture.index ]; + + } + + params.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); + params.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; + params.specular = new THREE.Color( 1.0, 1.0, 1.0 ); + + if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { + + params.specular.fromArray( pbrSpecularGlossiness.specularFactor ); + + } + + if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { + + params.glossinessMap = dependencies.textures[ pbrSpecularGlossiness.specularGlossinessTexture.index ]; + params.specularMap = dependencies.textures[ pbrSpecularGlossiness.specularGlossinessTexture.index ]; + + } + + }, + + createMaterial: function ( params ) { + + // setup material properties based on MeshStandardMaterial for Specular-Glossiness + + var material = new THREE.ShaderMaterial( { + defines: params.defines, + vertexShader: params.vertexShader, + fragmentShader: params.fragmentShader, + uniforms: params.uniforms, + fog: true, + lights: true, + opacity: params.opacity, + transparent: params.transparent + } ); + + material.color = params.color; + + material.map = params.map === undefined ? null : params.map; + + material.lightMap = null; + material.lightMapIntensity = 1.0; + + material.aoMap = params.aoMap === undefined ? null : params.aoMap; + material.aoMapIntensity = 1.0; + + material.emissive = params.emissive; + material.emissiveIntensity = 1.0; + material.emissiveMap = params.emissiveMap === undefined ? null : params.emissiveMap; + + material.bumpMap = params.bumpMap === undefined ? null : params.bumpMap; + material.bumpScale = 1; + + material.normalMap = params.normalMap === undefined ? null : params.normalMap; + material.normalScale = new THREE.Vector2( 1, 1 ); + + material.displacementMap = null; + material.displacementScale = 1; + material.displacementBias = 0; + + material.specularMap = params.specularMap === undefined ? null : params.specularMap; + material.specular = params.specular; + + material.glossinessMap = params.glossinessMap === undefined ? null : params.glossinessMap; + material.glossiness = params.glossiness; + + material.alphaMap = null; + + material.envMap = params.envMap === undefined ? null : params.envMap; + material.envMapIntensity = 1.0; + + material.refractionRatio = 0.98; + + material.extensions.derivatives = true; + + return material; + + }, + + // Here's based on refreshUniformsCommon() and refreshUniformsStandard() in WebGLRenderer. + refreshUniforms: function ( renderer, scene, camera, geometry, material, group ) { + + var uniforms = material.uniforms; + var defines = material.defines; + + uniforms.opacity.value = material.opacity; + + uniforms.diffuse.value.copy( material.color ); + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); + + uniforms.map.value = material.map; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; + + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; + + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + // 6. emissive map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.displacementMap ) { + + uvScaleMap = material.displacementMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } else if ( material.glossinessMap ) { + + uvScaleMap = material.glossinessMap; + + } else if ( material.alphaMap ) { + + uvScaleMap = material.alphaMap; + + } else if ( material.emissiveMap ) { + + uvScaleMap = material.emissiveMap; + + } + + if ( uvScaleMap !== undefined ) { + + // backwards compatibility + if ( uvScaleMap.isWebGLRenderTarget ) { + + uvScaleMap = uvScaleMap.texture; + + } + + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.envMapIntensity.value = material.envMapIntensity; + uniforms.flipEnvMap.value = ( material.envMap && material.envMap.isCubeTexture ) ? -1 : 1; + + uniforms.refractionRatio.value = material.refractionRatio; + + uniforms.specular.value.copy( material.specular ); + uniforms.glossiness.value = material.glossiness; + + uniforms.glossinessMap.value = material.glossinessMap; + + uniforms.emissiveMap.value = material.emissiveMap; + uniforms.bumpMap.value = material.bumpMap; + uniforms.normalMap.value = material.normalMap; + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + + if ( uniforms.glossinessMap.value !== null && defines.USE_GLOSSINESSMAP === undefined ) { + + defines.USE_GLOSSINESSMAP = ''; + // set USE_ROUGHNESSMAP to enable vUv + defines.USE_ROUGHNESSMAP = '' + + } + + if ( uniforms.glossinessMap.value === null && defines.USE_GLOSSINESSMAP !== undefined ) { + + delete defines.USE_GLOSSINESSMAP; + delete defines.USE_ROUGHNESSMAP; + + } + + } + + }; + + } + + /*********************************/ + /********** 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, + LINES: 1, + 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_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion', + weights: 'morphTargetInfluences' + }; + + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + var ALPHA_MODES = { + OPAQUE: 'OPAQUE', + MASK: 'MASK', + BLEND: 'BLEND' + }; + + /* UTILITY FUNCTIONS */ + + function _each( object, callback, thisObj ) { + + if ( !object ) { + return Promise.resolve(); + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + 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[ key ] = value; + + }.bind( this, idx )); + + } 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; + + }); + + } + + function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Blob URL + if ( /^blob:.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + + } + + function convertUint8ArrayToString( array ) { + + if ( window.TextDecoder !== undefined ) { + + return new TextDecoder().decode( array ); + + } + + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + + var s = ''; + + for ( var i = 0, il = array.length; i < il; 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 ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ 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 = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + 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 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + + } + + function createDefaultMaterial() { + + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + + } + + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + + } + + DeferredShaderMaterial.prototype.create = function () { + + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof THREE.Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new THREE.RawShaderMaterial( this.params ); + + }; + + /* GLTF PARSER */ + + function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + 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 ) { + + return dependency; + + } ); + + }; + + GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + + }; + + GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var options = this.options; + var extensions = this.extensions; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + var shaders = extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] !== undefined ? extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].shaders : json.shaders; + + if ( shaders === undefined ) shaders = {}; + + return _each( shaders, function ( shader ) { + + if ( shader.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ shader.bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return _each( json.buffers, function ( buffer, name ) { + + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + + // If present, GLB container is required to be the first buffer. + if ( buffer.uri === undefined && name === 0 ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + + }; + + GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength || 0; + var byteOffset = bufferView.byteOffset || 0; + + return arraybuffer.slice( byteOffset, byteOffset + byteLength ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + 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. + array = new TypedArray( arraybuffer ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new THREE.BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source !== undefined ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + + var urlCreator; + + if ( source.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ source.bufferView ]; + var blob = new Blob( [ bufferView ], { type: source.mimeType } ); + urlCreator = window.URL || window.webkitURL; + sourceUri = urlCreator.createObjectURL( blob ); + + } + + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new THREE.TextureLoader(); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + 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; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + + var samplers = json.samplers || {}; + var sampler = samplers[ texture.sampler ] || {}; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + + resolve( _texture ); + + }, undefined, function () { + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + var extensions = this.extensions; + + return this._withDependencies( [ + + 'shaders', + 'textures' + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialParams = {}; + var materialExtensions = material.extensions || {}; + + if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + materialType = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].getMaterialType( material ); + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].extendParams( materialParams, material, dependencies ); + + } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { + + materialType = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].getMaterialType( material ); + extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].extendParams( materialParams, material, dependencies ); + + } else if ( materialExtensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] ) { + + materialType = extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].getMaterialType( material ); + extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].extendParams( materialParams, material, dependencies ); + + } else if ( material.pbrMetallicRoughness !== undefined ) { + + // Specification: + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material + + materialType = THREE.MeshStandardMaterial; + + var metallicRoughness = material.pbrMetallicRoughness; + + materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + var array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; + + var alphaMode = metallicRoughness.baseColorTexture.alphaMode || ALPHA_MODES.OPAQUE; + + if ( alphaMode !== ALPHA_MODES.OPAQUE ) { + + materialParams.transparent = true; + + } + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + var textureIndex = metallicRoughness.metallicRoughnessTexture.index; + materialParams.metalnessMap = dependencies.textures[ textureIndex ]; + materialParams.roughnessMap = dependencies.textures[ textureIndex ]; + + } + + } else { + + materialType = THREE.MeshPhongMaterial; + + } + + if ( material.doubleSided === true ) { + + materialParams.side = THREE.DoubleSide; + + } + + if ( materialParams.opacity !== undefined && materialParams.opacity < 1.0 ) { + + materialParams.transparent = true; + + } else { + + materialParams.transparent = false; + + } + + if ( material.normalTexture !== undefined ) { + + materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; + + } + + if ( material.occlusionTexture !== undefined ) { + + materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; + + } + + if ( material.emissiveFactor !== undefined ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.color = new THREE.Color().fromArray( material.emissiveFactor ); + + } else { + + materialParams.emissive = new THREE.Color().fromArray( material.emissiveFactor ); + + } + + } + + if ( material.emissiveTexture !== undefined ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ material.emissiveTexture.index ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; + + } + + } + + var _material; + + if ( materialType === THREE.ShaderMaterial ) { + + _material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); + + } else { + + _material = new materialType( materialParams ); + + } + + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var geometry; + + var meshNode; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( attributeEntry === undefined ) 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 'TEXCOORD_1': + + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + if ( material.aoMap !== undefined + && geometry.attributes.uv2 === undefined + && geometry.attributes.uv !== undefined ) { + + console.log( 'GLTF2Loader: Duplicating UVs to support aoMap.' ); + geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) ); + + } + + meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + + if ( primitive.targets !== undefined ) { + + var targets = primitive.targets; + var morphAttributes = geometry.morphAttributes; + + morphAttributes.position = []; + morphAttributes.normal = []; + + material.morphTargets = true; + + for ( var i = 0, il = targets.length; i < il; i ++ ) { + + var target = targets[ i ]; + var attributeName = 'morphTarget' + i; + + var positionAttribute, normalAttribute; + + if ( target.POSITION !== undefined ) { + + // Three.js morph formula is + // position + // + weight0 * ( morphTarget0 - position ) + // + weight1 * ( morphTarget1 - position ) + // ... + // while the glTF one is + // position + // + weight0 * morphTarget0 + // + weight1 * morphTarget1 + // ... + // then adding position to morphTarget. + // So morphTarget value will depend on mesh's position, then cloning attribute + // for the case if attribute is shared among two or more meshes. + + positionAttribute = dependencies.accessors[ target.POSITION ].clone(); + var position = geometry.attributes.position; + + for ( var j = 0, jl = positionAttribute.array.length; j < jl; j ++ ) { + + positionAttribute.array[ j ] += position.array[ j ]; + + } + + } else { + + // Copying the original position not to affect the final position. + // See the formula above. + positionAttribute = geometry.attributes.position.clone(); + + } + + if ( target.NORMAL !== undefined ) { + + material.morphNormals = true; + + // see target.POSITION's comment + + normalAttribute = dependencies.accessors[ target.NORMAL ].clone(); + var normal = geometry.attributes.normal; + + for ( var j = 0, jl = normalAttribute.array.length; j < jl; j ++ ) { + + normalAttribute.array[ j ] += normal.array[ j ]; + + } + + } else { + + normalAttribute = geometry.attributes.normal.clone(); + + } + + // TODO: implement + if ( target.TANGENT !== undefined ) { + + } + + positionAttribute.name = attributeName; + normalAttribute.name = attributeName; + + morphAttributes.position.push( positionAttribute ); + morphAttributes.normal.push( normalAttribute ); + + } + + meshNode.updateMorphTargets(); + + if ( mesh.weights !== undefined ) { + + for ( var i = 0, il = mesh.weights.length; i < il; i ++ ) { + + meshNode.morphTargetInfluences[ i ] = mesh.weights[ i ]; + + } + + } + + } + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new THREE.LineSegments( geometry, material ); + + } else { + + meshNode = new THREE.Line( geometry, material ); + + } + + } else { + + throw new Error( "Only triangular and line primitives are supported" ); + + } + + if ( geometry.attributes.color !== undefined ) { + + material.vertexColors = THREE.VertexColors; + material.needsUpdate = true; + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } + + return group; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + 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 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + + }; + + GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new THREE.Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + joints: skin.joints, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack; + + switch ( PATH_PROPERTIES[ target.path ] ) { + + case PATH_PROPERTIES.weights: + + TypedKeyframeTrack = THREE.NumberKeyframeTrack; + break; + + case PATH_PROPERTIES.rotation: + + TypedKeyframeTrack = THREE.QuaternionKeyframeTrack; + break; + + case PATH_PROPERTIES.position: + case PATH_PROPERTIES.scale: + default: + + TypedKeyframeTrack = THREE.VectorKeyframeTrack; + break; + + } + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + + var targetNames = []; + + if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { + + // node should be THREE.Group here but + // PATH_PROPERTIES.weights(morphTargetInfluences) should be + // the property of a mesh object under node. + // So finding targets here. + + node.traverse( function ( object ) { + + if ( object.isMesh === true && object.material.morphTargets === true ) { + + targetNames.push( object.name ? object.name : object.uuid ); + + } + + } ); + + } else { + + targetNames.push( targetName ); + + } + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + for ( var i = 0, il = targetNames.length; i < il; i ++ ) { + + tracks.push( new TypedKeyframeTrack( + targetNames[ i ] + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new THREE.AnimationClip( name, undefined, tracks ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + var nodes = json.nodes || []; + var skins = json.skins || []; + + // Nothing in the node definition indicates whether it is a Bone or an + // Object3D. Use the skins' joint references to mark bones. + skins.forEach( function ( skin ) { + + skin.joints.forEach( function ( id ) { + + nodes[ id ].isBone = true; + + } ); + + } ); + + return _each( json.nodes, function ( node ) { + + var matrix = new THREE.Matrix4(); + + var _node = node.isBone === true ? new THREE.Bone() : new THREE.Object3D(); + + if ( node.name !== undefined ) { + + _node.name = THREE.PropertyBinding.sanitizeNodeName( node.name ); + + } + + if ( node.extras ) _node.userData = node.extras; + + 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; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + var meshes; + + if ( node.mesh !== undefined) { + + meshes = [ node.mesh ]; + + } else if ( node.meshes !== undefined ) { + + console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); + + meshes = node.meshes; + + } + + if ( meshes !== undefined ) { + + for ( var meshId in meshes ) { + + var mesh = meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + + default: + child = new THREE.Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin !== undefined ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var geometry = originalGeometry; + material = originalMaterial; + material.skinning = true; + + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.joints.length; i < l; i ++ ) { + + var jointId = skinEntry.joints[ i ]; + var jointNode = __nodes[ jointId ]; + + if ( jointNode ) { + + 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" ); + + } + + } + + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + + var extensionLights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_LIGHTS ].light ]; + + _node.add( light ); + + } + + return _node; + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + var extensions = this.extensions; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with GLTF2Loader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + + } + + // for Specular-Glossiness. + if ( child.material && child.material.type === 'ShaderMaterial' ) { + + child.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms; + + } + + } ); + + return _scene; + + } ); + + } ); + + }; + + return GLTF2Loader; + +} )(); diff --git a/three.js/examples/vendor/three.js/examples/js/loaders/GLTFLoader.js b/three.js/examples/vendor/three.js/examples/js/loaders/GLTFLoader.js new file mode 100644 index 0000000..326392a --- /dev/null +++ b/three.js/examples/vendor/three.js/examples/js/loaders/GLTFLoader.js @@ -0,0 +1,2213 @@ +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + */ + +THREE.GLTFLoader = ( function () { + + function GLTFLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + GLTFLoader.prototype = { + + constructor: GLTFLoader, + + 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.FileLoader( scope.manager ); + + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, onLoad, path ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + parse: function ( data, callback, path ) { + + var content; + 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 ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = convertUint8ArrayToString( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + 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, extensions, { + + path: path || this.path, + crossOrigin: this.crossOrigin + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + console.timeEnd( 'GLTFLoader' ); + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + + }; + + /* GLTFREGISTRY */ + + function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + + } + + /* GLTFSHADERS */ + + GLTFLoader.Shaders = { + + update: function () { + + console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); + + } + + }; + + /* GLTFSHADER */ + + function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + + } + + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + 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; + // transform 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 ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + + }; + + + /* ANIMATION */ + + GLTFLoader.Animations = { + + update: function () { + + console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); + + } + + }; + + /*********************************/ + /********** 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 extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + 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 ) { + + 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 ************/ + /*********************************/ + + /* 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, + LINES: 1, + 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_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + + function _each( object, callback, thisObj ) { + + if ( !object ) { + return Promise.resolve(); + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + 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[ key ] = value; + + }.bind( this, idx )); + + } 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; + + }); + + } + + function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + + } + + function convertUint8ArrayToString( array ) { + + if ( window.TextDecoder !== undefined ) { + + //return new TextDecoder().decode( array ); + + } + + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + + var s = ''; + + for ( var i = 0, il = array.length; i < il; 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 ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ 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 = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + 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 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case "WEIGHT": + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case "JOINT": + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + + } + + function createDefaultMaterial() { + + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + + } + + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + + } + + DeferredShaderMaterial.prototype.create = function () { + + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof THREE.Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new THREE.RawShaderMaterial( this.params ); + + }; + + /* GLTF PARSER */ + + function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + 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 ) { + + return dependency; + + } ); + + }; + + GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + + }; + + GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + 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 ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + 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 ) { + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + + }; + + GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + + // 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 ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new THREE.BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + + if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + + sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); + + } + + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new THREE.TextureLoader(); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + + _texture.flipY = false; + + if ( texture.name !== undefined ) _texture.name = texture.name; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + + if ( texture.sampler ) { + + var sampler = json.samplers[ texture.sampler ]; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + + } + + resolve( _texture ); + + }, undefined, function () { + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + + return this._withDependencies( [ + + "shaders", + "textures" + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialValues = {}; + var materialParams = {}; + + var khr_material; + + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + + } + + if ( khr_material ) { + + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + + switch ( khr_material.technique ) { + + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + + } + + keys.forEach( function( v ) { + + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + + } ); + + 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; + + Object.assign( materialValues, material.values ); + + } else { + + materialType = DeferredShaderMaterial; + + var technique = json.techniques[ material.technique ]; + + materialParams.uniforms = {}; + + var program = 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; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) 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 !== undefined ) { + + 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: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( "Unknown shader uniform param type: " + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + + } else { + + materialParams.side = THREE.DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + + } + + } + + } + + 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 ) ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + + } else { + + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + + } + + } else if ( typeof( materialValues.emission ) === 'string' ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ materialValues.emission ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + + } + + } + + if ( Array.isArray( materialValues.specular ) ) { + + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + + } else if ( typeof( materialValues.specular ) === 'string' ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + + } + + if ( materialValues.shininess !== undefined ) { + + materialParams.shininess = materialValues.shininess; + + } + + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + var geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ 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 'TEXCOORD_1': + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHT': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINT': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + var geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + var material = dependencies.materials[ primitive.material ]; + + var meshNode; + + if ( primitive.indices ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new THREE.LineSegments( geometry, material ); + + } else { + + meshNode = new THREE.Line( geometry, material ); + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } else { + + console.warn( "Only triangular and line primitives are supported" ); + + } + + } + + return group; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + 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 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + + }; + + GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new THREE.Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.id; + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new THREE.AnimationClip( name, undefined, tracks ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + return _each( json.nodes, function ( node ) { + + var matrix = new THREE.Matrix4(); + + var _node; + + if ( node.jointName ) { + + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + + } else { + + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + + } + + if ( node.extras ) _node.userData = node.extras; + + 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; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + if ( node.meshes !== undefined ) { + + for ( var meshId in node.meshes ) { + + var mesh = node.meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + + default: + child = new THREE.Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var getJointNode = function ( jointId ) { + + var keys = Object.keys( __nodes ); + + for ( var i = 0, il = keys.length; i < il; i ++ ) { + + var n = __nodes[ keys[ i ] ]; + + if ( n.jointName === jointId ) return n; + + } + + return null; + + }; + + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + + if ( jointNode ) { + + 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" ); + + } + + } + + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ 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 ); + + } + + return _node; + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with GLTFLoader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + + } + + } ); + + return _scene; + + } ); + + } ); + + }; + + return GLTFLoader; + +} )(); -- GitLab