diff --git a/package.json b/package.json index d993dc5..ceffc70 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "gltfjsx": "./cli.js" }, "main": "./src/utils/exports.js", + "types": "./src/utils/exports.d.js", "engines": { "node": ">=16" }, @@ -48,14 +49,15 @@ "meshoptimizer": "^0.18.1", "prettier": "^2.3.2", "sharp": "^0.31.3", - "three": "0.122.0" + "three": "^0.138.0" }, "devDependencies": { "chalk": "^4.1.1", "fast-glob": "^3.2.7", "fs-extra": "^9.0.1", "husky": "^4.3.0", - "lint-staged": "^10.4.0" + "lint-staged": "^10.4.0", + "@types/three": "^0.138.0" }, "husky": { "hooks": { diff --git a/src/bin/DRACOLoader.js b/src/bin/DRACOLoader.js index 8abf3b8..c2e3096 100644 --- a/src/bin/DRACOLoader.js +++ b/src/bin/DRACOLoader.js @@ -1,22 +1,23 @@ 'use strict' -import THREE from 'three' +import * as THREE from 'three' +import { DRACOLoader as BaseDRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js' import draco from 'draco3dgltf' + const decoder = draco.createDecoderModule() -const DRACOLoader = (THREE.DRACOLoader = function (t) { - ;(this.timeLoaded = 0), - (this.manager = t || THREE.DefaultLoadingManager), - (this.materials = null), - (this.verbosity = 0), - (this.attributeOptions = {}), - (this.drawMode = THREE.TrianglesDrawMode), - (this.nativeAttributeMap = { position: 'POSITION', normal: 'NORMAL', color: 'COLOR', uv: 'TEX_COORD' }) -}) -export default DRACOLoader +export class DRACOLoader extends BaseDRACOLoader { + constructor(manager) { + super(manager) + this.timeLoaded = 0 + this.manager = manager || THREE.DefaultLoadingManager + this.materials = null + this.verbosity = 0 + this.attributeOptions = {} + this.drawMode = THREE.TrianglesDrawMode + this.nativeAttributeMap = { position: 'POSITION', normal: 'NORMAL', color: 'COLOR', uv: 'TEX_COORD' } + } -THREE.DRACOLoader.prototype = { - constructor: THREE.DRACOLoader, - load: function (t, e, r, o) { + load(t, e, r, o) { var i = this, n = new THREE.FileLoader(i.manager) n.setPath(this.path), @@ -29,24 +30,30 @@ THREE.DRACOLoader.prototype = { r, o ) - }, - setPath: function (t) { + } + + setPath(t) { return (this.path = t), this - }, - setVerbosity: function (t) { + } + + setVerbosity(t) { return (this.verbosity = t), this - }, - setDrawMode: function (t) { + } + + setDrawMode(t) { return (this.drawMode = t), this - }, - setSkipDequantization: function (t, e) { + } + + setSkipDequantization(t, e) { var r = !0 return void 0 !== e && (r = e), (this.getAttributeOptions(t).skipDequantization = r), this - }, - decodeDracoFile: function (t, e, r, o) { + } + + decodeDracoFile(t, e, r, o) { decoder.then((decoder) => this.decodeDracoFileInternal(t, decoder, e, r, o)) - }, - decodeDracoFileInternal: function (t, e, r, o, i) { + } + + decodeDracoFileInternal(t, e, r, o, i) { var n = new e.DecoderBuffer() n.Init(new Int8Array(t), t.byteLength) var a = new e.Decoder(), @@ -60,8 +67,9 @@ THREE.DRACOLoader.prototype = { this.verbosity > 0 && console.log('Loaded a point cloud.') } r(this.convertDracoGeometryTo3JS(e, a, s, n, o, i)) - }, - addAttributeToGeometry: function (t, e, r, o, i, n, a, s) { + } + + addAttributeToGeometry(t, e, r, o, i, n, a, s) { if (0 === n.ptr) { var u = 'THREE.DRACOLoader: No attribute ' + o throw (console.error(u), new Error(u)) @@ -119,8 +127,9 @@ THREE.DRACOLoader.prototype = { } for (var b = 0; b < l; b++) s[o][b] = d.GetValue(b) a.setAttribute(o, new A(s[o], c)), t.destroy(d) - }, - convertDracoGeometryTo3JS: function (t, e, r, o, i, n) { + } + + convertDracoGeometryTo3JS(t, e, r, o, i, n) { var a, s, u if ( (!0 === this.getAttributeOptions('position').skipDequantization && e.SkipAttributeTransform(t.POSITION), @@ -198,11 +207,13 @@ THREE.DRACOLoader.prototype = { for (I = 0; I < 3; ++I) y.attributes.position.minValues[I] = G.min_value(I) } return t.destroy(G), t.destroy(e), t.destroy(a), y - }, - isVersionSupported: function (t, e) { + } + + isVersionSupported(t, e) { e(decoder.isVersionSupported(t)) - }, - getAttributeOptions: function (t) { + } + + getAttributeOptions(t) { return void 0 === this.attributeOptions[t] && (this.attributeOptions[t] = {}), this.attributeOptions[t] - }, + } } diff --git a/src/bin/GLTFLoader.js b/src/bin/GLTFLoader.js index 98ac8a9..97223e6 100644 --- a/src/bin/GLTFLoader.js +++ b/src/bin/GLTFLoader.js @@ -1,8 +1,8 @@ -import THREE from 'three' +import * as THREE from 'three' -const GLTFLoader = (THREE.GLTFLoader = (function () { - function GLTFLoader(manager) { - THREE.Loader.call(this, manager) +export class GLTFLoader extends THREE.Loader { + constructor(manager) { + super(manager) this.dracoLoader = null this.ddsLoader = null @@ -27,2708 +27,2695 @@ const GLTFLoader = (THREE.GLTFLoader = (function () { }) } - GLTFLoader.prototype = Object.assign(Object.create(THREE.Loader.prototype), { - constructor: GLTFLoader, + load(url, onLoad, onProgress, onError) { + var scope = this - load: function (url, onLoad, onProgress, onError) { - var scope = this + var resourcePath - var resourcePath + if (this.resourcePath !== '') { + resourcePath = this.resourcePath + } else if (this.path !== '') { + resourcePath = this.path + } else { + resourcePath = THREE.LoaderUtils.extractUrlBase(url) + } - if (this.resourcePath !== '') { - resourcePath = this.resourcePath - } else if (this.path !== '') { - resourcePath = this.path + // Tells the LoadingManager to track an extra item, which resolves after + // the model is fully loaded. This means the count of items loaded will + // be incorrect, but ensures manager.onLoad() does not fire early. + this.manager.itemStart(url) + + var _onError = function (e) { + if (onError) { + onError(e) } else { - resourcePath = THREE.LoaderUtils.extractUrlBase(url) + console.error(e) } - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart(url) + scope.manager.itemError(url) + scope.manager.itemEnd(url) + } - var _onError = function (e) { - if (onError) { - onError(e) - } else { - console.error(e) - } + var loader = new THREE.FileLoader(this.manager) - scope.manager.itemError(url) - scope.manager.itemEnd(url) - } + loader.setPath(this.path) + loader.setResponseType('arraybuffer') + loader.setRequestHeader(this.requestHeader) + loader.setWithCredentials(this.withCredentials) - var loader = new THREE.FileLoader(this.manager) - - loader.setPath(this.path) - loader.setResponseType('arraybuffer') - loader.setRequestHeader(this.requestHeader) - loader.setWithCredentials(this.withCredentials) - - loader.load( - url, - function (data) { - try { - scope.parse( - data, - resourcePath, - function (gltf) { - onLoad(gltf) - - scope.manager.itemEnd(url) - }, - _onError - ) - } catch (e) { - _onError(e) - } - }, - onProgress, - _onError - ) - }, + loader.load( + url, + function (data) { + try { + scope.parse( + data, + resourcePath, + function (gltf) { + onLoad(gltf) - setDRACOLoader: function (dracoLoader) { - this.dracoLoader = dracoLoader - return this - }, + scope.manager.itemEnd(url) + }, + _onError + ) + } catch (e) { + _onError(e) + } + }, + onProgress, + _onError + ) + } - setDDSLoader: function (ddsLoader) { - this.ddsLoader = ddsLoader - return this - }, + setDRACOLoader(dracoLoader) { + this.dracoLoader = dracoLoader + return this + } - setKTX2Loader: function (ktx2Loader) { - this.ktx2Loader = ktx2Loader - return this - }, + setDDSLoader(ddsLoader) { + this.ddsLoader = ddsLoader + return this + } - register: function (callback) { - if (this.pluginCallbacks.indexOf(callback) === -1) { - this.pluginCallbacks.push(callback) - } + setKTX2Loader(ktx2Loader) { + this.ktx2Loader = ktx2Loader + return this + } - return this - }, + register(callback) { + if (this.pluginCallbacks.indexOf(callback) === -1) { + this.pluginCallbacks.push(callback) + } - unregister: function (callback) { - if (this.pluginCallbacks.indexOf(callback) !== -1) { - this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback), 1) - } + return this + } - return this - }, + unregister(callback) { + if (this.pluginCallbacks.indexOf(callback) !== -1) { + this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback), 1) + } - parse: function (data, path, onLoad, onError) { - var content - var extensions = {} - var plugins = {} + return this + } - if (typeof data === 'string') { - content = data - } else { - var magic = THREE.LoaderUtils.decodeText(new Uint8Array(data, 0, 4)) - - if (magic === BINARY_EXTENSION_HEADER_MAGIC) { - try { - extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(data) - } catch (error) { - if (onError) onError(error) - return - } + parse(data, path, onLoad, onError) { + var content + var extensions = {} + var plugins = {} - content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content - } else { - content = THREE.LoaderUtils.decodeText(new Uint8Array(data)) + if (typeof data === 'string') { + content = data + } else { + var magic = THREE.LoaderUtils.decodeText(new Uint8Array(data, 0, 4)) + + if (magic === BINARY_EXTENSION_HEADER_MAGIC) { + try { + extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(data) + } catch (error) { + if (onError) onError(error) + return } + + content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content + } else { + content = THREE.LoaderUtils.decodeText(new Uint8Array(data)) } + } - var json = JSON.parse(content) + var json = JSON.parse(content) - if (json.asset === undefined || json.asset.version[0] < 2) { - if (onError) onError(new Error('THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.')) - return - } + if (json.asset === undefined || json.asset.version[0] < 2) { + if (onError) onError(new Error('THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.')) + return + } - var parser = new GLTFParser(json, { - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - }) + var parser = new GLTFParser(json, { + path: path || this.resourcePath || '', + crossOrigin: this.crossOrigin, + manager: this.manager, + ktx2Loader: this.ktx2Loader, + }) - parser.fileLoader.setRequestHeader(this.requestHeader) + parser.fileLoader.setRequestHeader(this.requestHeader) - for (var i = 0; i < this.pluginCallbacks.length; i++) { - var plugin = this.pluginCallbacks[i](parser) - plugins[plugin.name] = plugin + for (var i = 0; i < this.pluginCallbacks.length; i++) { + var plugin = this.pluginCallbacks[i](parser) + plugins[plugin.name] = plugin - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[plugin.name] = true - } + // Workaround to avoid determining as unknown extension + // in addUnknownExtensionsToUserData(). + // Remove this workaround if we move all the existing + // extension handlers to plugin system + extensions[plugin.name] = true + } - if (json.extensionsUsed) { - for (var i = 0; i < json.extensionsUsed.length; ++i) { - var extensionName = json.extensionsUsed[i] - var extensionsRequired = json.extensionsRequired || [] + if (json.extensionsUsed) { + for (var i = 0; i < json.extensionsUsed.length; ++i) { + var extensionName = json.extensionsUsed[i] + var extensionsRequired = json.extensionsRequired || [] - switch (extensionName) { - case EXTENSIONS.KHR_MATERIALS_UNLIT: - extensions[extensionName] = new GLTFMaterialsUnlitExtension() - break + switch (extensionName) { + case EXTENSIONS.KHR_MATERIALS_UNLIT: + extensions[extensionName] = new GLTFMaterialsUnlitExtension() + break - case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: - extensions[extensionName] = new GLTFMaterialsPbrSpecularGlossinessExtension() - break + case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: + extensions[extensionName] = new GLTFMaterialsPbrSpecularGlossinessExtension() + break - case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: - extensions[extensionName] = new GLTFDracoMeshCompressionExtension(json, this.dracoLoader) - break + case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION: + extensions[extensionName] = new GLTFDracoMeshCompressionExtension(json, this.dracoLoader) + break - case EXTENSIONS.MSFT_TEXTURE_DDS: - extensions[extensionName] = new GLTFTextureDDSExtension(this.ddsLoader) - break + case EXTENSIONS.MSFT_TEXTURE_DDS: + extensions[extensionName] = new GLTFTextureDDSExtension(this.ddsLoader) + break - case EXTENSIONS.KHR_TEXTURE_TRANSFORM: - extensions[extensionName] = new GLTFTextureTransformExtension() - break + case EXTENSIONS.KHR_TEXTURE_TRANSFORM: + extensions[extensionName] = new GLTFTextureTransformExtension() + break - case EXTENSIONS.KHR_MESH_QUANTIZATION: - extensions[extensionName] = new GLTFMeshQuantizationExtension() - break + case EXTENSIONS.KHR_MESH_QUANTIZATION: + extensions[extensionName] = new GLTFMeshQuantizationExtension() + break - default: - if (extensionsRequired.indexOf(extensionName) >= 0 && plugins[extensionName] === undefined) { - // console.warn('THREE.GLTFLoader: Unknown extension "' + extensionName + '".') - } - } + default: + if (extensionsRequired.indexOf(extensionName) >= 0 && plugins[extensionName] === undefined) { + // console.warn('THREE.GLTFLoader: Unknown extension "' + extensionName + '".') + } } } + } - parser.setExtensions(extensions) - parser.setPlugins(plugins) - parser.parse(onLoad, onError) - }, - }) + parser.setExtensions(extensions) + parser.setPlugins(plugins) + parser.parse(onLoad, onError) + } +} - /* GLTFREGISTRY */ +/* GLTFREGISTRY */ - function GLTFRegistry() { - var objects = {} +function GLTFRegistry() { + var objects = {} - return { - get: function (key) { - return objects[key] - }, + return { + get: function (key) { + return objects[key] + }, - add: function (key, object) { - objects[key] = object - }, + add: function (key, object) { + objects[key] = object + }, - remove: function (key) { - delete objects[key] - }, + remove: function (key) { + delete objects[key] + }, - removeAll: function () { - objects = {} - }, - } + removeAll: function () { + objects = {} + }, } - - /*********************************/ - /********** EXTENSIONS ***********/ - /*********************************/ - - var EXTENSIONS = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - MSFT_TEXTURE_DDS: 'MSFT_texture_dds', - } - - /** - * DDS Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds - * - */ - function GLTFTextureDDSExtension(ddsLoader) { - if (!ddsLoader) { - throw new Error('THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader') - } - - this.name = EXTENSIONS.MSFT_TEXTURE_DDS - this.ddsLoader = ddsLoader +} + +/*********************************/ +/********** EXTENSIONS ***********/ +/*********************************/ + +var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', + KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', + KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', + KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', + KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', + KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', + KHR_TEXTURE_BASISU: 'KHR_texture_basisu', + KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', + KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', + MSFT_TEXTURE_DDS: 'MSFT_texture_dds', +} + +/** + * DDS Texture Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds + * + */ +function GLTFTextureDDSExtension(ddsLoader) { + if (!ddsLoader) { + throw new Error('THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader') } - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - function GLTFLightsExtension(parser) { - this.parser = parser - this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL + this.name = EXTENSIONS.MSFT_TEXTURE_DDS + this.ddsLoader = ddsLoader +} + +/** + * Punctual Lights Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual + */ +function GLTFLightsExtension(parser) { + this.parser = parser + this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL + + // Object3D instance caches + this.cache = { refs: {}, uses: {} } +} + +GLTFLightsExtension.prototype._markDefs = function () { + var parser = this.parser + var nodeDefs = this.parser.json.nodes || [] + + for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) { + var nodeDef = nodeDefs[nodeIndex] + + if (nodeDef.extensions && nodeDef.extensions[this.name] && nodeDef.extensions[this.name].light !== undefined) { + parser._addNodeRef(this.cache, nodeDef.extensions[this.name].light) + } + } +} + +GLTFLightsExtension.prototype._loadLight = function (lightIndex) { + var parser = this.parser + var cacheKey = 'light:' + lightIndex + var dependency = parser.cache.get(cacheKey) + + if (dependency) return dependency + + var json = parser.json + var extensions = (json.extensions && json.extensions[this.name]) || {} + var lightDefs = extensions.lights || [] + var lightDef = lightDefs[lightIndex] + var lightNode + + var color = new THREE.Color(0xffffff) + + if (lightDef.color !== undefined) color.fromArray(lightDef.color) + + var range = lightDef.range !== undefined ? lightDef.range : 0 + + switch (lightDef.type) { + case 'directional': + lightNode = new THREE.DirectionalLight(color) + lightNode.target.position.set(0, 0, -1) + lightNode.add(lightNode.target) + break + + case 'point': + lightNode = new THREE.PointLight(color) + lightNode.distance = range + break + + case 'spot': + lightNode = new THREE.SpotLight(color) + lightNode.distance = range + // Handle spotlight properties. + lightDef.spot = lightDef.spot || {} + lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0 + lightDef.spot.outerConeAngle = + lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0 + lightNode.angle = lightDef.spot.outerConeAngle + lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle + lightNode.target.position.set(0, 0, -1) + lightNode.add(lightNode.target) + break + + default: + throw new Error('THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".') + } + + // Some lights (e.g. spot) default to a position other than the origin. Reset the position + // here, because node-level parsing will only override position if explicitly specified. + lightNode.position.set(0, 0, 0) + + lightNode.decay = 2 - // Object3D instance caches - this.cache = { refs: {}, uses: {} } - } + if (lightDef.intensity !== undefined) lightNode.intensity = lightDef.intensity - GLTFLightsExtension.prototype._markDefs = function () { - var parser = this.parser - var nodeDefs = this.parser.json.nodes || [] + lightNode.name = parser.createUniqueName(lightDef.name || 'light_' + lightIndex) - for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) { - var nodeDef = nodeDefs[nodeIndex] + dependency = Promise.resolve(lightNode) - if (nodeDef.extensions && nodeDef.extensions[this.name] && nodeDef.extensions[this.name].light !== undefined) { - parser._addNodeRef(this.cache, nodeDef.extensions[this.name].light) - } - } - } + parser.cache.add(cacheKey, dependency) - GLTFLightsExtension.prototype._loadLight = function (lightIndex) { - var parser = this.parser - var cacheKey = 'light:' + lightIndex - var dependency = parser.cache.get(cacheKey) + return dependency +} - if (dependency) return dependency +GLTFLightsExtension.prototype.createNodeAttachment = function (nodeIndex) { + var self = this + var parser = this.parser + var json = parser.json + var nodeDef = json.nodes[nodeIndex] + var lightDef = (nodeDef.extensions && nodeDef.extensions[this.name]) || {} + var lightIndex = lightDef.light - var json = parser.json - var extensions = (json.extensions && json.extensions[this.name]) || {} - var lightDefs = extensions.lights || [] - var lightDef = lightDefs[lightIndex] - var lightNode + if (lightIndex === undefined) return null - var color = new THREE.Color(0xffffff) + return this._loadLight(lightIndex).then(function (light) { + return parser._getNodeRef(self.cache, lightIndex, light) + }) +} - if (lightDef.color !== undefined) color.fromArray(lightDef.color) +/** + * Unlit Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit + */ +function GLTFMaterialsUnlitExtension() { + this.name = EXTENSIONS.KHR_MATERIALS_UNLIT +} - var range = lightDef.range !== undefined ? lightDef.range : 0 +GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () { + return THREE.MeshBasicMaterial +} - switch (lightDef.type) { - case 'directional': - lightNode = new THREE.DirectionalLight(color) - lightNode.target.position.set(0, 0, -1) - lightNode.add(lightNode.target) - break +GLTFMaterialsUnlitExtension.prototype.extendParams = function (materialParams, materialDef, parser) { + var pending = [] - case 'point': - lightNode = new THREE.PointLight(color) - lightNode.distance = range - break + materialParams.color = new THREE.Color(1.0, 1.0, 1.0) + materialParams.opacity = 1.0 - case 'spot': - lightNode = new THREE.SpotLight(color) - lightNode.distance = range - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {} - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0 - lightDef.spot.outerConeAngle = - lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0 - lightNode.angle = lightDef.spot.outerConeAngle - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle - lightNode.target.position.set(0, 0, -1) - lightNode.add(lightNode.target) - break + var metallicRoughness = materialDef.pbrMetallicRoughness - default: - throw new Error('THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".') + if (metallicRoughness) { + if (Array.isArray(metallicRoughness.baseColorFactor)) { + var array = metallicRoughness.baseColorFactor + + materialParams.color.fromArray(array) + materialParams.opacity = array[3] } + } - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set(0, 0, 0) + return Promise.all(pending) +} - lightNode.decay = 2 +/** + * Clearcoat Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ +function GLTFMaterialsClearcoatExtension(parser) { + this.parser = parser + this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT +} - if (lightDef.intensity !== undefined) lightNode.intensity = lightDef.intensity +GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function (materialIndex) { + var parser = this.parser + var materialDef = parser.json.materials[materialIndex] - lightNode.name = parser.createUniqueName(lightDef.name || 'light_' + lightIndex) + if (!materialDef.extensions || !materialDef.extensions[this.name]) return null - dependency = Promise.resolve(lightNode) + return THREE.MeshPhysicalMaterial +} - parser.cache.add(cacheKey, dependency) +GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function (materialIndex, materialParams) { + var parser = this.parser + var materialDef = parser.json.materials[materialIndex] - return dependency + if (!materialDef.extensions || !materialDef.extensions[this.name]) { + return Promise.resolve() } - GLTFLightsExtension.prototype.createNodeAttachment = function (nodeIndex) { - var self = this - var parser = this.parser - var json = parser.json - var nodeDef = json.nodes[nodeIndex] - var lightDef = (nodeDef.extensions && nodeDef.extensions[this.name]) || {} - var lightIndex = lightDef.light + var pending = [] - if (lightIndex === undefined) return null + var extension = materialDef.extensions[this.name] - return this._loadLight(lightIndex).then(function (light) { - return parser._getNodeRef(self.cache, lightIndex, light) - }) + if (extension.clearcoatFactor !== undefined) { + materialParams.clearcoat = extension.clearcoatFactor } - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - function GLTFMaterialsUnlitExtension() { - this.name = EXTENSIONS.KHR_MATERIALS_UNLIT + if (extension.clearcoatRoughnessFactor !== undefined) { + materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor } - GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () { - return THREE.MeshBasicMaterial + if (extension.clearcoatNormalTexture !== undefined) { + if (extension.clearcoatNormalTexture.scale !== undefined) { + var scale = extension.clearcoatNormalTexture.scale + materialParams.clearcoatNormalScale = new THREE.Vector2(scale, scale) + } } - GLTFMaterialsUnlitExtension.prototype.extendParams = function (materialParams, materialDef, parser) { - var pending = [] + return Promise.all(pending) +} - materialParams.color = new THREE.Color(1.0, 1.0, 1.0) - materialParams.opacity = 1.0 +/** + * Transmission Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission + * Draft: https://github.com/KhronosGroup/glTF/pull/1698 + */ +function GLTFMaterialsTransmissionExtension(parser) { + this.parser = parser + this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION +} - var metallicRoughness = materialDef.pbrMetallicRoughness +GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function (materialIndex) { + var parser = this.parser + var materialDef = parser.json.materials[materialIndex] - if (metallicRoughness) { - if (Array.isArray(metallicRoughness.baseColorFactor)) { - var array = metallicRoughness.baseColorFactor + if (!materialDef.extensions || !materialDef.extensions[this.name]) return null - materialParams.color.fromArray(array) - materialParams.opacity = array[3] - } - } + return THREE.MeshPhysicalMaterial +} - return Promise.all(pending) - } +GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function (materialIndex, materialParams) { + var parser = this.parser + var materialDef = parser.json.materials[materialIndex] - /** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ - function GLTFMaterialsClearcoatExtension(parser) { - this.parser = parser - this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT + if (!materialDef.extensions || !materialDef.extensions[this.name]) { + return Promise.resolve() } - GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function (materialIndex) { - var parser = this.parser - var materialDef = parser.json.materials[materialIndex] + var pending = [] - if (!materialDef.extensions || !materialDef.extensions[this.name]) return null + var extension = materialDef.extensions[this.name] - return THREE.MeshPhysicalMaterial + if (extension.transmissionFactor !== undefined) { + materialParams.transmission = extension.transmissionFactor } - GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function (materialIndex, materialParams) { - var parser = this.parser - var materialDef = parser.json.materials[materialIndex] - - if (!materialDef.extensions || !materialDef.extensions[this.name]) { - return Promise.resolve() - } + return Promise.all(pending) +} - var pending = [] +/** + * BasisU Texture Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu + * (draft PR https://github.com/KhronosGroup/glTF/pull/1751) + */ +function GLTFTextureBasisUExtension(parser) { + this.parser = parser + this.name = EXTENSIONS.KHR_TEXTURE_BASISU +} - var extension = materialDef.extensions[this.name] +GLTFTextureBasisUExtension.prototype.loadTexture = function (textureIndex) { + return Promise.resolve(new THREE.Texture()) +} - if (extension.clearcoatFactor !== undefined) { - materialParams.clearcoat = extension.clearcoatFactor - } +/* BINARY EXTENSION */ +var BINARY_EXTENSION_HEADER_MAGIC = 'glTF' +var BINARY_EXTENSION_HEADER_LENGTH = 12 +var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4e4f534a, BIN: 0x004e4942 } - if (extension.clearcoatRoughnessFactor !== undefined) { - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor - } +function GLTFBinaryExtension(data) { + this.name = EXTENSIONS.KHR_BINARY_GLTF + this.content = null + this.body = null - if (extension.clearcoatNormalTexture !== undefined) { - if (extension.clearcoatNormalTexture.scale !== undefined) { - var scale = extension.clearcoatNormalTexture.scale - materialParams.clearcoatNormalScale = new THREE.Vector2(scale, scale) - } - } + var headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH) - return Promise.all(pending) + this.header = { + magic: THREE.LoaderUtils.decodeText(new Uint8Array(data.slice(0, 4))), + version: headerView.getUint32(4, true), + length: headerView.getUint32(8, true), } - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ - function GLTFMaterialsTransmissionExtension(parser) { - this.parser = parser - this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION + if (this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC) { + throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.') + } else if (this.header.version < 2.0) { + throw new Error('THREE.GLTFLoader: Legacy binary file detected.') } - GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function (materialIndex) { - var parser = this.parser - var materialDef = parser.json.materials[materialIndex] - - if (!materialDef.extensions || !materialDef.extensions[this.name]) return null + var chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH) + var chunkIndex = 0 - return THREE.MeshPhysicalMaterial - } + while (chunkIndex < chunkView.byteLength) { + var chunkLength = chunkView.getUint32(chunkIndex, true) + chunkIndex += 4 - GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function (materialIndex, materialParams) { - var parser = this.parser - var materialDef = parser.json.materials[materialIndex] + var chunkType = chunkView.getUint32(chunkIndex, true) + chunkIndex += 4 - if (!materialDef.extensions || !materialDef.extensions[this.name]) { - return Promise.resolve() + if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) { + var contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength) + this.content = THREE.LoaderUtils.decodeText(contentArray) + } else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) { + var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex + this.body = data.slice(byteOffset, byteOffset + chunkLength) } - var pending = [] - - var extension = materialDef.extensions[this.name] - - if (extension.transmissionFactor !== undefined) { - materialParams.transmission = extension.transmissionFactor - } + // Clients must ignore chunks with unknown types. - return Promise.all(pending) + chunkIndex += chunkLength } - /** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - * (draft PR https://github.com/KhronosGroup/glTF/pull/1751) - */ - function GLTFTextureBasisUExtension(parser) { - this.parser = parser - this.name = EXTENSIONS.KHR_TEXTURE_BASISU + if (this.content === null) { + throw new Error('THREE.GLTFLoader: JSON content not found.') } +} - GLTFTextureBasisUExtension.prototype.loadTexture = function (textureIndex) { - return Promise.resolve(new THREE.Texture()) +/** + * DRACO Mesh Compression Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression + */ +function GLTFDracoMeshCompressionExtension(json, dracoLoader) { + if (!dracoLoader) { + throw new Error('THREE.GLTFLoader: No DRACOLoader instance provided.') } - /* BINARY EXTENSION */ - var BINARY_EXTENSION_HEADER_MAGIC = 'glTF' - var BINARY_EXTENSION_HEADER_LENGTH = 12 - var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4e4f534a, BIN: 0x004e4942 } + this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION + this.json = json + this.dracoLoader = dracoLoader +} - function GLTFBinaryExtension(data) { - this.name = EXTENSIONS.KHR_BINARY_GLTF - this.content = null - this.body = null +GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function (primitive, parser) { + var json = this.json + var dracoLoader = this.dracoLoader + var bufferViewIndex = primitive.extensions[this.name].bufferView + var gltfAttributeMap = primitive.extensions[this.name].attributes + var threeAttributeMap = {} + var attributeNormalizedMap = {} + var attributeTypeMap = {} - var headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH) + for (var attributeName in gltfAttributeMap) { + var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase() - this.header = { - magic: THREE.LoaderUtils.decodeText(new Uint8Array(data.slice(0, 4))), - version: headerView.getUint32(4, true), - length: headerView.getUint32(8, true), - } + threeAttributeMap[threeAttributeName] = gltfAttributeMap[attributeName] + } - if (this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC) { - throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.') - } else if (this.header.version < 2.0) { - throw new Error('THREE.GLTFLoader: Legacy binary file detected.') - } + for (attributeName in primitive.attributes) { + var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase() - var chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH) - var chunkIndex = 0 + if (gltfAttributeMap[attributeName] !== undefined) { + var accessorDef = json.accessors[primitive.attributes[attributeName]] + var componentType = WEBGL_COMPONENT_TYPES[accessorDef.componentType] - while (chunkIndex < chunkView.byteLength) { - var chunkLength = chunkView.getUint32(chunkIndex, true) - chunkIndex += 4 + attributeTypeMap[threeAttributeName] = componentType + attributeNormalizedMap[threeAttributeName] = accessorDef.normalized === true + } + } - var chunkType = chunkView.getUint32(chunkIndex, true) - chunkIndex += 4 + return parser.getDependency('bufferView', bufferViewIndex).then(function (bufferView) { + return new Promise(function (resolve) { + dracoLoader.decodeDracoFile( + bufferView, + function (geometry) { + for (var attributeName in geometry.attributes) { + var attribute = geometry.attributes[attributeName] + var normalized = attributeNormalizedMap[attributeName] - if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) { - var contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength) - this.content = THREE.LoaderUtils.decodeText(contentArray) - } else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) { - var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex - this.body = data.slice(byteOffset, byteOffset + chunkLength) - } + if (normalized !== undefined) attribute.normalized = normalized + } - // Clients must ignore chunks with unknown types. + resolve(geometry) + }, + threeAttributeMap, + attributeTypeMap + ) + }) + }) +} - chunkIndex += chunkLength - } +/** + * Texture Transform Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform + */ +function GLTFTextureTransformExtension() { + this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM +} - if (this.content === null) { - throw new Error('THREE.GLTFLoader: JSON content not found.') - } - } +GLTFTextureTransformExtension.prototype.extendTexture = function (texture, transform) { + texture = texture.clone() - /** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ - function GLTFDracoMeshCompressionExtension(json, dracoLoader) { - if (!dracoLoader) { - throw new Error('THREE.GLTFLoader: No DRACOLoader instance provided.') - } + if (transform.offset !== undefined) { + texture.offset.fromArray(transform.offset) + } - this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION - this.json = json - this.dracoLoader = dracoLoader + if (transform.rotation !== undefined) { + texture.rotation = transform.rotation } - GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function (primitive, parser) { - var json = this.json - var dracoLoader = this.dracoLoader - var bufferViewIndex = primitive.extensions[this.name].bufferView - var gltfAttributeMap = primitive.extensions[this.name].attributes - var threeAttributeMap = {} - var attributeNormalizedMap = {} - var attributeTypeMap = {} + if (transform.scale !== undefined) { + texture.repeat.fromArray(transform.scale) + } - for (var attributeName in gltfAttributeMap) { - var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase() + if (transform.texCoord !== undefined) { + console.warn('THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.') + } - threeAttributeMap[threeAttributeName] = gltfAttributeMap[attributeName] - } + texture.needsUpdate = true - for (attributeName in primitive.attributes) { - var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase() + return texture +} - if (gltfAttributeMap[attributeName] !== undefined) { - var accessorDef = json.accessors[primitive.attributes[attributeName]] - var componentType = WEBGL_COMPONENT_TYPES[accessorDef.componentType] +/** + * Specular-Glossiness Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness + */ - attributeTypeMap[threeAttributeName] = componentType - attributeNormalizedMap[threeAttributeName] = accessorDef.normalized === true - } - } +/** + * A sub class of THREE.StandardMaterial with some of the functionality + * changed via the `onBeforeCompile` callback + * @pailhead + */ - return parser.getDependency('bufferView', bufferViewIndex).then(function (bufferView) { - return new Promise(function (resolve) { - dracoLoader.decodeDracoFile( - bufferView, - function (geometry) { - for (var attributeName in geometry.attributes) { - var attribute = geometry.attributes[attributeName] - var normalized = attributeNormalizedMap[attributeName] +function GLTFMeshStandardSGMaterial(params) { + THREE.MeshStandardMaterial.call(this) - if (normalized !== undefined) attribute.normalized = normalized - } + this.isGLTFSpecularGlossinessMaterial = true - resolve(geometry) - }, - threeAttributeMap, - attributeTypeMap - ) - }) - }) - } + //various chunks that need replacing + var specularMapParsFragmentChunk = ['#ifdef USE_SPECULARMAP', ' uniform sampler2D specularMap;', '#endif'].join('\n') - /** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ - function GLTFTextureTransformExtension() { - this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM - } + var glossinessMapParsFragmentChunk = ['#ifdef USE_GLOSSINESSMAP', ' uniform sampler2D glossinessMap;', '#endif'].join( + '\n' + ) - GLTFTextureTransformExtension.prototype.extendTexture = function (texture, transform) { - texture = texture.clone() + var specularMapFragmentChunk = [ + 'vec3 specularFactor = specular;', + '#ifdef USE_SPECULARMAP', + ' vec4 texelSpecular = texture2D( specularMap, vUv );', + ' texelSpecular = sRGBToLinear( texelSpecular );', + ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' specularFactor *= texelSpecular.rgb;', + '#endif', + ].join('\n') - if (transform.offset !== undefined) { - texture.offset.fromArray(transform.offset) - } + 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') - if (transform.rotation !== undefined) { - texture.rotation = transform.rotation - } + var lightPhysicalFragmentChunk = [ + 'PhysicalMaterial material;', + 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', + 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', + 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', + 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', + 'material.specularRoughness += geometryRoughness;', + 'material.specularRoughness = min( material.specularRoughness, 1.0 );', + 'material.specularColor = specularFactor;', + ].join('\n') - if (transform.scale !== undefined) { - texture.repeat.fromArray(transform.scale) - } + var uniforms = { + specular: { value: new THREE.Color().setHex(0xffffff) }, + glossiness: { value: 1 }, + specularMap: { value: null }, + glossinessMap: { value: null }, + } - if (transform.texCoord !== undefined) { - console.warn('THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.') - } + this._extraUniforms = uniforms - texture.needsUpdate = true - - return texture - } - - /** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ - - /** - * A sub class of THREE.StandardMaterial with some of the functionality - * changed via the `onBeforeCompile` callback - * @pailhead - */ - - function GLTFMeshStandardSGMaterial(params) { - THREE.MeshStandardMaterial.call(this) - - this.isGLTFSpecularGlossinessMaterial = true - - //various chunks that need replacing - 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 );', - ' texelSpecular = sRGBToLinear( texelSpecular );', - ' // 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 * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', - 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', - 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', - 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', - 'material.specularRoughness += geometryRoughness;', - 'material.specularRoughness = min( material.specularRoughness, 1.0 );', - 'material.specularColor = specularFactor;', - ].join('\n') - - var uniforms = { - specular: { value: new THREE.Color().setHex(0xffffff) }, - glossiness: { value: 1 }, - specularMap: { value: null }, - glossinessMap: { value: null }, + this.onBeforeCompile = function (shader) { + for (var uniformName in uniforms) { + shader.uniforms[uniformName] = uniforms[uniformName] } - this._extraUniforms = uniforms - - this.onBeforeCompile = function (shader) { - for (var uniformName in uniforms) { - shader.uniforms[uniformName] = uniforms[uniformName] - } + shader.fragmentShader = shader.fragmentShader + .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) + } - shader.fragmentShader = shader.fragmentShader - .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) - } + Object.defineProperties(this, { + specular: { + get: function () { + return uniforms.specular.value + }, + set: function (v) { + uniforms.specular.value = v + }, + }, - Object.defineProperties(this, { - specular: { - get: function () { - return uniforms.specular.value - }, - set: function (v) { - uniforms.specular.value = v - }, + specularMap: { + get: function () { + return uniforms.specularMap.value }, + set: function (v) { + uniforms.specularMap.value = v - specularMap: { - get: function () { - return uniforms.specularMap.value - }, - set: function (v) { - uniforms.specularMap.value = v + if (v) { + this.defines.USE_SPECULARMAP = '' // USE_UV is set by the renderer for specular maps + } else { + delete this.defines.USE_SPECULARMAP + } + }, + }, - if (v) { - this.defines.USE_SPECULARMAP = '' // USE_UV is set by the renderer for specular maps - } else { - delete this.defines.USE_SPECULARMAP - } - }, + glossiness: { + get: function () { + return uniforms.glossiness.value }, + set: function (v) { + uniforms.glossiness.value = v + }, + }, - glossiness: { - get: function () { - return uniforms.glossiness.value - }, - set: function (v) { - uniforms.glossiness.value = v - }, + glossinessMap: { + get: function () { + return uniforms.glossinessMap.value }, + set: function (v) { + uniforms.glossinessMap.value = v - glossinessMap: { - get: function () { - return uniforms.glossinessMap.value - }, - set: function (v) { - uniforms.glossinessMap.value = v - - if (v) { - this.defines.USE_GLOSSINESSMAP = '' - this.defines.USE_UV = '' - } else { - delete this.defines.USE_GLOSSINESSMAP - delete this.defines.USE_UV - } - }, + if (v) { + this.defines.USE_GLOSSINESSMAP = '' + this.defines.USE_UV = '' + } else { + delete this.defines.USE_GLOSSINESSMAP + delete this.defines.USE_UV + } }, - }) + }, + }) - delete this.metalness - delete this.roughness - delete this.metalnessMap - delete this.roughnessMap + delete this.metalness + delete this.roughness + delete this.metalnessMap + delete this.roughnessMap + + this.setValues(params) +} + +GLTFMeshStandardSGMaterial.prototype = Object.create(THREE.MeshStandardMaterial.prototype) +GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial + +GLTFMeshStandardSGMaterial.prototype.copy = function (source) { + THREE.MeshStandardMaterial.prototype.copy.call(this, source) + this.specularMap = source.specularMap + this.specular.copy(source.specular) + this.glossinessMap = source.glossinessMap + this.glossiness = source.glossiness + delete this.metalness + delete this.roughness + delete this.metalnessMap + delete this.roughnessMap + return this +} + +function GLTFMaterialsPbrSpecularGlossinessExtension() { + return { + name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS, + + specularGlossinessParams: [ + 'color', + 'map', + 'lightMap', + 'lightMapIntensity', + 'aoMap', + 'aoMapIntensity', + 'emissive', + 'emissiveIntensity', + 'emissiveMap', + 'bumpMap', + 'bumpScale', + 'normalMap', + 'normalMapType', + 'displacementMap', + 'displacementScale', + 'displacementBias', + 'specularMap', + 'specular', + 'glossinessMap', + 'glossiness', + 'alphaMap', + 'envMap', + 'envMapIntensity', + 'refractionRatio', + ], + + getMaterialType: function () { + return GLTFMeshStandardSGMaterial + }, - this.setValues(params) - } + extendParams: function (materialParams, materialDef, parser) { + var pbrSpecularGlossiness = materialDef.extensions[this.name] - GLTFMeshStandardSGMaterial.prototype = Object.create(THREE.MeshStandardMaterial.prototype) - GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial + materialParams.color = new THREE.Color(1.0, 1.0, 1.0) + materialParams.opacity = 1.0 - GLTFMeshStandardSGMaterial.prototype.copy = function (source) { - THREE.MeshStandardMaterial.prototype.copy.call(this, source) - this.specularMap = source.specularMap - this.specular.copy(source.specular) - this.glossinessMap = source.glossinessMap - this.glossiness = source.glossiness - delete this.metalness - delete this.roughness - delete this.metalnessMap - delete this.roughnessMap - return this - } + var pending = [] - function GLTFMaterialsPbrSpecularGlossinessExtension() { - return { - name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS, - - specularGlossinessParams: [ - 'color', - 'map', - 'lightMap', - 'lightMapIntensity', - 'aoMap', - 'aoMapIntensity', - 'emissive', - 'emissiveIntensity', - 'emissiveMap', - 'bumpMap', - 'bumpScale', - 'normalMap', - 'normalMapType', - 'displacementMap', - 'displacementScale', - 'displacementBias', - 'specularMap', - 'specular', - 'glossinessMap', - 'glossiness', - 'alphaMap', - 'envMap', - 'envMapIntensity', - 'refractionRatio', - ], - - getMaterialType: function () { - return GLTFMeshStandardSGMaterial - }, + if (Array.isArray(pbrSpecularGlossiness.diffuseFactor)) { + var array = pbrSpecularGlossiness.diffuseFactor - extendParams: function (materialParams, materialDef, parser) { - var pbrSpecularGlossiness = materialDef.extensions[this.name] + materialParams.color.fromArray(array) + materialParams.opacity = array[3] + } - materialParams.color = new THREE.Color(1.0, 1.0, 1.0) - materialParams.opacity = 1.0 + materialParams.emissive = new THREE.Color(0.0, 0.0, 0.0) + materialParams.glossiness = + pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0 + materialParams.specular = new THREE.Color(1.0, 1.0, 1.0) - var pending = [] + if (Array.isArray(pbrSpecularGlossiness.specularFactor)) { + materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor) + } - if (Array.isArray(pbrSpecularGlossiness.diffuseFactor)) { - var array = pbrSpecularGlossiness.diffuseFactor + return Promise.all(pending) + }, - materialParams.color.fromArray(array) - materialParams.opacity = array[3] - } + createMaterial: function (materialParams) { + var material = new GLTFMeshStandardSGMaterial(materialParams) + material.fog = true - materialParams.emissive = new THREE.Color(0.0, 0.0, 0.0) - materialParams.glossiness = - pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0 - materialParams.specular = new THREE.Color(1.0, 1.0, 1.0) + material.color = materialParams.color - if (Array.isArray(pbrSpecularGlossiness.specularFactor)) { - materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor) - } + material.map = materialParams.map === undefined ? null : materialParams.map - return Promise.all(pending) - }, + material.lightMap = null + material.lightMapIntensity = 1.0 - createMaterial: function (materialParams) { - var material = new GLTFMeshStandardSGMaterial(materialParams) - material.fog = true + material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap + material.aoMapIntensity = 1.0 - material.color = materialParams.color + material.emissive = materialParams.emissive + material.emissiveIntensity = 1.0 + material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap - material.map = materialParams.map === undefined ? null : materialParams.map + material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap + material.bumpScale = 1 - material.lightMap = null - material.lightMapIntensity = 1.0 + material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap + material.normalMapType = THREE.TangentSpaceNormalMap - material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap - material.aoMapIntensity = 1.0 + if (materialParams.normalScale) material.normalScale = materialParams.normalScale - material.emissive = materialParams.emissive - material.emissiveIntensity = 1.0 - material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap + material.displacementMap = null + material.displacementScale = 1 + material.displacementBias = 0 - material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap - material.bumpScale = 1 + material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap + material.specular = materialParams.specular - material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap - material.normalMapType = THREE.TangentSpaceNormalMap + material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap + material.glossiness = materialParams.glossiness - if (materialParams.normalScale) material.normalScale = materialParams.normalScale + material.alphaMap = null - material.displacementMap = null - material.displacementScale = 1 - material.displacementBias = 0 + material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap + material.envMapIntensity = 1.0 - material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap - material.specular = materialParams.specular + material.refractionRatio = 0.98 - material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap - material.glossiness = materialParams.glossiness + return material + }, + } +} - material.alphaMap = null +/** + * Mesh Quantization Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization + */ +function GLTFMeshQuantizationExtension() { + this.name = EXTENSIONS.KHR_MESH_QUANTIZATION +} + +/*********************************/ +/********** INTERPOLATION ********/ +/*********************************/ + +// Spline Interpolation +// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation +function GLTFCubicSplineInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { + THREE.Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer) +} + +GLTFCubicSplineInterpolant.prototype = Object.create(THREE.Interpolant.prototype) +GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant + +GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function (index) { + // Copies a sample value to the result buffer. See description of glTF + // CUBICSPLINE values layout in interpolate_() function below. + + var result = this.resultBuffer, + values = this.sampleValues, + valueSize = this.valueSize, + offset = index * valueSize * 3 + valueSize + + for (var i = 0; i !== valueSize; i++) { + result[i] = values[offset + i] + } + + return result +} + +GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_ + +GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_ + +GLTFCubicSplineInterpolant.prototype.interpolate_ = function (i1, t0, t, t1) { + var result = this.resultBuffer + var values = this.sampleValues + var stride = this.valueSize + + var stride2 = stride * 2 + var stride3 = stride * 3 + + var td = t1 - t0 + + var p = (t - t0) / td + var pp = p * p + var ppp = pp * p + + var offset1 = i1 * stride3 + var offset0 = offset1 - stride3 + + var s2 = -2 * ppp + 3 * pp + var s3 = ppp - pp + var s0 = 1 - s2 + var s1 = s3 - pp + p + + // Layout of keyframe output values for CUBICSPLINE animations: + // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] + for (var i = 0; i !== stride; i++) { + var p0 = values[offset0 + i + stride] // splineVertex_k + var m0 = values[offset0 + i + stride2] * td // outTangent_k * (t_k+1 - t_k) + var p1 = values[offset1 + i + stride] // splineVertex_k+1 + var m1 = values[offset1 + i] * td // inTangent_k+1 * (t_k+1 - t_k) + + result[i] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1 + } + + return result +} + +/*********************************/ +/********** 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, + POINTS: 0, + LINES: 1, + LINE_LOOP: 2, + LINE_STRIP: 3, + TRIANGLES: 4, + TRIANGLE_STRIP: 5, + TRIANGLE_FAN: 6, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, +} + +var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array, +} + +var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipmapNearestFilter, + 9985: THREE.LinearMipmapNearestFilter, + 9986: THREE.NearestMipmapLinearFilter, + 9987: THREE.LinearMipmapLinearFilter, +} + +var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping, +} + +var WEBGL_TYPE_SIZES = { + SCALAR: 1, + VEC2: 2, + VEC3: 3, + VEC4: 4, + MAT2: 4, + MAT3: 9, + MAT4: 16, +} + +var ATTRIBUTES = { + POSITION: 'position', + NORMAL: 'normal', + TANGENT: 'tangent', + TEXCOORD_0: 'uv', + TEXCOORD_1: 'uv2', + COLOR_0: 'color', + WEIGHTS_0: 'skinWeight', + JOINTS_0: 'skinIndex', +} + +var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion', + weights: 'morphTargetInfluences', +} + +var INTERPOLATION = { + CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each + // keyframe track will be initialized with a default interpolation type, then modified. + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete, +} + +var ALPHA_MODES = { + OPAQUE: 'OPAQUE', + MASK: 'MASK', + BLEND: 'BLEND', +} + +/* UTILITY FUNCTIONS */ + +function resolveURL(url, path) { + // Invalid URL + if (typeof url !== 'string' || url === '') return '' + + // Host Relative URL + if (/^https?:\/\//i.test(path) && /^\//.test(url)) { + path = path.replace(/(^https?:\/\/[^\/]+).*/i, '$1') + } + + // 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 +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material + */ +function createDefaultMaterial(cache) { + if (cache['DefaultMaterial'] === undefined) { + cache['DefaultMaterial'] = new THREE.MeshStandardMaterial({ + color: 0xffffff, + emissive: 0x000000, + metalness: 1, + roughness: 1, + transparent: false, + depthTest: true, + side: THREE.FrontSide, + }) + } - material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap - material.envMapIntensity = 1.0 + return cache['DefaultMaterial'] +} - material.refractionRatio = 0.98 +function addUnknownExtensionsToUserData(knownExtensions, object, objectDef) { + // Add unknown glTF extensions to an object's userData. - return material - }, + for (var name in objectDef.extensions) { + if (knownExtensions[name] === undefined) { + object.userData.gltfExtensions = object.userData.gltfExtensions || {} + object.userData.gltfExtensions[name] = objectDef.extensions[name] } } +} - /** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ - function GLTFMeshQuantizationExtension() { - this.name = EXTENSIONS.KHR_MESH_QUANTIZATION +/** + * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry} object + * @param {GLTF.definition} gltfDef + */ +function assignExtrasToUserData(object, gltfDef) { + if (gltfDef.extras !== undefined) { + if (typeof gltfDef.extras === 'object') { + Object.assign(object.userData, gltfDef.extras) + } else { + console.warn('THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras) + } } +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets + * + * @param {THREE.BufferGeometry} geometry + * @param {Array} targets + * @param {GLTFParser} parser + * @return {Promise} + */ +function addMorphTargets(geometry, targets, parser) { + var hasMorphPosition = false + var hasMorphNormal = false - /*********************************/ - /********** INTERPOLATION ********/ - /*********************************/ + for (var i = 0, il = targets.length; i < il; i++) { + var target = targets[i] - // Spline Interpolation - // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - function GLTFCubicSplineInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) { - THREE.Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer) + if (target.POSITION !== undefined) hasMorphPosition = true + if (target.NORMAL !== undefined) hasMorphNormal = true + + if (hasMorphPosition && hasMorphNormal) break } - GLTFCubicSplineInterpolant.prototype = Object.create(THREE.Interpolant.prototype) - GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant + if (!hasMorphPosition && !hasMorphNormal) return Promise.resolve(geometry) - GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function (index) { - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. + var pendingPositionAccessors = [] + var pendingNormalAccessors = [] - var result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize + for (var i = 0, il = targets.length; i < il; i++) { + var target = targets[i] - for (var i = 0; i !== valueSize; i++) { - result[i] = values[offset + i] - } + if (hasMorphPosition) { + var pendingAccessor = + target.POSITION !== undefined ? parser.getDependency('accessor', target.POSITION) : geometry.attributes.position - return result - } + pendingPositionAccessors.push(pendingAccessor) + } - GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_ + if (hasMorphNormal) { + var pendingAccessor = + target.NORMAL !== undefined ? parser.getDependency('accessor', target.NORMAL) : geometry.attributes.normal - GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_ + pendingNormalAccessors.push(pendingAccessor) + } + } - GLTFCubicSplineInterpolant.prototype.interpolate_ = function (i1, t0, t, t1) { - var result = this.resultBuffer - var values = this.sampleValues - var stride = this.valueSize + return Promise.all([Promise.all(pendingPositionAccessors), Promise.all(pendingNormalAccessors)]).then(function ( + accessors + ) { + var morphPositions = accessors[0] + var morphNormals = accessors[1] - var stride2 = stride * 2 - var stride3 = stride * 3 + if (hasMorphPosition) geometry.morphAttributes.position = morphPositions + if (hasMorphNormal) geometry.morphAttributes.normal = morphNormals + geometry.morphTargetsRelative = true - var td = t1 - t0 + return geometry + }) +} - var p = (t - t0) / td - var pp = p * p - var ppp = pp * p +/** + * @param {THREE.Mesh} mesh + * @param {GLTF.Mesh} meshDef + */ +function updateMorphTargets(mesh, meshDef) { + mesh.updateMorphTargets() - var offset1 = i1 * stride3 - var offset0 = offset1 - stride3 + if (meshDef.weights !== undefined) { + for (var i = 0, il = meshDef.weights.length; i < il; i++) { + mesh.morphTargetInfluences[i] = meshDef.weights[i] + } + } - var s2 = -2 * ppp + 3 * pp - var s3 = ppp - pp - var s0 = 1 - s2 - var s1 = s3 - pp + p + // .extras has user-defined data, so check that .extras.targetNames is an array. + if (meshDef.extras && Array.isArray(meshDef.extras.targetNames)) { + var targetNames = meshDef.extras.targetNames - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for (var i = 0; i !== stride; i++) { - var p0 = values[offset0 + i + stride] // splineVertex_k - var m0 = values[offset0 + i + stride2] * td // outTangent_k * (t_k+1 - t_k) - var p1 = values[offset1 + i + stride] // splineVertex_k+1 - var m1 = values[offset1 + i] * td // inTangent_k+1 * (t_k+1 - t_k) + if (mesh.morphTargetInfluences.length === targetNames.length) { + mesh.morphTargetDictionary = {} - result[i] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1 + for (var i = 0, il = targetNames.length; i < il; i++) { + mesh.morphTargetDictionary[targetNames[i]] = i + } + } else { + console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.') } + } +} - return result +function createPrimitiveKey(primitiveDef) { + var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION] + var geometryKey + + if (dracoExtension) { + geometryKey = + 'draco:' + + dracoExtension.bufferView + + ':' + + dracoExtension.indices + + ':' + + createAttributesKey(dracoExtension.attributes) + } else { + geometryKey = primitiveDef.indices + ':' + createAttributesKey(primitiveDef.attributes) + ':' + primitiveDef.mode } - /*********************************/ - /********** 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, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123, - } - - var WEBGL_COMPONENT_TYPES = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array, - } - - var WEBGL_FILTERS = { - 9728: THREE.NearestFilter, - 9729: THREE.LinearFilter, - 9984: THREE.NearestMipmapNearestFilter, - 9985: THREE.LinearMipmapNearestFilter, - 9986: THREE.NearestMipmapLinearFilter, - 9987: THREE.LinearMipmapLinearFilter, - } - - var WEBGL_WRAPPINGS = { - 33071: THREE.ClampToEdgeWrapping, - 33648: THREE.MirroredRepeatWrapping, - 10497: THREE.RepeatWrapping, - } - - var WEBGL_TYPE_SIZES = { - SCALAR: 1, - VEC2: 2, - VEC3: 3, - VEC4: 4, - MAT2: 4, - MAT3: 9, - MAT4: 16, - } - - var ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv2', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', - } - - var PATH_PROPERTIES = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences', - } - - var INTERPOLATION = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: THREE.InterpolateLinear, - STEP: THREE.InterpolateDiscrete, - } - - var ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND', - } - - /* UTILITY FUNCTIONS */ - - function resolveURL(url, path) { - // Invalid URL - if (typeof url !== 'string' || url === '') return '' - - // Host Relative URL - if (/^https?:\/\//i.test(path) && /^\//.test(url)) { - path = path.replace(/(^https?:\/\/[^\/]+).*/i, '$1') - } - - // Absolute URL http://,https://,// - if (/^(https?:)?\/\//i.test(url)) return url + return geometryKey +} - // Data URI - if (/^data:.*,.*$/i.test(url)) return url +function createAttributesKey(attributes) { + var attributesKey = '' - // Blob URL - if (/^blob:.*$/i.test(url)) return url + var keys = Object.keys(attributes).sort() - // Relative URL - return path + url + for (var i = 0, il = keys.length; i < il; i++) { + attributesKey += keys[i] + ':' + attributes[keys[i]] + ';' } - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ - function createDefaultMaterial(cache) { - if (cache['DefaultMaterial'] === undefined) { - cache['DefaultMaterial'] = new THREE.MeshStandardMaterial({ - color: 0xffffff, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: THREE.FrontSide, - }) - } + return attributesKey +} - return cache['DefaultMaterial'] - } +/* GLTF PARSER */ - function addUnknownExtensionsToUserData(knownExtensions, object, objectDef) { - // Add unknown glTF extensions to an object's userData. +function GLTFParser(json, options) { + this.json = json || {} + this.extensions = {} + this.plugins = {} + this.options = options || {} - for (var name in objectDef.extensions) { - if (knownExtensions[name] === undefined) { - object.userData.gltfExtensions = object.userData.gltfExtensions || {} - object.userData.gltfExtensions[name] = objectDef.extensions[name] - } - } - } + // loader object cache + this.cache = new GLTFRegistry() - /** - * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ - function assignExtrasToUserData(object, gltfDef) { - if (gltfDef.extras !== undefined) { - if (typeof gltfDef.extras === 'object') { - Object.assign(object.userData, gltfDef.extras) - } else { - console.warn('THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras) - } - } - } + // associations between Three.js objects and glTF elements + this.associations = new Map() - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {THREE.BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ - function addMorphTargets(geometry, targets, parser) { - var hasMorphPosition = false - var hasMorphNormal = false + // BufferGeometry caching + this.primitiveCache = {} - for (var i = 0, il = targets.length; i < il; i++) { - var target = targets[i] + // Object3D instance caches + this.meshCache = { refs: {}, uses: {} } + this.cameraCache = { refs: {}, uses: {} } + this.lightCache = { refs: {}, uses: {} } - if (target.POSITION !== undefined) hasMorphPosition = true - if (target.NORMAL !== undefined) hasMorphNormal = true + // Track node names, to ensure no duplicates + this.nodeNamesUsed = {} - if (hasMorphPosition && hasMorphNormal) break - } + // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the + // expensive work of uploading a texture to the GPU off the main thread. + if (typeof createImageBitmap !== 'undefined' && /Firefox/.test(navigator.userAgent) === false) { + this.textureLoader = new THREE.ImageBitmapLoader(this.options.manager) + } else { + this.textureLoader = new THREE.TextureLoader(this.options.manager) + } - if (!hasMorphPosition && !hasMorphNormal) return Promise.resolve(geometry) + this.textureLoader.setCrossOrigin(this.options.crossOrigin) - var pendingPositionAccessors = [] - var pendingNormalAccessors = [] + this.fileLoader = new THREE.FileLoader(this.options.manager) + this.fileLoader.setResponseType('arraybuffer') - for (var i = 0, il = targets.length; i < il; i++) { - var target = targets[i] + if (this.options.crossOrigin === 'use-credentials') { + this.fileLoader.setWithCredentials(true) + } +} - if (hasMorphPosition) { - var pendingAccessor = - target.POSITION !== undefined - ? parser.getDependency('accessor', target.POSITION) - : geometry.attributes.position +GLTFParser.prototype.setExtensions = function (extensions) { + this.extensions = extensions +} - pendingPositionAccessors.push(pendingAccessor) - } +GLTFParser.prototype.setPlugins = function (plugins) { + this.plugins = plugins +} - if (hasMorphNormal) { - var pendingAccessor = - target.NORMAL !== undefined ? parser.getDependency('accessor', target.NORMAL) : geometry.attributes.normal +GLTFParser.prototype.parse = function (onLoad, onError) { + var parser = this + var json = this.json + var extensions = this.extensions - pendingNormalAccessors.push(pendingAccessor) + // Clear the loader cache + this.cache.removeAll() + + // Mark the special nodes/meshes in json for efficient parse + this._invokeAll(function (ext) { + return ext._markDefs && ext._markDefs() + }) + + Promise.all([this.getDependencies('scene'), this.getDependencies('animation'), this.getDependencies('camera')]) + .then(function (dependencies) { + var result = { + scene: dependencies[0][json.scene || 0], + scenes: dependencies[0], + animations: dependencies[1], + cameras: dependencies[2], + asset: json.asset, + parser: parser, + userData: {}, } - } - return Promise.all([Promise.all(pendingPositionAccessors), Promise.all(pendingNormalAccessors)]).then(function ( - accessors - ) { - var morphPositions = accessors[0] - var morphNormals = accessors[1] + addUnknownExtensionsToUserData(extensions, result, json) - if (hasMorphPosition) geometry.morphAttributes.position = morphPositions - if (hasMorphNormal) geometry.morphAttributes.normal = morphNormals - geometry.morphTargetsRelative = true + assignExtrasToUserData(result, json) - return geometry + onLoad(result) }) - } + .catch(onError) +} - /** - * @param {THREE.Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ - function updateMorphTargets(mesh, meshDef) { - mesh.updateMorphTargets() +/** + * Marks the special nodes/meshes in json for efficient parse. + */ +GLTFParser.prototype._markDefs = function () { + var nodeDefs = this.json.nodes || [] + var skinDefs = this.json.skins || [] + var meshDefs = this.json.meshes || [] - if (meshDef.weights !== undefined) { - for (var i = 0, il = meshDef.weights.length; i < il; i++) { - mesh.morphTargetInfluences[i] = meshDef.weights[i] - } + // Nothing in the node definition indicates whether it is a Bone or an + // Object3D. Use the skins' joint references to mark bones. + for (var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex++) { + var joints = skinDefs[skinIndex].joints + + for (var i = 0, il = joints.length; i < il; i++) { + nodeDefs[joints[i]].isBone = true } + } - // .extras has user-defined data, so check that .extras.targetNames is an array. - if (meshDef.extras && Array.isArray(meshDef.extras.targetNames)) { - var targetNames = meshDef.extras.targetNames + // Iterate over all nodes, marking references to shared resources, + // as well as skeleton joints. + for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) { + var nodeDef = nodeDefs[nodeIndex] - if (mesh.morphTargetInfluences.length === targetNames.length) { - mesh.morphTargetDictionary = {} + if (nodeDef.mesh !== undefined) { + this._addNodeRef(this.meshCache, nodeDef.mesh) - for (var i = 0, il = targetNames.length; i < il; i++) { - mesh.morphTargetDictionary[targetNames[i]] = i - } - } else { - console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.') + // Nothing in the mesh definition indicates whether it is + // a SkinnedMesh or Mesh. Use the node's mesh reference + // to mark SkinnedMesh if node has skin. + if (nodeDef.skin !== undefined) { + meshDefs[nodeDef.mesh].isSkinnedMesh = true } } - } - function createPrimitiveKey(primitiveDef) { - var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION] - var geometryKey - - if (dracoExtension) { - geometryKey = - 'draco:' + - dracoExtension.bufferView + - ':' + - dracoExtension.indices + - ':' + - createAttributesKey(dracoExtension.attributes) - } else { - geometryKey = primitiveDef.indices + ':' + createAttributesKey(primitiveDef.attributes) + ':' + primitiveDef.mode + if (nodeDef.camera !== undefined) { + this._addNodeRef(this.cameraCache, nodeDef.camera) } - - return geometryKey } +} - function createAttributesKey(attributes) { - var attributesKey = '' - - var keys = Object.keys(attributes).sort() - - for (var i = 0, il = keys.length; i < il; i++) { - attributesKey += keys[i] + ':' + attributes[keys[i]] + ';' - } +/** + * Counts references to shared node / Object3D resources. These resources + * can be reused, or "instantiated", at multiple nodes in the scene + * hierarchy. Mesh, Camera, and Light instances are instantiated and must + * be marked. Non-scenegraph resources (like Materials, Geometries, and + * Textures) can be reused directly and are not marked here. + * + * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. + */ +GLTFParser.prototype._addNodeRef = function (cache, index) { + if (index === undefined) return - return attributesKey + if (cache.refs[index] === undefined) { + cache.refs[index] = cache.uses[index] = 0 } - /* GLTF PARSER */ + cache.refs[index]++ +} - function GLTFParser(json, options) { - this.json = json || {} - this.extensions = {} - this.plugins = {} - this.options = options || {} +/** Returns a reference to a shared resource, cloning it if necessary. */ +GLTFParser.prototype._getNodeRef = function (cache, index, object) { + if (cache.refs[index] <= 1) return object - // loader object cache - this.cache = new GLTFRegistry() + var ref = object.clone() - // associations between Three.js objects and glTF elements - this.associations = new Map() + ref.name += '_instance_' + cache.uses[index]++ - // BufferGeometry caching - this.primitiveCache = {} + return ref +} - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} } - this.cameraCache = { refs: {}, uses: {} } - this.lightCache = { refs: {}, uses: {} } +GLTFParser.prototype._invokeOne = function (func) { + var extensions = Object.values(this.plugins) + extensions.push(this) - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {} + for (var i = 0; i < extensions.length; i++) { + var result = func(extensions[i]) - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - if (typeof createImageBitmap !== 'undefined' && /Firefox/.test(navigator.userAgent) === false) { - this.textureLoader = new THREE.ImageBitmapLoader(this.options.manager) - } else { - this.textureLoader = new THREE.TextureLoader(this.options.manager) - } - - this.textureLoader.setCrossOrigin(this.options.crossOrigin) - - this.fileLoader = new THREE.FileLoader(this.options.manager) - this.fileLoader.setResponseType('arraybuffer') - - if (this.options.crossOrigin === 'use-credentials') { - this.fileLoader.setWithCredentials(true) - } + if (result) return result } +} - GLTFParser.prototype.setExtensions = function (extensions) { - this.extensions = extensions - } +GLTFParser.prototype._invokeAll = function (func) { + var extensions = Object.values(this.plugins) + extensions.unshift(this) - GLTFParser.prototype.setPlugins = function (plugins) { - this.plugins = plugins - } + var pending = [] - GLTFParser.prototype.parse = function (onLoad, onError) { - var parser = this - var json = this.json - var extensions = this.extensions + for (var i = 0; i < extensions.length; i++) { + var result = func(extensions[i]) - // Clear the loader cache - this.cache.removeAll() + if (result) pending.push(result) + } - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll(function (ext) { - return ext._markDefs && ext._markDefs() - }) + return pending +} - Promise.all([this.getDependencies('scene'), this.getDependencies('animation'), this.getDependencies('camera')]) - .then(function (dependencies) { - var result = { - scene: dependencies[0][json.scene || 0], - scenes: dependencies[0], - animations: dependencies[1], - cameras: dependencies[2], - asset: json.asset, - parser: parser, - userData: {}, - } +/** + * Requests the specified dependency asynchronously, with caching. + * @param {string} type + * @param {number} index + * @return {Promise} + */ +GLTFParser.prototype.getDependency = function (type, index) { + var cacheKey = type + ':' + index + var dependency = this.cache.get(cacheKey) - addUnknownExtensionsToUserData(extensions, result, json) + if (!dependency) { + switch (type) { + case 'scene': + dependency = this.loadScene(index) + break - assignExtrasToUserData(result, json) + case 'node': + dependency = this.loadNode(index) + break - onLoad(result) - }) - .catch(onError) - } + case 'mesh': + dependency = this._invokeOne(function (ext) { + return ext.loadMesh && ext.loadMesh(index) + }) + break - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - GLTFParser.prototype._markDefs = function () { - var nodeDefs = this.json.nodes || [] - var skinDefs = this.json.skins || [] - var meshDefs = this.json.meshes || [] + case 'accessor': + dependency = this.loadAccessor(index) + break - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for (var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex++) { - var joints = skinDefs[skinIndex].joints + case 'bufferView': + dependency = Promise.resolve(new Float32Array(0)) + break - for (var i = 0, il = joints.length; i < il; i++) { - nodeDefs[joints[i]].isBone = true - } - } + case 'buffer': + dependency = Promise.resolve(new Float32Array(0)) + break - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) { - var nodeDef = nodeDefs[nodeIndex] + case 'material': + dependency = this._invokeOne(function (ext) { + return ext.loadMaterial && ext.loadMaterial(index) + }) + break - if (nodeDef.mesh !== undefined) { - this._addNodeRef(this.meshCache, nodeDef.mesh) + case 'skin': + dependency = this.loadSkin(index) + break - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if (nodeDef.skin !== undefined) { - meshDefs[nodeDef.mesh].isSkinnedMesh = true - } - } + case 'animation': + dependency = this.loadAnimation(index) + break - if (nodeDef.camera !== undefined) { - this._addNodeRef(this.cameraCache, nodeDef.camera) - } - } - } + case 'camera': + dependency = this.loadCamera(index) + break - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - GLTFParser.prototype._addNodeRef = function (cache, index) { - if (index === undefined) return - - if (cache.refs[index] === undefined) { - cache.refs[index] = cache.uses[index] = 0 + default: + throw new Error('Unknown type: ' + type) } - cache.refs[index]++ + this.cache.add(cacheKey, dependency) } - /** Returns a reference to a shared resource, cloning it if necessary. */ - GLTFParser.prototype._getNodeRef = function (cache, index, object) { - if (cache.refs[index] <= 1) return object + return dependency +} - var ref = object.clone() +/** + * Requests all dependencies of the specified type asynchronously, with caching. + * @param {string} type + * @return {Promise>} + */ +GLTFParser.prototype.getDependencies = function (type) { + var dependencies = this.cache.get(type) - ref.name += '_instance_' + cache.uses[index]++ - - return ref - } - - GLTFParser.prototype._invokeOne = function (func) { - var extensions = Object.values(this.plugins) - extensions.push(this) + if (!dependencies) { + var parser = this + var defs = this.json[type + (type === 'mesh' ? 'es' : 's')] || [] - for (var i = 0; i < extensions.length; i++) { - var result = func(extensions[i]) + dependencies = Promise.all( + defs.map(function (def, index) { + return parser.getDependency(type, index) + }) + ) - if (result) return result - } + this.cache.add(type, dependencies) } - GLTFParser.prototype._invokeAll = function (func) { - var extensions = Object.values(this.plugins) - extensions.unshift(this) + return dependencies +} - var pending = [] - - for (var i = 0; i < extensions.length; i++) { - var result = func(extensions[i]) - - if (result) pending.push(result) - } +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views + * @param {number} bufferIndex + * @return {Promise} + */ +GLTFParser.prototype.loadBuffer = function (bufferIndex) { + var bufferDef = this.json.buffers[bufferIndex] + var loader = this.fileLoader - return pending + if (bufferDef.type && bufferDef.type !== 'arraybuffer') { + throw new Error('THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.') } - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - GLTFParser.prototype.getDependency = function (type, index) { - var cacheKey = type + ':' + index - var dependency = this.cache.get(cacheKey) - - if (!dependency) { - switch (type) { - case 'scene': - dependency = this.loadScene(index) - break - - case 'node': - dependency = this.loadNode(index) - break - - case 'mesh': - dependency = this._invokeOne(function (ext) { - return ext.loadMesh && ext.loadMesh(index) - }) - break - - case 'accessor': - dependency = this.loadAccessor(index) - break - - case 'bufferView': - dependency = Promise.resolve(new Float32Array(0)) - break + // If present, GLB container is required to be the first buffer. + if (bufferDef.uri === undefined && bufferIndex === 0) { + return Promise.resolve(this.extensions[EXTENSIONS.KHR_BINARY_GLTF].body) + } - case 'buffer': - dependency = Promise.resolve(new Float32Array(0)) - break + var options = this.options - case 'material': - dependency = this._invokeOne(function (ext) { - return ext.loadMaterial && ext.loadMaterial(index) - }) - break + return new Promise(function (resolve, reject) { + loader.load(resolveURL(bufferDef.uri, options.path), resolve, undefined, function () { + reject(new Error('THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".')) + }) + }) +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views + * @param {number} bufferViewIndex + * @return {Promise} + */ +GLTFParser.prototype.loadBufferView = function (bufferViewIndex) { + var bufferViewDef = this.json.bufferViews[bufferViewIndex] + + return this.getDependency('buffer', bufferViewDef.buffer).then(function (buffer) { + var byteLength = bufferViewDef.byteLength || 0 + var byteOffset = bufferViewDef.byteOffset || 0 + return buffer.slice(byteOffset, byteOffset + byteLength) + }) +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors + * @param {number} accessorIndex + * @return {Promise} + */ +GLTFParser.prototype.loadAccessor = function (accessorIndex) { + var parser = this + var json = this.json + + var accessorDef = this.json.accessors[accessorIndex] + + if (accessorDef.bufferView === undefined && accessorDef.sparse === undefined) { + // Ignore empty accessors, which may be used to declare runtime + // information about attributes coming from another source (e.g. Draco + // compression extension). + return Promise.resolve(null) + } + + var pendingBufferViews = [] + + if (accessorDef.bufferView !== undefined) { + pendingBufferViews.push(this.getDependency('bufferView', accessorDef.bufferView)) + } else { + pendingBufferViews.push(null) + } + + if (accessorDef.sparse !== undefined) { + pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.indices.bufferView)) + pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.values.bufferView)) + } + + return Promise.all(pendingBufferViews).then(function (bufferViews) { + var bufferView = bufferViews[0] + + var itemSize = WEBGL_TYPE_SIZES[accessorDef.type] + var TypedArray = WEBGL_COMPONENT_TYPES[accessorDef.componentType] + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT + var itemBytes = elementBytes * itemSize + var byteOffset = accessorDef.byteOffset || 0 + var byteStride = + accessorDef.bufferView !== undefined ? json.bufferViews[accessorDef.bufferView].byteStride : undefined + var normalized = accessorDef.normalized === true + var array, bufferAttribute + + // The buffer is not interleaved if the stride is the item size in bytes. + if (byteStride && byteStride !== itemBytes) { + // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer + // This makes sure that IBA.count reflects accessor.count properly + var ibSlice = Math.floor(byteOffset / byteStride) + var ibCacheKey = + 'InterleavedBuffer:' + + accessorDef.bufferView + + ':' + + accessorDef.componentType + + ':' + + ibSlice + + ':' + + accessorDef.count + var ib = parser.cache.get(ibCacheKey) - case 'skin': - dependency = this.loadSkin(index) - break + if (!ib) { + array = new TypedArray(bufferView, ibSlice * byteStride, (accessorDef.count * byteStride) / elementBytes) - case 'animation': - dependency = this.loadAnimation(index) - break + // Integer parameters to IB/IBA are in array elements, not bytes. + ib = new THREE.InterleavedBuffer(array, byteStride / elementBytes) - case 'camera': - dependency = this.loadCamera(index) - break + parser.cache.add(ibCacheKey, ib) + } - default: - throw new Error('Unknown type: ' + type) + bufferAttribute = new THREE.InterleavedBufferAttribute( + ib, + itemSize, + (byteOffset % byteStride) / elementBytes, + normalized + ) + } else { + if (bufferView === null) { + array = new TypedArray(accessorDef.count * itemSize) + } else { + array = new TypedArray(bufferView, byteOffset, accessorDef.count * itemSize) } - this.cache.add(cacheKey, dependency) + bufferAttribute = new THREE.BufferAttribute(array, itemSize, normalized) } - return dependency - } - - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - GLTFParser.prototype.getDependencies = function (type) { - var dependencies = this.cache.get(type) + // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors + if (accessorDef.sparse !== undefined) { + var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR + var TypedArrayIndices = WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType] - if (!dependencies) { - var parser = this - var defs = this.json[type + (type === 'mesh' ? 'es' : 's')] || [] + var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0 + var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0 - dependencies = Promise.all( - defs.map(function (def, index) { - return parser.getDependency(type, index) - }) + var sparseIndices = new TypedArrayIndices( + bufferViews[1], + byteOffsetIndices, + accessorDef.sparse.count * itemSizeIndices ) + var sparseValues = new TypedArray(bufferViews[2], byteOffsetValues, accessorDef.sparse.count * itemSize) + + if (bufferView !== null) { + // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. + bufferAttribute = new THREE.BufferAttribute( + bufferAttribute.array.slice(), + bufferAttribute.itemSize, + bufferAttribute.normalized + ) + } - this.cache.add(type, dependencies) - } - - return dependencies - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - GLTFParser.prototype.loadBuffer = function (bufferIndex) { - var bufferDef = this.json.buffers[bufferIndex] - var loader = this.fileLoader + for (var i = 0, il = sparseIndices.length; i < il; i++) { + var index = sparseIndices[i] - if (bufferDef.type && bufferDef.type !== 'arraybuffer') { - throw new Error('THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.') + bufferAttribute.setX(index, sparseValues[i * itemSize]) + if (itemSize >= 2) bufferAttribute.setY(index, sparseValues[i * itemSize + 1]) + if (itemSize >= 3) bufferAttribute.setZ(index, sparseValues[i * itemSize + 2]) + if (itemSize >= 4) bufferAttribute.setW(index, sparseValues[i * itemSize + 3]) + if (itemSize >= 5) throw new Error('THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.') + } } - // If present, GLB container is required to be the first buffer. - if (bufferDef.uri === undefined && bufferIndex === 0) { - return Promise.resolve(this.extensions[EXTENSIONS.KHR_BINARY_GLTF].body) - } + return bufferAttribute + }) +} - var options = this.options +/** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures + * @param {number} textureIndex + * @return {Promise} + */ +GLTFParser.prototype.loadTexture = function (textureIndex) { + return Promise.resolve(new THREE.Texture()) +} - return new Promise(function (resolve, reject) { - loader.load(resolveURL(bufferDef.uri, options.path), resolve, undefined, function () { - reject(new Error('THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".')) - }) - }) - } +/** + * Assigns final material to a Mesh, Line, or Points instance. The instance + * already has a material (generated from the glTF material options alone) + * but reuse of the same glTF material may require multiple threejs materials + * to accomodate different primitive types, defines, etc. New materials will + * be created if necessary, and reused from a cache. + * @param {THREE.Object3D} mesh Mesh, Line, or Points instance. + */ +GLTFParser.prototype.assignFinalMaterial = function (mesh) { + var geometry = mesh.geometry + var material = mesh.material - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - GLTFParser.prototype.loadBufferView = function (bufferViewIndex) { - var bufferViewDef = this.json.bufferViews[bufferViewIndex] + var useVertexTangents = geometry.attributes.tangent !== undefined + var useVertexColors = geometry.attributes.color !== undefined + var useFlatShading = geometry.attributes.normal === undefined + var useSkinning = mesh.isSkinnedMesh === true + var useMorphTargets = Object.keys(geometry.morphAttributes).length > 0 + var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined - return this.getDependency('buffer', bufferViewDef.buffer).then(function (buffer) { - var byteLength = bufferViewDef.byteLength || 0 - var byteOffset = bufferViewDef.byteOffset || 0 - return buffer.slice(byteOffset, byteOffset + byteLength) - }) - } + if (mesh.isPoints) { + var cacheKey = 'PointsMaterial:' + material.uuid - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - GLTFParser.prototype.loadAccessor = function (accessorIndex) { - var parser = this - var json = this.json + var pointsMaterial = this.cache.get(cacheKey) - var accessorDef = this.json.accessors[accessorIndex] + if (!pointsMaterial) { + pointsMaterial = new THREE.PointsMaterial() + THREE.Material.prototype.copy.call(pointsMaterial, material) + pointsMaterial.color.copy(material.color) + pointsMaterial.map = material.map + pointsMaterial.sizeAttenuation = false // glTF spec says points should be 1px - if (accessorDef.bufferView === undefined && accessorDef.sparse === undefined) { - // Ignore empty accessors, which may be used to declare runtime - // information about attributes coming from another source (e.g. Draco - // compression extension). - return Promise.resolve(null) + this.cache.add(cacheKey, pointsMaterial) } - var pendingBufferViews = [] + material = pointsMaterial + } else if (mesh.isLine) { + var cacheKey = 'LineBasicMaterial:' + material.uuid - if (accessorDef.bufferView !== undefined) { - pendingBufferViews.push(this.getDependency('bufferView', accessorDef.bufferView)) - } else { - pendingBufferViews.push(null) - } + var lineMaterial = this.cache.get(cacheKey) - if (accessorDef.sparse !== undefined) { - pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.indices.bufferView)) - pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.values.bufferView)) + if (!lineMaterial) { + lineMaterial = new THREE.LineBasicMaterial() + THREE.Material.prototype.copy.call(lineMaterial, material) + lineMaterial.color.copy(material.color) + + this.cache.add(cacheKey, lineMaterial) } - return Promise.all(pendingBufferViews).then(function (bufferViews) { - var bufferView = bufferViews[0] - - var itemSize = WEBGL_TYPE_SIZES[accessorDef.type] - var TypedArray = WEBGL_COMPONENT_TYPES[accessorDef.componentType] - - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - var elementBytes = TypedArray.BYTES_PER_ELEMENT - var itemBytes = elementBytes * itemSize - var byteOffset = accessorDef.byteOffset || 0 - var byteStride = - accessorDef.bufferView !== undefined ? json.bufferViews[accessorDef.bufferView].byteStride : undefined - var normalized = accessorDef.normalized === true - var array, bufferAttribute - - // The buffer is not interleaved if the stride is the item size in bytes. - if (byteStride && byteStride !== itemBytes) { - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - var ibSlice = Math.floor(byteOffset / byteStride) - var ibCacheKey = - 'InterleavedBuffer:' + - accessorDef.bufferView + - ':' + - accessorDef.componentType + - ':' + - ibSlice + - ':' + - accessorDef.count - var ib = parser.cache.get(ibCacheKey) - - if (!ib) { - array = new TypedArray(bufferView, ibSlice * byteStride, (accessorDef.count * byteStride) / elementBytes) - - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new THREE.InterleavedBuffer(array, byteStride / elementBytes) - - parser.cache.add(ibCacheKey, ib) - } + material = lineMaterial + } - bufferAttribute = new THREE.InterleavedBufferAttribute( - ib, - itemSize, - (byteOffset % byteStride) / elementBytes, - normalized - ) - } else { - if (bufferView === null) { - array = new TypedArray(accessorDef.count * itemSize) - } else { - array = new TypedArray(bufferView, byteOffset, accessorDef.count * itemSize) - } + // Clone the material if it will be modified + if (useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets) { + var cacheKey = 'ClonedMaterial:' + material.uuid + ':' - bufferAttribute = new THREE.BufferAttribute(array, itemSize, normalized) - } + if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:' + if (useSkinning) cacheKey += 'skinning:' + if (useVertexTangents) cacheKey += 'vertex-tangents:' + if (useVertexColors) cacheKey += 'vertex-colors:' + if (useFlatShading) cacheKey += 'flat-shading:' + if (useMorphTargets) cacheKey += 'morph-targets:' + if (useMorphNormals) cacheKey += 'morph-normals:' - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if (accessorDef.sparse !== undefined) { - var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR - var TypedArrayIndices = WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType] + var cachedMaterial = this.cache.get(cacheKey) - var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0 - var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0 + if (!cachedMaterial) { + cachedMaterial = material.clone() - var sparseIndices = new TypedArrayIndices( - bufferViews[1], - byteOffsetIndices, - accessorDef.sparse.count * itemSizeIndices - ) - var sparseValues = new TypedArray(bufferViews[2], byteOffsetValues, accessorDef.sparse.count * itemSize) - - if (bufferView !== null) { - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new THREE.BufferAttribute( - bufferAttribute.array.slice(), - bufferAttribute.itemSize, - bufferAttribute.normalized - ) - } + if (useSkinning) cachedMaterial.skinning = true + if (useVertexTangents) cachedMaterial.vertexTangents = true + if (useVertexColors) cachedMaterial.vertexColors = true + if (useFlatShading) cachedMaterial.flatShading = true + if (useMorphTargets) cachedMaterial.morphTargets = true + if (useMorphNormals) cachedMaterial.morphNormals = true - for (var i = 0, il = sparseIndices.length; i < il; i++) { - var index = sparseIndices[i] + this.cache.add(cacheKey, cachedMaterial) - bufferAttribute.setX(index, sparseValues[i * itemSize]) - if (itemSize >= 2) bufferAttribute.setY(index, sparseValues[i * itemSize + 1]) - if (itemSize >= 3) bufferAttribute.setZ(index, sparseValues[i * itemSize + 2]) - if (itemSize >= 4) bufferAttribute.setW(index, sparseValues[i * itemSize + 3]) - if (itemSize >= 5) throw new Error('THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.') - } - } + this.associations.set(cachedMaterial, this.associations.get(material)) + } - return bufferAttribute - }) + material = cachedMaterial } - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - GLTFParser.prototype.loadTexture = function (textureIndex) { - return Promise.resolve(new THREE.Texture()) - } - - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accomodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {THREE.Object3D} mesh Mesh, Line, or Points instance. - */ - GLTFParser.prototype.assignFinalMaterial = function (mesh) { - var geometry = mesh.geometry - var material = mesh.material - - var useVertexTangents = geometry.attributes.tangent !== undefined - var useVertexColors = geometry.attributes.color !== undefined - var useFlatShading = geometry.attributes.normal === undefined - var useSkinning = mesh.isSkinnedMesh === true - var useMorphTargets = Object.keys(geometry.morphAttributes).length > 0 - var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined - - if (mesh.isPoints) { - var cacheKey = 'PointsMaterial:' + material.uuid - - var pointsMaterial = this.cache.get(cacheKey) - - if (!pointsMaterial) { - pointsMaterial = new THREE.PointsMaterial() - THREE.Material.prototype.copy.call(pointsMaterial, material) - pointsMaterial.color.copy(material.color) - pointsMaterial.map = material.map - pointsMaterial.sizeAttenuation = false // glTF spec says points should be 1px - - this.cache.add(cacheKey, pointsMaterial) - } - - material = pointsMaterial - } else if (mesh.isLine) { - var cacheKey = 'LineBasicMaterial:' + material.uuid - - var lineMaterial = this.cache.get(cacheKey) + // workarounds for mesh and geometry - if (!lineMaterial) { - lineMaterial = new THREE.LineBasicMaterial() - THREE.Material.prototype.copy.call(lineMaterial, material) - lineMaterial.color.copy(material.color) + if (material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined) { + geometry.setAttribute('uv2', geometry.attributes.uv) + } - this.cache.add(cacheKey, lineMaterial) - } + // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 + if (material.normalScale && !useVertexTangents) { + material.normalScale.y = -material.normalScale.y + } - material = lineMaterial - } + if (material.clearcoatNormalScale && !useVertexTangents) { + material.clearcoatNormalScale.y = -material.clearcoatNormalScale.y + } - // Clone the material if it will be modified - if (useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets) { - var cacheKey = 'ClonedMaterial:' + material.uuid + ':' + mesh.material = material +} - if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:' - if (useSkinning) cacheKey += 'skinning:' - if (useVertexTangents) cacheKey += 'vertex-tangents:' - if (useVertexColors) cacheKey += 'vertex-colors:' - if (useFlatShading) cacheKey += 'flat-shading:' - if (useMorphTargets) cacheKey += 'morph-targets:' - if (useMorphNormals) cacheKey += 'morph-normals:' +GLTFParser.prototype.getMaterialType = function (/* materialIndex */) { + return THREE.MeshStandardMaterial +} - var cachedMaterial = this.cache.get(cacheKey) +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials + * @param {number} materialIndex + * @return {Promise} + */ +GLTFParser.prototype.loadMaterial = function (materialIndex) { + var parser = this + var json = this.json + var extensions = this.extensions + var materialDef = json.materials[materialIndex] - if (!cachedMaterial) { - cachedMaterial = material.clone() + var materialType + var materialParams = {} + var materialExtensions = materialDef.extensions || {} - if (useSkinning) cachedMaterial.skinning = true - if (useVertexTangents) cachedMaterial.vertexTangents = true - if (useVertexColors) cachedMaterial.vertexColors = true - if (useFlatShading) cachedMaterial.flatShading = true - if (useMorphTargets) cachedMaterial.morphTargets = true - if (useMorphNormals) cachedMaterial.morphNormals = true + var pending = [] - this.cache.add(cacheKey, cachedMaterial) + if (materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]) { + var sgExtension = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS] + materialType = sgExtension.getMaterialType() + pending.push(sgExtension.extendParams(materialParams, materialDef, parser)) + } else if (materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT]) { + var kmuExtension = extensions[EXTENSIONS.KHR_MATERIALS_UNLIT] + materialType = kmuExtension.getMaterialType() + pending.push(kmuExtension.extendParams(materialParams, materialDef, parser)) + } else { + // Specification: + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - this.associations.set(cachedMaterial, this.associations.get(material)) - } + var metallicRoughness = materialDef.pbrMetallicRoughness || {} - material = cachedMaterial - } + materialParams.color = new THREE.Color(1.0, 1.0, 1.0) + materialParams.opacity = 1.0 - // workarounds for mesh and geometry + if (Array.isArray(metallicRoughness.baseColorFactor)) { + var array = metallicRoughness.baseColorFactor - if (material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined) { - geometry.setAttribute('uv2', geometry.attributes.uv) + materialParams.color.fromArray(array) + materialParams.opacity = array[3] } - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if (material.normalScale && !useVertexTangents) { - material.normalScale.y = -material.normalScale.y - } + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0 + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0 - if (material.clearcoatNormalScale && !useVertexTangents) { - material.clearcoatNormalScale.y = -material.clearcoatNormalScale.y - } + materialType = this._invokeOne(function (ext) { + return ext.getMaterialType && ext.getMaterialType(materialIndex) + }) - mesh.material = material + pending.push( + Promise.all( + this._invokeAll(function (ext) { + return ext.extendMaterialParams && ext.extendMaterialParams(materialIndex, materialParams) + }) + ) + ) } - GLTFParser.prototype.getMaterialType = function (/* materialIndex */) { - return THREE.MeshStandardMaterial + if (materialDef.doubleSided === true) { + materialParams.side = THREE.DoubleSide } - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - GLTFParser.prototype.loadMaterial = function (materialIndex) { - var parser = this - var json = this.json - var extensions = this.extensions - var materialDef = json.materials[materialIndex] - - var materialType - var materialParams = {} - var materialExtensions = materialDef.extensions || {} - - var pending = [] - - if (materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]) { - var sgExtension = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS] - materialType = sgExtension.getMaterialType() - pending.push(sgExtension.extendParams(materialParams, materialDef, parser)) - } else if (materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT]) { - var kmuExtension = extensions[EXTENSIONS.KHR_MATERIALS_UNLIT] - materialType = kmuExtension.getMaterialType() - pending.push(kmuExtension.extendParams(materialParams, materialDef, parser)) - } else { - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material - - var metallicRoughness = materialDef.pbrMetallicRoughness || {} + var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE - materialParams.color = new THREE.Color(1.0, 1.0, 1.0) - materialParams.opacity = 1.0 + if (alphaMode === ALPHA_MODES.BLEND) { + materialParams.transparent = true - if (Array.isArray(metallicRoughness.baseColorFactor)) { - var array = metallicRoughness.baseColorFactor + // See: https://github.com/mrdoob/three.js/issues/17706 + materialParams.depthWrite = false + } else { + materialParams.transparent = false - materialParams.color.fromArray(array) - materialParams.opacity = array[3] - } - - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0 - materialParams.roughness = - metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0 + if (alphaMode === ALPHA_MODES.MASK) { + materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5 + } + } - materialType = this._invokeOne(function (ext) { - return ext.getMaterialType && ext.getMaterialType(materialIndex) - }) + if (materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial) { + materialParams.normalScale = new THREE.Vector2(1, 1) - pending.push( - Promise.all( - this._invokeAll(function (ext) { - return ext.extendMaterialParams && ext.extendMaterialParams(materialIndex, materialParams) - }) - ) - ) + if (materialDef.normalTexture.scale !== undefined) { + materialParams.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale) } + } - if (materialDef.doubleSided === true) { - materialParams.side = THREE.DoubleSide + if (materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial) { + if (materialDef.occlusionTexture.strength !== undefined) { + materialParams.aoMapIntensity = materialDef.occlusionTexture.strength } + } - var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE + if (materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial) { + materialParams.emissive = new THREE.Color().fromArray(materialDef.emissiveFactor) + } - if (alphaMode === ALPHA_MODES.BLEND) { - materialParams.transparent = true + return Promise.all(pending).then(function () { + var material - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false + if (materialType === GLTFMeshStandardSGMaterial) { + material = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams) } else { - materialParams.transparent = false - - if (alphaMode === ALPHA_MODES.MASK) { - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5 - } - } - - if (materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial) { - materialParams.normalScale = new THREE.Vector2(1, 1) - - if (materialDef.normalTexture.scale !== undefined) { - materialParams.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale) - } + material = new materialType(materialParams) } - if (materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial) { - if (materialDef.occlusionTexture.strength !== undefined) { - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength - } - } + if (materialDef.name) material.name = materialDef.name - if (materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial) { - materialParams.emissive = new THREE.Color().fromArray(materialDef.emissiveFactor) - } + // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. + if (material.map) material.map.encoding = THREE.sRGBEncoding + if (material.emissiveMap) material.emissiveMap.encoding = THREE.sRGBEncoding - return Promise.all(pending).then(function () { - var material + assignExtrasToUserData(material, materialDef) - if (materialType === GLTFMeshStandardSGMaterial) { - material = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams) - } else { - material = new materialType(materialParams) - } + parser.associations.set(material, { type: 'materials', index: materialIndex }) - if (materialDef.name) material.name = materialDef.name + if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef) - // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. - if (material.map) material.map.encoding = THREE.sRGBEncoding - if (material.emissiveMap) material.emissiveMap.encoding = THREE.sRGBEncoding - - assignExtrasToUserData(material, materialDef) + return material + }) +} - parser.associations.set(material, { type: 'materials', index: materialIndex }) +/** When Object3D instances are targeted by animation, they need unique names. */ +GLTFParser.prototype.createUniqueName = function (originalName) { + var sanitizedName = THREE.PropertyBinding.sanitizeNodeName(originalName || '') - if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef) + var name = sanitizedName - return material - }) + for (var i = 1; this.nodeNamesUsed[name]; ++i) { + name = sanitizedName + '_' + i } - /** When Object3D instances are targeted by animation, they need unique names. */ - GLTFParser.prototype.createUniqueName = function (originalName) { - var sanitizedName = THREE.PropertyBinding.sanitizeNodeName(originalName || '') - - var name = sanitizedName - - for (var i = 1; this.nodeNamesUsed[name]; ++i) { - name = sanitizedName + '_' + i - } - - this.nodeNamesUsed[name] = true - - return name - } + this.nodeNamesUsed[name] = true - /** - * @param {THREE.BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ - function computeBounds(geometry, primitiveDef, parser) { - var attributes = primitiveDef.attributes + return name +} - var box = new THREE.Box3() +/** + * @param {THREE.BufferGeometry} geometry + * @param {GLTF.Primitive} primitiveDef + * @param {GLTFParser} parser + */ +function computeBounds(geometry, primitiveDef, parser) { + var attributes = primitiveDef.attributes - if (attributes.POSITION !== undefined) { - var accessor = parser.json.accessors[attributes.POSITION] + var box = new THREE.Box3() - var min = accessor.min - var max = accessor.max + if (attributes.POSITION !== undefined) { + var accessor = parser.json.accessors[attributes.POSITION] - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + var min = accessor.min + var max = accessor.max - if (min !== undefined && max !== undefined) { - box.set(new THREE.Vector3(min[0], min[1], min[2]), new THREE.Vector3(max[0], max[1], max[2])) - } else { - console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.') + // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - return - } + if (min !== undefined && max !== undefined) { + box.set(new THREE.Vector3(min[0], min[1], min[2]), new THREE.Vector3(max[0], max[1], max[2])) } else { + console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.') + return } + } else { + return + } - var targets = primitiveDef.targets + var targets = primitiveDef.targets - if (targets !== undefined) { - var maxDisplacement = new THREE.Vector3() - var vector = new THREE.Vector3() + if (targets !== undefined) { + var maxDisplacement = new THREE.Vector3() + var vector = new THREE.Vector3() - for (var i = 0, il = targets.length; i < il; i++) { - var target = targets[i] + for (var i = 0, il = targets.length; i < il; i++) { + var target = targets[i] - if (target.POSITION !== undefined) { - var accessor = parser.json.accessors[target.POSITION] - var min = accessor.min - var max = accessor.max + if (target.POSITION !== undefined) { + var accessor = parser.json.accessors[target.POSITION] + var min = accessor.min + var max = accessor.max - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. - if (min !== undefined && max !== undefined) { - // we need to get max of absolute components because target weight is [-1,1] - vector.setX(Math.max(Math.abs(min[0]), Math.abs(max[0]))) - vector.setY(Math.max(Math.abs(min[1]), Math.abs(max[1]))) - vector.setZ(Math.max(Math.abs(min[2]), Math.abs(max[2]))) + if (min !== undefined && max !== undefined) { + // we need to get max of absolute components because target weight is [-1,1] + vector.setX(Math.max(Math.abs(min[0]), Math.abs(max[0]))) + vector.setY(Math.max(Math.abs(min[1]), Math.abs(max[1]))) + vector.setZ(Math.max(Math.abs(min[2]), Math.abs(max[2]))) - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max(vector) - } else { - console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.') - } + // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative + // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets + // are used to implement key-frame animations and as such only two are active at a time - this results in very large + // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. + maxDisplacement.max(vector) + } else { + console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.') } } - - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector(maxDisplacement) } - geometry.boundingBox = box - - var sphere = new THREE.Sphere() - - box.getCenter(sphere.center) - sphere.radius = box.min.distanceTo(box.max) / 2 - - geometry.boundingSphere = sphere + // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. + box.expandByVector(maxDisplacement) } - /** - * @param {THREE.BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} - */ - function addPrimitiveAttributes(geometry, primitiveDef, parser) { - var attributes = primitiveDef.attributes + geometry.boundingBox = box - var pending = [] + var sphere = new THREE.Sphere() - function assignAttributeAccessor(accessorIndex, attributeName) { - return parser.getDependency('accessor', accessorIndex).then(function (accessor) { - geometry.setAttribute(attributeName, accessor) - }) - } + box.getCenter(sphere.center) + sphere.radius = box.min.distanceTo(box.max) / 2 - for (var gltfAttributeName in attributes) { - var threeAttributeName = ATTRIBUTES[gltfAttributeName] || gltfAttributeName.toLowerCase() + geometry.boundingSphere = sphere +} - // Skip attributes already provided by e.g. Draco extension. - if (threeAttributeName in geometry.attributes) continue +/** + * @param {THREE.BufferGeometry} geometry + * @param {GLTF.Primitive} primitiveDef + * @param {GLTFParser} parser + * @return {Promise} + */ +function addPrimitiveAttributes(geometry, primitiveDef, parser) { + var attributes = primitiveDef.attributes - pending.push(assignAttributeAccessor(attributes[gltfAttributeName], threeAttributeName)) - } + var pending = [] - if (primitiveDef.indices !== undefined && !geometry.index) { - var accessor = parser.getDependency('accessor', primitiveDef.indices).then(function (accessor) { - geometry.setIndex(accessor) - }) + function assignAttributeAccessor(accessorIndex, attributeName) { + return parser.getDependency('accessor', accessorIndex).then(function (accessor) { + geometry.setAttribute(attributeName, accessor) + }) + } - pending.push(accessor) - } + for (var gltfAttributeName in attributes) { + var threeAttributeName = ATTRIBUTES[gltfAttributeName] || gltfAttributeName.toLowerCase() - assignExtrasToUserData(geometry, primitiveDef) + // Skip attributes already provided by e.g. Draco extension. + if (threeAttributeName in geometry.attributes) continue - computeBounds(geometry, primitiveDef, parser) + pending.push(assignAttributeAccessor(attributes[gltfAttributeName], threeAttributeName)) + } - return Promise.all(pending).then(function () { - return primitiveDef.targets !== undefined ? addMorphTargets(geometry, primitiveDef.targets, parser) : geometry + if (primitiveDef.indices !== undefined && !geometry.index) { + var accessor = parser.getDependency('accessor', primitiveDef.indices).then(function (accessor) { + geometry.setIndex(accessor) }) + + pending.push(accessor) } - /** - * @param {THREE.BufferGeometry} geometry - * @param {Number} drawMode - * @return {THREE.BufferGeometry} - */ - function toTrianglesDrawMode(geometry, drawMode) { - var index = geometry.getIndex() + assignExtrasToUserData(geometry, primitiveDef) - // generate index if not present + computeBounds(geometry, primitiveDef, parser) - if (index === null) { - var indices = [] + return Promise.all(pending).then(function () { + return primitiveDef.targets !== undefined ? addMorphTargets(geometry, primitiveDef.targets, parser) : geometry + }) +} - var position = geometry.getAttribute('position') +/** + * @param {THREE.BufferGeometry} geometry + * @param {Number} drawMode + * @return {THREE.BufferGeometry} + */ +function toTrianglesDrawMode(geometry, drawMode) { + var index = geometry.getIndex() - if (position !== undefined) { - for (var i = 0; i < position.count; i++) { - indices.push(i) - } + // generate index if not present - geometry.setIndex(indices) - index = geometry.getIndex() - } else { - console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.') - return geometry + if (index === null) { + var indices = [] + + var position = geometry.getAttribute('position') + + if (position !== undefined) { + for (var i = 0; i < position.count; i++) { + indices.push(i) } + + geometry.setIndex(indices) + index = geometry.getIndex() + } else { + console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.') + return geometry } + } + + // - // + var numberOfTriangles = index.count - 2 + var newIndices = [] - var numberOfTriangles = index.count - 2 - var newIndices = [] + if (drawMode === THREE.TriangleFanDrawMode) { + // gl.TRIANGLE_FAN - if (drawMode === THREE.TriangleFanDrawMode) { - // gl.TRIANGLE_FAN + for (var i = 1; i <= numberOfTriangles; i++) { + newIndices.push(index.getX(0)) + newIndices.push(index.getX(i)) + newIndices.push(index.getX(i + 1)) + } + } else { + // gl.TRIANGLE_STRIP - for (var i = 1; i <= numberOfTriangles; i++) { - newIndices.push(index.getX(0)) + for (var i = 0; i < numberOfTriangles; i++) { + if (i % 2 === 0) { newIndices.push(index.getX(i)) newIndices.push(index.getX(i + 1)) - } - } else { - // gl.TRIANGLE_STRIP - - for (var i = 0; i < numberOfTriangles; i++) { - if (i % 2 === 0) { - newIndices.push(index.getX(i)) - newIndices.push(index.getX(i + 1)) - newIndices.push(index.getX(i + 2)) - } else { - newIndices.push(index.getX(i + 2)) - newIndices.push(index.getX(i + 1)) - newIndices.push(index.getX(i)) - } + newIndices.push(index.getX(i + 2)) + } else { + newIndices.push(index.getX(i + 2)) + newIndices.push(index.getX(i + 1)) + newIndices.push(index.getX(i)) } } + } - if (newIndices.length / 3 !== numberOfTriangles) { - console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.') - } + if (newIndices.length / 3 !== numberOfTriangles) { + console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.') + } - // build final geometry + // build final geometry - var newGeometry = geometry.clone() - newGeometry.setIndex(newIndices) + var newGeometry = geometry.clone() + newGeometry.setIndex(newIndices) - return newGeometry - } + return newGeometry +} - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - GLTFParser.prototype.loadGeometries = function (primitives) { - var parser = this - var extensions = this.extensions - var cache = this.primitiveCache +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry + * + * Creates BufferGeometries from primitives. + * + * @param {Array} primitives + * @return {Promise>} + */ +GLTFParser.prototype.loadGeometries = function (primitives) { + var parser = this + var extensions = this.extensions + var cache = this.primitiveCache - function createDracoPrimitive(primitive) { - return extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION] - .decodePrimitive(primitive, parser) - .then(function (geometry) { - return addPrimitiveAttributes(geometry, primitive, parser) - }) - } + function createDracoPrimitive(primitive) { + return extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION] + .decodePrimitive(primitive, parser) + .then(function (geometry) { + return addPrimitiveAttributes(geometry, primitive, parser) + }) + } - var pending = [] + var pending = [] - for (var i = 0, il = primitives.length; i < il; i++) { - var primitive = primitives[i] - var cacheKey = createPrimitiveKey(primitive) + for (var i = 0, il = primitives.length; i < il; i++) { + var primitive = primitives[i] + var cacheKey = createPrimitiveKey(primitive) - // See if we've already created this geometry - var cached = cache[cacheKey] + // See if we've already created this geometry + var cached = cache[cacheKey] - if (cached) { - // Use the cached geometry if it exists - pending.push(cached.promise) - } else { - var geometryPromise + if (cached) { + // Use the cached geometry if it exists + pending.push(cached.promise) + } else { + var geometryPromise - if (primitive.extensions && primitive.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]) { - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive(primitive) - } else { - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes(new THREE.BufferGeometry(), primitive, parser) - } + if (primitive.extensions && primitive.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]) { + // Use DRACO geometry if available + geometryPromise = createDracoPrimitive(primitive) + } else { + // Otherwise create a new geometry + geometryPromise = addPrimitiveAttributes(new THREE.BufferGeometry(), primitive, parser) + } - // Cache this geometry - cache[cacheKey] = { primitive: primitive, promise: geometryPromise } + // Cache this geometry + cache[cacheKey] = { primitive: primitive, promise: geometryPromise } - pending.push(geometryPromise) - } + pending.push(geometryPromise) } - - return Promise.all(pending) } - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - GLTFParser.prototype.loadMesh = function (meshIndex) { - var parser = this - var json = this.json - var extensions = this.extensions + return Promise.all(pending) +} - var meshDef = json.meshes[meshIndex] - var primitives = meshDef.primitives +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes + * @param {number} meshIndex + * @return {Promise} + */ +GLTFParser.prototype.loadMesh = function (meshIndex) { + var parser = this + var json = this.json + var extensions = this.extensions - var pending = [] + var meshDef = json.meshes[meshIndex] + var primitives = meshDef.primitives - for (var i = 0, il = primitives.length; i < il; i++) { - var material = - primitives[i].material === undefined - ? createDefaultMaterial(this.cache) - : this.getDependency('material', primitives[i].material) + var pending = [] - pending.push(material) - } + for (var i = 0, il = primitives.length; i < il; i++) { + var material = + primitives[i].material === undefined + ? createDefaultMaterial(this.cache) + : this.getDependency('material', primitives[i].material) - pending.push(parser.loadGeometries(primitives)) + pending.push(material) + } - return Promise.all(pending).then(function (results) { - var materials = results.slice(0, results.length - 1) - var geometries = results[results.length - 1] + pending.push(parser.loadGeometries(primitives)) - var meshes = [] + return Promise.all(pending).then(function (results) { + var materials = results.slice(0, results.length - 1) + var geometries = results[results.length - 1] - for (var i = 0, il = geometries.length; i < il; i++) { - var geometry = geometries[i] - var primitive = primitives[i] + var meshes = [] - // 1. create Mesh + for (var i = 0, il = geometries.length; i < il; i++) { + var geometry = geometries[i] + var primitive = primitives[i] - var mesh + // 1. create Mesh - var material = materials[i] + var mesh - if ( - primitive.mode === WEBGL_CONSTANTS.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || - primitive.mode === undefined - ) { - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = - meshDef.isSkinnedMesh === true - ? new THREE.SkinnedMesh(geometry, material) - : new THREE.Mesh(geometry, material) + var material = materials[i] - if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP) { - mesh.geometry = toTrianglesDrawMode(mesh.geometry, THREE.TriangleStripDrawMode) - } else if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN) { - mesh.geometry = toTrianglesDrawMode(mesh.geometry, THREE.TriangleFanDrawMode) - } - } else if (primitive.mode === WEBGL_CONSTANTS.LINES) { - mesh = new THREE.LineSegments(geometry, material) - } else if (primitive.mode === WEBGL_CONSTANTS.LINE_STRIP) { - mesh = new THREE.Line(geometry, material) - } else if (primitive.mode === WEBGL_CONSTANTS.LINE_LOOP) { - mesh = new THREE.LineLoop(geometry, material) - } else if (primitive.mode === WEBGL_CONSTANTS.POINTS) { - mesh = new THREE.Points(geometry, material) - } else { - throw new Error('THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode) - } + if ( + primitive.mode === WEBGL_CONSTANTS.TRIANGLES || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || + primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || + primitive.mode === undefined + ) { + // .isSkinnedMesh isn't in glTF spec. See ._markDefs() + mesh = + meshDef.isSkinnedMesh === true + ? new THREE.SkinnedMesh(geometry, material) + : new THREE.Mesh(geometry, material) - if (Object.keys(mesh.geometry.morphAttributes).length > 0) { - updateMorphTargets(mesh, meshDef) + if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP) { + mesh.geometry = toTrianglesDrawMode(mesh.geometry, THREE.TriangleStripDrawMode) + } else if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN) { + mesh.geometry = toTrianglesDrawMode(mesh.geometry, THREE.TriangleFanDrawMode) } - - mesh.name = parser.createUniqueName(meshDef.name || 'mesh_' + meshIndex) - - assignExtrasToUserData(mesh, meshDef) - if (primitive.extensions) addUnknownExtensionsToUserData(extensions, mesh, primitive) - - parser.assignFinalMaterial(mesh) - - meshes.push(mesh) + } else if (primitive.mode === WEBGL_CONSTANTS.LINES) { + mesh = new THREE.LineSegments(geometry, material) + } else if (primitive.mode === WEBGL_CONSTANTS.LINE_STRIP) { + mesh = new THREE.Line(geometry, material) + } else if (primitive.mode === WEBGL_CONSTANTS.LINE_LOOP) { + mesh = new THREE.LineLoop(geometry, material) + } else if (primitive.mode === WEBGL_CONSTANTS.POINTS) { + mesh = new THREE.Points(geometry, material) + } else { + throw new Error('THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode) } - if (meshes.length === 1) { - return meshes[0] + if (Object.keys(mesh.geometry.morphAttributes).length > 0) { + updateMorphTargets(mesh, meshDef) } - var group = new THREE.Group() - - for (var i = 0, il = meshes.length; i < il; i++) { - group.add(meshes[i]) - } + mesh.name = parser.createUniqueName(meshDef.name || 'mesh_' + meshIndex) - return group - }) - } + assignExtrasToUserData(mesh, meshDef) + if (primitive.extensions) addUnknownExtensionsToUserData(extensions, mesh, primitive) - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - GLTFParser.prototype.loadCamera = function (cameraIndex) { - var camera - var cameraDef = this.json.cameras[cameraIndex] - var params = cameraDef[cameraDef.type] + parser.assignFinalMaterial(mesh) - if (!params) { - console.warn('THREE.GLTFLoader: Missing camera parameters.') - return + meshes.push(mesh) } - if (cameraDef.type === 'perspective') { - camera = new THREE.PerspectiveCamera( - THREE.MathUtils.radToDeg(params.yfov), - params.aspectRatio || 1, - params.znear || 1, - params.zfar || 2e6 - ) - } else if (cameraDef.type === 'orthographic') { - camera = new THREE.OrthographicCamera( - -params.xmag, - params.xmag, - params.ymag, - -params.ymag, - params.znear, - params.zfar - ) + if (meshes.length === 1) { + return meshes[0] } - if (cameraDef.name) camera.name = this.createUniqueName(cameraDef.name) + var group = new THREE.Group() - assignExtrasToUserData(camera, cameraDef) - - return Promise.resolve(camera) - } + for (var i = 0, il = meshes.length; i < il; i++) { + group.add(meshes[i]) + } - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - GLTFParser.prototype.loadSkin = function (skinIndex) { - var skinDef = this.json.skins[skinIndex] + return group + }) +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras + * @param {number} cameraIndex + * @return {Promise} + */ +GLTFParser.prototype.loadCamera = function (cameraIndex) { + var camera + var cameraDef = this.json.cameras[cameraIndex] + var params = cameraDef[cameraDef.type] + + if (!params) { + console.warn('THREE.GLTFLoader: Missing camera parameters.') + return + } + + if (cameraDef.type === 'perspective') { + camera = new THREE.PerspectiveCamera( + THREE.MathUtils.radToDeg(params.yfov), + params.aspectRatio || 1, + params.znear || 1, + params.zfar || 2e6 + ) + } else if (cameraDef.type === 'orthographic') { + camera = new THREE.OrthographicCamera( + -params.xmag, + params.xmag, + params.ymag, + -params.ymag, + params.znear, + params.zfar + ) + } + + if (cameraDef.name) camera.name = this.createUniqueName(cameraDef.name) + + assignExtrasToUserData(camera, cameraDef) + + return Promise.resolve(camera) +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins + * @param {number} skinIndex + * @return {Promise} + */ +GLTFParser.prototype.loadSkin = function (skinIndex) { + var skinDef = this.json.skins[skinIndex] + + var skinEntry = { joints: skinDef.joints } + + if (skinDef.inverseBindMatrices === undefined) { + return Promise.resolve(skinEntry) + } + + return this.getDependency('accessor', skinDef.inverseBindMatrices).then(function (accessor) { + skinEntry.inverseBindMatrices = accessor + + return skinEntry + }) +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations + * @param {number} animationIndex + * @return {Promise} + */ +GLTFParser.prototype.loadAnimation = function (animationIndex) { + var json = this.json + + var animationDef = json.animations[animationIndex] + + var pendingNodes = [] + var pendingInputAccessors = [] + var pendingOutputAccessors = [] + var pendingSamplers = [] + var pendingTargets = [] + + for (var i = 0, il = animationDef.channels.length; i < il; i++) { + var channel = animationDef.channels[i] + var sampler = animationDef.samplers[channel.sampler] + var target = channel.target + var name = target.node !== undefined ? target.node : target.id // NOTE: target.id is deprecated. + var input = animationDef.parameters !== undefined ? animationDef.parameters[sampler.input] : sampler.input + var output = animationDef.parameters !== undefined ? animationDef.parameters[sampler.output] : sampler.output + + pendingNodes.push(this.getDependency('node', name)) + pendingInputAccessors.push(this.getDependency('accessor', input)) + pendingOutputAccessors.push(this.getDependency('accessor', output)) + pendingSamplers.push(sampler) + pendingTargets.push(target) + } + + return Promise.all([ + Promise.all(pendingNodes), + Promise.all(pendingInputAccessors), + Promise.all(pendingOutputAccessors), + Promise.all(pendingSamplers), + Promise.all(pendingTargets), + ]).then(function (dependencies) { + var nodes = dependencies[0] + var inputAccessors = dependencies[1] + var outputAccessors = dependencies[2] + var samplers = dependencies[3] + var targets = dependencies[4] + + var tracks = [] + + for (var i = 0, il = nodes.length; i < il; i++) { + var node = nodes[i] + var inputAccessor = inputAccessors[i] + var outputAccessor = outputAccessors[i] + var sampler = samplers[i] + var target = targets[i] - var skinEntry = { joints: skinDef.joints } + if (node === undefined) continue - if (skinDef.inverseBindMatrices === undefined) { - return Promise.resolve(skinEntry) - } + node.updateMatrix() + node.matrixAutoUpdate = true - return this.getDependency('accessor', skinDef.inverseBindMatrices).then(function (accessor) { - skinEntry.inverseBindMatrices = accessor + var TypedKeyframeTrack - return skinEntry - }) - } + switch (PATH_PROPERTIES[target.path]) { + case PATH_PROPERTIES.weights: + TypedKeyframeTrack = THREE.NumberKeyframeTrack + break - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - GLTFParser.prototype.loadAnimation = function (animationIndex) { - var json = this.json + case PATH_PROPERTIES.rotation: + TypedKeyframeTrack = THREE.QuaternionKeyframeTrack + break - var animationDef = json.animations[animationIndex] - - var pendingNodes = [] - var pendingInputAccessors = [] - var pendingOutputAccessors = [] - var pendingSamplers = [] - var pendingTargets = [] - - for (var i = 0, il = animationDef.channels.length; i < il; i++) { - var channel = animationDef.channels[i] - var sampler = animationDef.samplers[channel.sampler] - var target = channel.target - var name = target.node !== undefined ? target.node : target.id // NOTE: target.id is deprecated. - var input = animationDef.parameters !== undefined ? animationDef.parameters[sampler.input] : sampler.input - var output = animationDef.parameters !== undefined ? animationDef.parameters[sampler.output] : sampler.output - - pendingNodes.push(this.getDependency('node', name)) - pendingInputAccessors.push(this.getDependency('accessor', input)) - pendingOutputAccessors.push(this.getDependency('accessor', output)) - pendingSamplers.push(sampler) - pendingTargets.push(target) - } + case PATH_PROPERTIES.position: + case PATH_PROPERTIES.scale: + default: + TypedKeyframeTrack = THREE.VectorKeyframeTrack + break + } - return Promise.all([ - Promise.all(pendingNodes), - Promise.all(pendingInputAccessors), - Promise.all(pendingOutputAccessors), - Promise.all(pendingSamplers), - Promise.all(pendingTargets), - ]).then(function (dependencies) { - var nodes = dependencies[0] - var inputAccessors = dependencies[1] - var outputAccessors = dependencies[2] - var samplers = dependencies[3] - var targets = dependencies[4] - - var tracks = [] - - for (var i = 0, il = nodes.length; i < il; i++) { - var node = nodes[i] - var inputAccessor = inputAccessors[i] - var outputAccessor = outputAccessors[i] - var sampler = samplers[i] - var target = targets[i] - - if (node === undefined) continue - - node.updateMatrix() - node.matrixAutoUpdate = true - - var TypedKeyframeTrack - - switch (PATH_PROPERTIES[target.path]) { - case PATH_PROPERTIES.weights: - TypedKeyframeTrack = THREE.NumberKeyframeTrack - break + var targetName = node.name ? node.name : node.uuid - case PATH_PROPERTIES.rotation: - TypedKeyframeTrack = THREE.QuaternionKeyframeTrack - break + var interpolation = + sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : THREE.InterpolateLinear - case PATH_PROPERTIES.position: - case PATH_PROPERTIES.scale: - default: - TypedKeyframeTrack = THREE.VectorKeyframeTrack - break - } + var targetNames = [] - var targetName = node.name ? node.name : node.uuid + if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) { + // Node may be a THREE.Group (glTF mesh with several primitives) or a THREE.Mesh. + node.traverse(function (object) { + if (object.isMesh === true && object.morphTargetInfluences) { + targetNames.push(object.name ? object.name : object.uuid) + } + }) + } else { + targetNames.push(targetName) + } - var interpolation = - sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : THREE.InterpolateLinear + var outputArray = outputAccessor.array - var targetNames = [] + if (outputAccessor.normalized) { + var scale - if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) { - // Node may be a THREE.Group (glTF mesh with several primitives) or a THREE.Mesh. - node.traverse(function (object) { - if (object.isMesh === true && object.morphTargetInfluences) { - targetNames.push(object.name ? object.name : object.uuid) - } - }) + if (outputArray.constructor === Int8Array) { + scale = 1 / 127 + } else if (outputArray.constructor === Uint8Array) { + scale = 1 / 255 + } else if (outputArray.constructor == Int16Array) { + scale = 1 / 32767 + } else if (outputArray.constructor === Uint16Array) { + scale = 1 / 65535 } else { - targetNames.push(targetName) + throw new Error('THREE.GLTFLoader: Unsupported output accessor component type.') } - var outputArray = outputAccessor.array - - if (outputAccessor.normalized) { - var scale - - if (outputArray.constructor === Int8Array) { - scale = 1 / 127 - } else if (outputArray.constructor === Uint8Array) { - scale = 1 / 255 - } else if (outputArray.constructor == Int16Array) { - scale = 1 / 32767 - } else if (outputArray.constructor === Uint16Array) { - scale = 1 / 65535 - } else { - throw new Error('THREE.GLTFLoader: Unsupported output accessor component type.') - } - - var scaled = new Float32Array(outputArray.length) + var scaled = new Float32Array(outputArray.length) - for (var j = 0, jl = outputArray.length; j < jl; j++) { - scaled[j] = outputArray[j] * scale - } - - outputArray = scaled + for (var j = 0, jl = outputArray.length; j < jl; j++) { + scaled[j] = outputArray[j] * scale } - } - var name = animationDef.name ? animationDef.name : 'animation_' + animationIndex - var clip = new THREE.AnimationClip(name, undefined, tracks) - clip.targetNames = targetNames - return clip - }) - } + outputArray = scaled + } + } - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - GLTFParser.prototype.loadNode = function (nodeIndex) { - var json = this.json - var extensions = this.extensions - var parser = this + var name = animationDef.name ? animationDef.name : 'animation_' + animationIndex + var clip = new THREE.AnimationClip(name, undefined, tracks) + clip.targetNames = targetNames + return clip + }) +} - var nodeDef = json.nodes[nodeIndex] +/** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy + * @param {number} nodeIndex + * @return {Promise} + */ +GLTFParser.prototype.loadNode = function (nodeIndex) { + var json = this.json + var extensions = this.extensions + var parser = this - // reserve node's name before its dependencies, so the root has the intended name. - var nodeName = nodeDef.name ? parser.createUniqueName(nodeDef.name) : '' + var nodeDef = json.nodes[nodeIndex] - return (function () { - var pending = [] + // reserve node's name before its dependencies, so the root has the intended name. + var nodeName = nodeDef.name ? parser.createUniqueName(nodeDef.name) : '' - if (nodeDef.mesh !== undefined) { - pending.push( - parser.getDependency('mesh', nodeDef.mesh).then(function (mesh) { - var node = parser._getNodeRef(parser.meshCache, nodeDef.mesh, mesh) + return (function () { + var pending = [] - // if weights are provided on the node, override weights on the mesh. - if (nodeDef.weights !== undefined) { - node.traverse(function (o) { - if (!o.isMesh) return + if (nodeDef.mesh !== undefined) { + pending.push( + parser.getDependency('mesh', nodeDef.mesh).then(function (mesh) { + var node = parser._getNodeRef(parser.meshCache, nodeDef.mesh, mesh) - for (var i = 0, il = nodeDef.weights.length; i < il; i++) { - o.morphTargetInfluences[i] = nodeDef.weights[i] - } - }) - } + // if weights are provided on the node, override weights on the mesh. + if (nodeDef.weights !== undefined) { + node.traverse(function (o) { + if (!o.isMesh) return - return node - }) - ) - } - - if (nodeDef.camera !== undefined) { - pending.push( - parser.getDependency('camera', nodeDef.camera).then(function (camera) { - return parser._getNodeRef(parser.cameraCache, nodeDef.camera, camera) - }) - ) - } + for (var i = 0, il = nodeDef.weights.length; i < il; i++) { + o.morphTargetInfluences[i] = nodeDef.weights[i] + } + }) + } - parser - ._invokeAll(function (ext) { - return ext.createNodeAttachment && ext.createNodeAttachment(nodeIndex) + return node }) - .forEach(function (promise) { - pending.push(promise) + ) + } + + if (nodeDef.camera !== undefined) { + pending.push( + parser.getDependency('camera', nodeDef.camera).then(function (camera) { + return parser._getNodeRef(parser.cameraCache, nodeDef.camera, camera) }) + ) + } - return Promise.all(pending) - })().then(function (objects) { - var node - - // .isBone isn't in glTF spec. See ._markDefs - if (nodeDef.isBone === true) { - node = new THREE.Bone() - } else if (objects.length > 1) { - node = new THREE.Group() - } else if (objects.length === 1) { - node = objects[0] - } else { - node = new THREE.Object3D() - } + parser + ._invokeAll(function (ext) { + return ext.createNodeAttachment && ext.createNodeAttachment(nodeIndex) + }) + .forEach(function (promise) { + pending.push(promise) + }) - if (node !== objects[0]) { - for (var i = 0, il = objects.length; i < il; i++) { - node.add(objects[i]) - } - } + return Promise.all(pending) + })().then(function (objects) { + var node + + // .isBone isn't in glTF spec. See ._markDefs + if (nodeDef.isBone === true) { + node = new THREE.Bone() + } else if (objects.length > 1) { + node = new THREE.Group() + } else if (objects.length === 1) { + node = objects[0] + } else { + node = new THREE.Object3D() + } - if (nodeDef.name) { - node.userData.name = nodeDef.name - node.name = nodeName + if (node !== objects[0]) { + for (var i = 0, il = objects.length; i < il; i++) { + node.add(objects[i]) } + } - assignExtrasToUserData(node, nodeDef) + if (nodeDef.name) { + node.userData.name = nodeDef.name + node.name = nodeName + } - if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef) + assignExtrasToUserData(node, nodeDef) - if (nodeDef.matrix !== undefined) { - var matrix = new THREE.Matrix4() - matrix.fromArray(nodeDef.matrix) - node.applyMatrix4(matrix) - } else { - if (nodeDef.translation !== undefined) { - node.position.fromArray(nodeDef.translation) - } + if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef) - if (nodeDef.rotation !== undefined) { - node.quaternion.fromArray(nodeDef.rotation) - } + if (nodeDef.matrix !== undefined) { + var matrix = new THREE.Matrix4() + matrix.fromArray(nodeDef.matrix) + node.applyMatrix4(matrix) + } else { + if (nodeDef.translation !== undefined) { + node.position.fromArray(nodeDef.translation) + } - if (nodeDef.scale !== undefined) { - node.scale.fromArray(nodeDef.scale) - } + if (nodeDef.rotation !== undefined) { + node.quaternion.fromArray(nodeDef.rotation) } - parser.associations.set(node, { type: 'nodes', index: nodeIndex }) + if (nodeDef.scale !== undefined) { + node.scale.fromArray(nodeDef.scale) + } + } - return node - }) - } + parser.associations.set(node, { type: 'nodes', index: nodeIndex }) - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - GLTFParser.prototype.loadScene = (function () { - // scene node hierachy builder + return node + }) +} - function buildNodeHierachy(nodeId, parentObject, json, parser) { - var nodeDef = json.nodes[nodeId] +/** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes + * @param {number} sceneIndex + * @return {Promise} + */ +GLTFParser.prototype.loadScene = (function () { + // scene node hierachy builder - return parser - .getDependency('node', nodeId) - .then(function (node) { - if (nodeDef.skin === undefined) return node + function buildNodeHierachy(nodeId, parentObject, json, parser) { + var nodeDef = json.nodes[nodeId] - // build skeleton here as well + return parser + .getDependency('node', nodeId) + .then(function (node) { + if (nodeDef.skin === undefined) return node - var skinEntry + // build skeleton here as well - return parser - .getDependency('skin', nodeDef.skin) - .then(function (skin) { - skinEntry = skin + var skinEntry - var pendingJoints = [] + return parser + .getDependency('skin', nodeDef.skin) + .then(function (skin) { + skinEntry = skin - for (var i = 0, il = skinEntry.joints.length; i < il; i++) { - pendingJoints.push(parser.getDependency('node', skinEntry.joints[i])) - } + var pendingJoints = [] - return Promise.all(pendingJoints) - }) - .then(function (jointNodes) { - node.traverse(function (mesh) { - if (!mesh.isMesh) return + for (var i = 0, il = skinEntry.joints.length; i < il; i++) { + pendingJoints.push(parser.getDependency('node', skinEntry.joints[i])) + } - var bones = [] - var boneInverses = [] + return Promise.all(pendingJoints) + }) + .then(function (jointNodes) { + node.traverse(function (mesh) { + if (!mesh.isMesh) return - for (var j = 0, jl = jointNodes.length; j < jl; j++) { - var jointNode = jointNodes[j] + var bones = [] + var boneInverses = [] - if (jointNode) { - bones.push(jointNode) + for (var j = 0, jl = jointNodes.length; j < jl; j++) { + var jointNode = jointNodes[j] - var mat = new THREE.Matrix4() + if (jointNode) { + bones.push(jointNode) - if (skinEntry.inverseBindMatrices !== undefined) { - mat.fromArray(skinEntry.inverseBindMatrices.array, j * 16) - } + var mat = new THREE.Matrix4() - boneInverses.push(mat) - } else { - console.warn('THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[j]) + if (skinEntry.inverseBindMatrices !== undefined) { + mat.fromArray(skinEntry.inverseBindMatrices.array, j * 16) } - } - mesh.bind(new THREE.Skeleton(bones, boneInverses), mesh.matrixWorld) - }) + boneInverses.push(mat) + } else { + console.warn('THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[j]) + } + } - return node + mesh.bind(new THREE.Skeleton(bones, boneInverses), mesh.matrixWorld) }) - }) - .then(function (node) { - // build node hierachy - parentObject.add(node) + return node + }) + }) + .then(function (node) { + // build node hierachy - var pending = [] + parentObject.add(node) - if (nodeDef.children) { - var children = nodeDef.children + var pending = [] - for (var i = 0, il = children.length; i < il; i++) { - var child = children[i] - pending.push(buildNodeHierachy(child, node, json, parser)) - } - } + if (nodeDef.children) { + var children = nodeDef.children - return Promise.all(pending) - }) - } + for (var i = 0, il = children.length; i < il; i++) { + var child = children[i] + pending.push(buildNodeHierachy(child, node, json, parser)) + } + } - return function loadScene(sceneIndex) { - var json = this.json - var extensions = this.extensions - var sceneDef = this.json.scenes[sceneIndex] - var parser = this + return Promise.all(pending) + }) + } - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - var scene = new THREE.Group() - if (sceneDef.name) scene.name = parser.createUniqueName(sceneDef.name) + return function loadScene(sceneIndex) { + var json = this.json + var extensions = this.extensions + var sceneDef = this.json.scenes[sceneIndex] + var parser = this - assignExtrasToUserData(scene, sceneDef) + // Loader returns Group, not Scene. + // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 + var scene = new THREE.Group() + if (sceneDef.name) scene.name = parser.createUniqueName(sceneDef.name) - if (sceneDef.extensions) addUnknownExtensionsToUserData(extensions, scene, sceneDef) + assignExtrasToUserData(scene, sceneDef) - var nodeIds = sceneDef.nodes || [] + if (sceneDef.extensions) addUnknownExtensionsToUserData(extensions, scene, sceneDef) - var pending = [] + var nodeIds = sceneDef.nodes || [] - for (var i = 0, il = nodeIds.length; i < il; i++) { - pending.push(buildNodeHierachy(nodeIds[i], scene, json, parser)) - } + var pending = [] - return Promise.all(pending).then(function () { - return scene - }) + for (var i = 0, il = nodeIds.length; i < il; i++) { + pending.push(buildNodeHierachy(nodeIds[i], scene, json, parser)) } - })() - - return GLTFLoader -})()) -export default GLTFLoader + return Promise.all(pending).then(function () { + return scene + }) + } +})() diff --git a/src/gltfjsx.js b/src/gltfjsx.js index 940e5ea..f7ac09b 100755 --- a/src/gltfjsx.js +++ b/src/gltfjsx.js @@ -4,16 +4,15 @@ import path from 'path' import transform from './utils/transform.js' import prettier from 'prettier' -import THREE from 'three' -global.THREE = THREE -import './bin/GLTFLoader.js' -import DracoLoader from './bin/DRACOLoader.js' -THREE.DRACOLoader.getDecoderModule = () => {} +import { GLTFLoader } from './bin/GLTFLoader.js' +import { DRACOLoader } from './bin/DRACOLoader.js' + +DRACOLoader.getDecoderModule = () => {} import parse from './utils/parser.js' -const gltfLoader = new THREE.GLTFLoader() -gltfLoader.setDRACOLoader(new DracoLoader()) +const gltfLoader = new GLTFLoader() +gltfLoader.setDRACOLoader(new DRACOLoader()) function toArrayBuffer(buf) { var ab = new ArrayBuffer(buf.length) diff --git a/src/utils/exports.d.ts b/src/utils/exports.d.ts new file mode 100644 index 0000000..72a7f24 --- /dev/null +++ b/src/utils/exports.d.ts @@ -0,0 +1,31 @@ +import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader' + +export function transform( + file: string, + output: string, + config?: { + resolution?: number + simplify?: boolean + weld?: number + ratio?: number + error?: number + } +): Promise + +export function parse( + fileName: string, + gltf: GLTF, + options?: { + instanceall?: boolean + instance?: boolean + precision?: number + shadows?: boolean + meta?: boolean + keepgroups?: boolean + keepnames?: boolean + types?: boolean + debug?: boolean + header?: string + draco?: object + } +): string diff --git a/src/utils/glftLoader.js b/src/utils/glftLoader.js index 5907f11..d7ebf09 100644 --- a/src/utils/glftLoader.js +++ b/src/utils/glftLoader.js @@ -1,4 +1,3 @@ -const THREE = (global.THREE = require('three')) -require('../bin/GLTFLoader') +import { GLTFLoader } from '../bin/GLTFLoader.js' -module.exports = THREE.GLTFLoader \ No newline at end of file +module.exports = GLTFLoader diff --git a/src/utils/parser.js b/src/utils/parser.js index c4d7d80..f4769e5 100644 --- a/src/utils/parser.js +++ b/src/utils/parser.js @@ -1,4 +1,4 @@ -import THREE from 'three' +import * as THREE from 'three' import isVarName from './isVarName.js' function parse(fileName, gltf, options = {}) { @@ -167,7 +167,7 @@ function parse(fileName, gltf, options = {}) { if (obj.color && obj.color.getHexString() !== 'ffffff') result += `color="#${obj.color.getHexString()}" ` if (obj.position && obj.position.isVector3 && rNbr(obj.position.length())) result += `position={[${rNbr(obj.position.x)}, ${rNbr(obj.position.y)}, ${rNbr(obj.position.z)},]} ` - if (obj.rotation && obj.rotation.isEuler && rNbr(obj.rotation.toVector3().length())) + if (obj.rotation && obj.rotation.isEuler && rNbr(new THREE.Vector3().setFromEuler(obj.rotation).length())) result += `rotation={[${rDeg(obj.rotation.x)}, ${rDeg(obj.rotation.y)}, ${rDeg(obj.rotation.z)},]} ` if ( obj.scale && diff --git a/yarn.lock b/yarn.lock index d2db0dd..dbd4af4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -103,6 +103,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/three@^0.138.0": + version "0.138.0" + resolved "https://registry.yarnpkg.com/@types/three/-/three-0.138.0.tgz#ec9f1d1fd8dffea2e0572f9d815ff5a35cf4dc6b" + integrity sha512-D8AoV7h2kbCfrv/DcebHOFh1WDwyus3HdooBkAwcBikXArdqnsQ38PQ85JCunnvun160oA9jz53GszF3zch3tg== + abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -2103,10 +2108,10 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -three@0.122.0: - version "0.122.0" - resolved "https://registry.yarnpkg.com/three/-/three-0.122.0.tgz#bd9ed8a8820074856e8ba7b63fe0a65176e01aeb" - integrity sha512-bgYMo0WdaQhf7DhLE8OSNN/rVFO5J4K1A2VeeKqoV4MjjuHjfCP6xLpg8Xedhns7NlEnN3sZ6VJROq19Qyl6Sg== +three@^0.138.0: + version "0.138.3" + resolved "https://registry.yarnpkg.com/three/-/three-0.138.3.tgz#7be5153d79dcbf9e9baad82e7faf8c29edda4ed0" + integrity sha512-4t1cKC8gimNyJChJbaklg8W/qj3PpsLJUIFm5LIuAy/hVxxNm1ru2FGTSfbTSsuHmC/7ipsyuGKqrSAKLNtkzg== through@^2.3.4, through@^2.3.8: version "2.3.8"