12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191 |
- ;(function () {
- class GLTFLoader extends THREE.Loader {
- constructor(manager) {
- super(manager)
- this.dracoLoader = null
- this.ktx2Loader = null
- this.meshoptDecoder = null
- this.pluginCallbacks = []
- this.register(function (parser) {
- return new GLTFMaterialsClearcoatExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFTextureBasisUExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFTextureWebPExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsSheenExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsTransmissionExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsVolumeExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsIorExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsEmissiveStrengthExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsSpecularExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMaterialsIridescenceExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFLightsExtension(parser)
- })
- this.register(function (parser) {
- return new GLTFMeshoptCompression(parser)
- })
- }
- load(url, onLoad, onProgress, onError) {
- const scope = this
- let resourcePath
- if (this.resourcePath !== '') {
- resourcePath = this.resourcePath
- } else if (this.path !== '') {
- resourcePath = this.path
- } else {
- resourcePath = THREE.LoaderUtils.extractUrlBase(url)
- } // 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)
- const _onError = function (e) {
- if (onError) {
- onError(e)
- } else {
- console.error(e)
- }
- scope.manager.itemError(url)
- scope.manager.itemEnd(url)
- }
- const 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
- )
- }
- setDRACOLoader(dracoLoader) {
- this.dracoLoader = dracoLoader
- return this
- }
- setDDSLoader() {
- throw new Error('THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".')
- }
- setKTX2Loader(ktx2Loader) {
- this.ktx2Loader = ktx2Loader
- return this
- }
- setMeshoptDecoder(meshoptDecoder) {
- this.meshoptDecoder = meshoptDecoder
- return this
- }
- register(callback) {
- if (this.pluginCallbacks.indexOf(callback) === -1) {
- this.pluginCallbacks.push(callback)
- }
- return this
- }
- unregister(callback) {
- if (this.pluginCallbacks.indexOf(callback) !== -1) {
- this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback), 1)
- }
- return this
- }
- parse(data, path, onLoad, onError) {
- let content
- const extensions = {}
- const plugins = {}
- if (typeof data === 'string') {
- content = data
- } else {
- const 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))
- }
- }
- const 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
- }
- const parser = new GLTFParser(json, {
- path: path || this.resourcePath || '',
- crossOrigin: this.crossOrigin,
- requestHeader: this.requestHeader,
- manager: this.manager,
- ktx2Loader: this.ktx2Loader,
- meshoptDecoder: this.meshoptDecoder,
- })
- parser.fileLoader.setRequestHeader(this.requestHeader)
- for (let i = 0; i < this.pluginCallbacks.length; i++) {
- const 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
- }
- if (json.extensionsUsed) {
- for (let i = 0; i < json.extensionsUsed.length; ++i) {
- const extensionName = json.extensionsUsed[i]
- const extensionsRequired = json.extensionsRequired || []
- 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_DRACO_MESH_COMPRESSION:
- extensions[extensionName] = new GLTFDracoMeshCompressionExtension(json, this.dracoLoader)
- break
- case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
- extensions[extensionName] = new GLTFTextureTransformExtension()
- 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 + '".')
- }
- }
- }
- }
- parser.setExtensions(extensions)
- parser.setPlugins(plugins)
- parser.parse(onLoad, onError)
- }
- parseAsync(data, path) {
- const scope = this
- return new Promise(function (resolve, reject) {
- scope.parse(data, path, resolve, reject)
- })
- }
- }
- /* GLTFREGISTRY */
- function GLTFRegistry() {
- let objects = {}
- return {
- get: function (key) {
- return objects[key]
- },
- add: function (key, object) {
- objects[key] = object
- },
- remove: function (key) {
- delete objects[key]
- },
- removeAll: function () {
- objects = {}
- },
- }
- }
- /*********************************/
- /********** EXTENSIONS ***********/
- /*********************************/
- const 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_IOR: 'KHR_materials_ior',
- KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
- KHR_MATERIALS_SHEEN: 'KHR_materials_sheen',
- KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
- KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
- KHR_MATERIALS_IRIDESCENCE: 'KHR_materials_iridescence',
- KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
- KHR_MATERIALS_VOLUME: 'KHR_materials_volume',
- KHR_TEXTURE_BASISU: 'KHR_texture_basisu',
- KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
- KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
- KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength',
- EXT_TEXTURE_WEBP: 'EXT_texture_webp',
- EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
- }
- /**
- * Punctual Lights Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
- */
- class GLTFLightsExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL // THREE.Object3D instance caches
- this.cache = {
- refs: {},
- uses: {},
- }
- }
- _markDefs() {
- const parser = this.parser
- const nodeDefs = this.parser.json.nodes || []
- for (let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) {
- const 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)
- }
- }
- }
- _loadLight(lightIndex) {
- const parser = this.parser
- const cacheKey = 'light:' + lightIndex
- let dependency = parser.cache.get(cacheKey)
- if (dependency) return dependency
- const json = parser.json
- const extensions = (json.extensions && json.extensions[this.name]) || {}
- const lightDefs = extensions.lights || []
- const lightDef = lightDefs[lightIndex]
- let lightNode
- const color = new THREE.Color(0xffffff)
- if (lightDef.color !== undefined) color.fromArray(lightDef.color)
- const 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
- if (lightDef.intensity !== undefined) lightNode.intensity = lightDef.intensity
- lightNode.name = parser.createUniqueName(lightDef.name || 'light_' + lightIndex)
- dependency = Promise.resolve(lightNode)
- parser.cache.add(cacheKey, dependency)
- return dependency
- }
- createNodeAttachment(nodeIndex) {
- const self = this
- const parser = this.parser
- const json = parser.json
- const nodeDef = json.nodes[nodeIndex]
- const lightDef = (nodeDef.extensions && nodeDef.extensions[this.name]) || {}
- const lightIndex = lightDef.light
- if (lightIndex === undefined) return null
- return this._loadLight(lightIndex).then(function (light) {
- return parser._getNodeRef(self.cache, lightIndex, light)
- })
- }
- }
- /**
- * Unlit Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
- */
- class GLTFMaterialsUnlitExtension {
- constructor() {
- this.name = EXTENSIONS.KHR_MATERIALS_UNLIT
- }
- getMaterialType() {
- return THREE.MeshBasicMaterial
- }
- extendParams(materialParams, materialDef, parser) {
- const pending = []
- materialParams.color = new THREE.Color(1.0, 1.0, 1.0)
- materialParams.opacity = 1.0
- const metallicRoughness = materialDef.pbrMetallicRoughness
- if (metallicRoughness) {
- if (Array.isArray(metallicRoughness.baseColorFactor)) {
- const array = metallicRoughness.baseColorFactor
- materialParams.color.fromArray(array)
- materialParams.opacity = array[3]
- }
- if (metallicRoughness.baseColorTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'map', metallicRoughness.baseColorTexture, THREE.sRGBEncoding))
- }
- }
- return Promise.all(pending)
- }
- }
- /**
- * Materials Emissive Strength Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md
- */
- class GLTFMaterialsEmissiveStrengthExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const emissiveStrength = materialDef.extensions[this.name].emissiveStrength
- if (emissiveStrength !== undefined) {
- materialParams.emissiveIntensity = emissiveStrength
- }
- return Promise.resolve()
- }
- }
- /**
- * Clearcoat Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
- */
- class GLTFMaterialsClearcoatExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const pending = []
- const extension = materialDef.extensions[this.name]
- if (extension.clearcoatFactor !== undefined) {
- materialParams.clearcoat = extension.clearcoatFactor
- }
- if (extension.clearcoatTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'clearcoatMap', extension.clearcoatTexture))
- }
- if (extension.clearcoatRoughnessFactor !== undefined) {
- materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor
- }
- if (extension.clearcoatRoughnessTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture))
- }
- if (extension.clearcoatNormalTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture))
- if (extension.clearcoatNormalTexture.scale !== undefined) {
- const scale = extension.clearcoatNormalTexture.scale
- materialParams.clearcoatNormalScale = new THREE.Vector2(scale, scale)
- }
- }
- return Promise.all(pending)
- }
- }
- /**
- * Iridescence Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence
- */
- class GLTFMaterialsIridescenceExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const pending = []
- const extension = materialDef.extensions[this.name]
- if (extension.iridescenceFactor !== undefined) {
- materialParams.iridescence = extension.iridescenceFactor
- }
- if (extension.iridescenceTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'iridescenceMap', extension.iridescenceTexture))
- }
- if (extension.iridescenceIor !== undefined) {
- materialParams.iridescenceIOR = extension.iridescenceIor
- }
- if (materialParams.iridescenceThicknessRange === undefined) {
- materialParams.iridescenceThicknessRange = [100, 400]
- }
- if (extension.iridescenceThicknessMinimum !== undefined) {
- materialParams.iridescenceThicknessRange[0] = extension.iridescenceThicknessMinimum
- }
- if (extension.iridescenceThicknessMaximum !== undefined) {
- materialParams.iridescenceThicknessRange[1] = extension.iridescenceThicknessMaximum
- }
- if (extension.iridescenceThicknessTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture))
- }
- return Promise.all(pending)
- }
- }
- /**
- * Sheen Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen
- */
- class GLTFMaterialsSheenExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_SHEEN
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const pending = []
- materialParams.sheenColor = new THREE.Color(0, 0, 0)
- materialParams.sheenRoughness = 0
- materialParams.sheen = 1
- const extension = materialDef.extensions[this.name]
- if (extension.sheenColorFactor !== undefined) {
- materialParams.sheenColor.fromArray(extension.sheenColorFactor)
- }
- if (extension.sheenRoughnessFactor !== undefined) {
- materialParams.sheenRoughness = extension.sheenRoughnessFactor
- }
- if (extension.sheenColorTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'sheenColorMap', extension.sheenColorTexture, THREE.sRGBEncoding))
- }
- if (extension.sheenRoughnessTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture))
- }
- return Promise.all(pending)
- }
- }
- /**
- * 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
- */
- class GLTFMaterialsTransmissionExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const pending = []
- const extension = materialDef.extensions[this.name]
- if (extension.transmissionFactor !== undefined) {
- materialParams.transmission = extension.transmissionFactor
- }
- if (extension.transmissionTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'transmissionMap', extension.transmissionTexture))
- }
- return Promise.all(pending)
- }
- }
- /**
- * Materials Volume Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume
- */
- class GLTFMaterialsVolumeExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_VOLUME
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const pending = []
- const extension = materialDef.extensions[this.name]
- materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0
- if (extension.thicknessTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'thicknessMap', extension.thicknessTexture))
- }
- materialParams.attenuationDistance = extension.attenuationDistance || 0
- const colorArray = extension.attenuationColor || [1, 1, 1]
- materialParams.attenuationColor = new THREE.Color(colorArray[0], colorArray[1], colorArray[2])
- return Promise.all(pending)
- }
- }
- /**
- * Materials ior Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior
- */
- class GLTFMaterialsIorExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_IOR
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const extension = materialDef.extensions[this.name]
- materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5
- return Promise.resolve()
- }
- }
- /**
- * Materials specular Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular
- */
- class GLTFMaterialsSpecularExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR
- }
- getMaterialType(materialIndex) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
- return THREE.MeshPhysicalMaterial
- }
- extendMaterialParams(materialIndex, materialParams) {
- const parser = this.parser
- const materialDef = parser.json.materials[materialIndex]
- if (!materialDef.extensions || !materialDef.extensions[this.name]) {
- return Promise.resolve()
- }
- const pending = []
- const extension = materialDef.extensions[this.name]
- materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0
- if (extension.specularTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'specularIntensityMap', extension.specularTexture))
- }
- const colorArray = extension.specularColorFactor || [1, 1, 1]
- materialParams.specularColor = new THREE.Color(colorArray[0], colorArray[1], colorArray[2])
- if (extension.specularColorTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'specularColorMap', extension.specularColorTexture, THREE.sRGBEncoding))
- }
- return Promise.all(pending)
- }
- }
- /**
- * BasisU THREE.Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
- */
- class GLTFTextureBasisUExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.KHR_TEXTURE_BASISU
- }
- loadTexture(textureIndex) {
- const parser = this.parser
- const json = parser.json
- const textureDef = json.textures[textureIndex]
- if (!textureDef.extensions || !textureDef.extensions[this.name]) {
- return null
- }
- const extension = textureDef.extensions[this.name]
- const loader = parser.options.ktx2Loader
- if (!loader) {
- if (json.extensionsRequired && json.extensionsRequired.indexOf(this.name) >= 0) {
- throw new Error('THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures')
- } else {
- // Assumes that the extension is optional and that a fallback texture is present
- return null
- }
- }
- return parser.loadTextureImage(textureIndex, extension.source, loader)
- }
- }
- /**
- * WebP THREE.Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp
- */
- class GLTFTextureWebPExtension {
- constructor(parser) {
- this.parser = parser
- this.name = EXTENSIONS.EXT_TEXTURE_WEBP
- this.isSupported = null
- }
- loadTexture(textureIndex) {
- const name = this.name
- const parser = this.parser
- const json = parser.json
- const textureDef = json.textures[textureIndex]
- if (!textureDef.extensions || !textureDef.extensions[name]) {
- return null
- }
- const extension = textureDef.extensions[name]
- const source = json.images[extension.source]
- let loader = parser.textureLoader
- if (source.uri) {
- const handler = parser.options.manager.getHandler(source.uri)
- if (handler !== null) loader = handler
- }
- return this.detectSupport().then(function (isSupported) {
- if (isSupported) return parser.loadTextureImage(textureIndex, extension.source, loader)
- if (json.extensionsRequired && json.extensionsRequired.indexOf(name) >= 0) {
- throw new Error('THREE.GLTFLoader: WebP required by asset but unsupported.')
- } // Fall back to PNG or JPEG.
- return parser.loadTexture(textureIndex)
- })
- }
- detectSupport() {
- if (!this.isSupported) {
- this.isSupported = new Promise(function (resolve) {
- const image = new Image() // Lossy test image. Support for lossy images doesn't guarantee support for all
- // WebP images, unfortunately.
- image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'
- image.onload = image.onerror = function () {
- resolve(image.height === 1)
- }
- })
- }
- return this.isSupported
- }
- }
- /**
- * meshopt BufferView Compression Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression
- */
- class GLTFMeshoptCompression {
- constructor(parser) {
- this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION
- this.parser = parser
- }
- loadBufferView(index) {
- const json = this.parser.json
- const bufferView = json.bufferViews[index]
- if (bufferView.extensions && bufferView.extensions[this.name]) {
- const extensionDef = bufferView.extensions[this.name]
- const buffer = this.parser.getDependency('buffer', extensionDef.buffer)
- const decoder = this.parser.options.meshoptDecoder
- if (!decoder || !decoder.supported) {
- if (json.extensionsRequired && json.extensionsRequired.indexOf(this.name) >= 0) {
- throw new Error('THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files')
- } else {
- // Assumes that the extension is optional and that fallback buffer data is present
- return null
- }
- }
- return buffer.then(function (res) {
- const byteOffset = extensionDef.byteOffset || 0
- const byteLength = extensionDef.byteLength || 0
- const count = extensionDef.count
- const stride = extensionDef.byteStride
- const source = new Uint8Array(res, byteOffset, byteLength)
- if (decoder.decodeGltfBufferAsync) {
- return decoder.decodeGltfBufferAsync(count, stride, source, extensionDef.mode, extensionDef.filter).then(function (res) {
- return res.buffer
- })
- } else {
- // Support for MeshoptDecoder 0.18 or earlier, without decodeGltfBufferAsync
- return decoder.ready.then(function () {
- const result = new ArrayBuffer(count * stride)
- decoder.decodeGltfBuffer(new Uint8Array(result), count, stride, source, extensionDef.mode, extensionDef.filter)
- return result
- })
- }
- })
- } else {
- return null
- }
- }
- }
- /* BINARY EXTENSION */
- const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'
- const BINARY_EXTENSION_HEADER_LENGTH = 12
- const BINARY_EXTENSION_CHUNK_TYPES = {
- JSON: 0x4e4f534a,
- BIN: 0x004e4942,
- }
- class GLTFBinaryExtension {
- constructor(data) {
- this.name = EXTENSIONS.KHR_BINARY_GLTF
- this.content = null
- this.body = null
- const headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH)
- this.header = {
- magic: THREE.LoaderUtils.decodeText(new Uint8Array(data.slice(0, 4))),
- version: headerView.getUint32(4, true),
- length: headerView.getUint32(8, true),
- }
- if (this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {
- throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.')
- } else if (this.header.version < 2.0) {
- throw new Error('THREE.GLTFLoader: Legacy binary file detected.')
- }
- const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH
- const chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH)
- let chunkIndex = 0
- while (chunkIndex < chunkContentsLength) {
- const chunkLength = chunkView.getUint32(chunkIndex, true)
- chunkIndex += 4
- const chunkType = chunkView.getUint32(chunkIndex, true)
- chunkIndex += 4
- if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {
- const contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength)
- this.content = THREE.LoaderUtils.decodeText(contentArray)
- } else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {
- const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex
- this.body = data.slice(byteOffset, byteOffset + chunkLength)
- } // Clients must ignore chunks with unknown types.
- chunkIndex += chunkLength
- }
- if (this.content === null) {
- throw new Error('THREE.GLTFLoader: JSON content not found.')
- }
- }
- }
- /**
- * DRACO THREE.Mesh Compression Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
- */
- class GLTFDracoMeshCompressionExtension {
- constructor(json, dracoLoader) {
- if (!dracoLoader) {
- throw new Error('THREE.GLTFLoader: No DRACOLoader instance provided.')
- }
- this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION
- this.json = json
- this.dracoLoader = dracoLoader
- this.dracoLoader.preload()
- }
- decodePrimitive(primitive, parser) {
- const json = this.json
- const dracoLoader = this.dracoLoader
- const bufferViewIndex = primitive.extensions[this.name].bufferView
- const gltfAttributeMap = primitive.extensions[this.name].attributes
- const threeAttributeMap = {}
- const attributeNormalizedMap = {}
- const attributeTypeMap = {}
- for (const attributeName in gltfAttributeMap) {
- const threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase()
- threeAttributeMap[threeAttributeName] = gltfAttributeMap[attributeName]
- }
- for (const attributeName in primitive.attributes) {
- const threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase()
- if (gltfAttributeMap[attributeName] !== undefined) {
- const accessorDef = json.accessors[primitive.attributes[attributeName]]
- const componentType = WEBGL_COMPONENT_TYPES[accessorDef.componentType]
- attributeTypeMap[threeAttributeName] = componentType.name
- attributeNormalizedMap[threeAttributeName] = accessorDef.normalized === true
- }
- }
- return parser.getDependency('bufferView', bufferViewIndex).then(function (bufferView) {
- return new Promise(function (resolve) {
- dracoLoader.decodeDracoFile(
- bufferView,
- function (geometry) {
- for (const attributeName in geometry.attributes) {
- const attribute = geometry.attributes[attributeName]
- const normalized = attributeNormalizedMap[attributeName]
- if (normalized !== undefined) attribute.normalized = normalized
- }
- resolve(geometry)
- },
- threeAttributeMap,
- attributeTypeMap
- )
- })
- })
- }
- }
- /**
- * THREE.Texture Transform Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
- */
- class GLTFTextureTransformExtension {
- constructor() {
- this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM
- }
- extendTexture(texture, transform) {
- if (transform.texCoord !== undefined) {
- console.warn('THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.')
- }
- if (transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined) {
- // See https://github.com/mrdoob/three.js/issues/21819.
- return texture
- }
- texture = texture.clone()
- if (transform.offset !== undefined) {
- texture.offset.fromArray(transform.offset)
- }
- if (transform.rotation !== undefined) {
- texture.rotation = transform.rotation
- }
- if (transform.scale !== undefined) {
- texture.repeat.fromArray(transform.scale)
- }
- texture.needsUpdate = true
- return texture
- }
- }
- /**
- * Specular-Glossiness Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness
- */
- /**
- * A sub class of StandardMaterial with some of the functionality
- * changed via the `onBeforeCompile` callback
- * @pailhead
- */
- class GLTFMeshStandardSGMaterial extends THREE.MeshStandardMaterial {
- constructor(params) {
- super()
- this.isGLTFSpecularGlossinessMaterial = true //various chunks that need replacing
- const specularMapParsFragmentChunk = ['#ifdef USE_SPECULARMAP', ' uniform sampler2D specularMap;', '#endif'].join('\n')
- const glossinessMapParsFragmentChunk = ['#ifdef USE_GLOSSINESSMAP', ' uniform sampler2D glossinessMap;', '#endif'].join('\n')
- const specularMapFragmentChunk = [
- 'vec3 specularFactor = specular;',
- '#ifdef USE_SPECULARMAP',
- ' vec4 texelSpecular = texture2D( specularMap, vUv );',
- ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
- ' specularFactor *= texelSpecular.rgb;',
- '#endif',
- ].join('\n')
- const 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')
- const 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.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.',
- 'material.roughness += geometryRoughness;',
- 'material.roughness = min( material.roughness, 1.0 );',
- 'material.specularColor = specularFactor;',
- ].join('\n')
- const uniforms = {
- specular: {
- value: new THREE.Color().setHex(0xffffff),
- },
- glossiness: {
- value: 1,
- },
- specularMap: {
- value: null,
- },
- glossinessMap: {
- value: null,
- },
- }
- this._extraUniforms = uniforms
- this.onBeforeCompile = function (shader) {
- for (const 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 <roughnessmap_pars_fragment>', specularMapParsFragmentChunk)
- .replace('#include <metalnessmap_pars_fragment>', glossinessMapParsFragmentChunk)
- .replace('#include <roughnessmap_fragment>', specularMapFragmentChunk)
- .replace('#include <metalnessmap_fragment>', glossinessMapFragmentChunk)
- .replace('#include <lights_physical_fragment>', lightPhysicalFragmentChunk)
- }
- 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
- 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
- },
- },
- 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
- }
- },
- },
- })
- delete this.metalness
- delete this.roughness
- delete this.metalnessMap
- delete this.roughnessMap
- this.setValues(params)
- }
- copy(source) {
- super.copy(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
- }
- }
- class GLTFMaterialsPbrSpecularGlossinessExtension {
- constructor() {
- this.name = EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS
- this.specularGlossinessParams = [
- 'color',
- 'map',
- 'lightMap',
- 'lightMapIntensity',
- 'aoMap',
- 'aoMapIntensity',
- 'emissive',
- 'emissiveIntensity',
- 'emissiveMap',
- 'bumpMap',
- 'bumpScale',
- 'normalMap',
- 'normalMapType',
- 'displacementMap',
- 'displacementScale',
- 'displacementBias',
- 'specularMap',
- 'specular',
- 'glossinessMap',
- 'glossiness',
- 'alphaMap',
- 'envMap',
- 'envMapIntensity',
- ]
- }
- getMaterialType() {
- return GLTFMeshStandardSGMaterial
- }
- extendParams(materialParams, materialDef, parser) {
- const pbrSpecularGlossiness = materialDef.extensions[this.name]
- materialParams.color = new THREE.Color(1.0, 1.0, 1.0)
- materialParams.opacity = 1.0
- const pending = []
- if (Array.isArray(pbrSpecularGlossiness.diffuseFactor)) {
- const array = pbrSpecularGlossiness.diffuseFactor
- materialParams.color.fromArray(array)
- materialParams.opacity = array[3]
- }
- if (pbrSpecularGlossiness.diffuseTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'map', pbrSpecularGlossiness.diffuseTexture, THREE.sRGBEncoding))
- }
- 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)
- if (Array.isArray(pbrSpecularGlossiness.specularFactor)) {
- materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor)
- }
- if (pbrSpecularGlossiness.specularGlossinessTexture !== undefined) {
- const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture
- pending.push(parser.assignTexture(materialParams, 'glossinessMap', specGlossMapDef))
- pending.push(parser.assignTexture(materialParams, 'specularMap', specGlossMapDef, THREE.sRGBEncoding))
- }
- return Promise.all(pending)
- }
- createMaterial(materialParams) {
- const material = new GLTFMeshStandardSGMaterial(materialParams)
- material.fog = true
- material.color = materialParams.color
- material.map = materialParams.map === undefined ? null : materialParams.map
- material.lightMap = null
- material.lightMapIntensity = 1.0
- material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap
- material.aoMapIntensity = 1.0
- material.emissive = materialParams.emissive
- material.emissiveIntensity = materialParams.emissiveIntensity === undefined ? 1.0 : materialParams.emissiveIntensity
- material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap
- material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap
- material.bumpScale = 1
- material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap
- material.normalMapType = THREE.TangentSpaceNormalMap
- if (materialParams.normalScale) material.normalScale = materialParams.normalScale
- material.displacementMap = null
- material.displacementScale = 1
- material.displacementBias = 0
- material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap
- material.specular = materialParams.specular
- material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap
- material.glossiness = materialParams.glossiness
- material.alphaMap = null
- material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap
- material.envMapIntensity = 1.0
- return material
- }
- }
- /**
- * THREE.Mesh Quantization Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization
- */
- class GLTFMeshQuantizationExtension {
- constructor() {
- 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
- class GLTFCubicSplineInterpolant extends THREE.Interpolant {
- constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) {
- super(parameterPositions, sampleValues, sampleSize, resultBuffer)
- }
- copySampleValue_(index) {
- // Copies a sample value to the result buffer. See description of glTF
- // CUBICSPLINE values layout in interpolate_() function below.
- const result = this.resultBuffer,
- values = this.sampleValues,
- valueSize = this.valueSize,
- offset = index * valueSize * 3 + valueSize
- for (let i = 0; i !== valueSize; i++) {
- result[i] = values[offset + i]
- }
- return result
- }
- interpolate_(i1, t0, t, t1) {
- const result = this.resultBuffer
- const values = this.sampleValues
- const stride = this.valueSize
- const stride2 = stride * 2
- const stride3 = stride * 3
- const td = t1 - t0
- const p = (t - t0) / td
- const pp = p * p
- const ppp = pp * p
- const offset1 = i1 * stride3
- const offset0 = offset1 - stride3
- const s2 = -2 * ppp + 3 * pp
- const s3 = ppp - pp
- const s0 = 1 - s2
- const s1 = s3 - pp + p // Layout of keyframe output values for CUBICSPLINE animations:
- // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
- for (let i = 0; i !== stride; i++) {
- const p0 = values[offset0 + i + stride] // splineVertex_k
- const m0 = values[offset0 + i + stride2] * td // outTangent_k * (t_k+1 - t_k)
- const p1 = values[offset1 + i + stride] // splineVertex_k+1
- const 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
- }
- }
- const _q = new THREE.Quaternion()
- class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant {
- interpolate_(i1, t0, t, t1) {
- const result = super.interpolate_(i1, t0, t, t1)
- _q.fromArray(result).normalize().toArray(result)
- return result
- }
- }
- /*********************************/
- /********** INTERNALS ************/
- /*********************************/
- /* CONSTANTS */
- const 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,
- }
- const WEBGL_COMPONENT_TYPES = {
- 5120: Int8Array,
- 5121: Uint8Array,
- 5122: Int16Array,
- 5123: Uint16Array,
- 5125: Uint32Array,
- 5126: Float32Array,
- }
- const WEBGL_FILTERS = {
- 9728: THREE.NearestFilter,
- 9729: THREE.LinearFilter,
- 9984: THREE.NearestMipmapNearestFilter,
- 9985: THREE.LinearMipmapNearestFilter,
- 9986: THREE.NearestMipmapLinearFilter,
- 9987: THREE.LinearMipmapLinearFilter,
- }
- const WEBGL_WRAPPINGS = {
- 33071: THREE.ClampToEdgeWrapping,
- 33648: THREE.MirroredRepeatWrapping,
- 10497: THREE.RepeatWrapping,
- }
- const WEBGL_TYPE_SIZES = {
- SCALAR: 1,
- VEC2: 2,
- VEC3: 3,
- VEC4: 4,
- MAT2: 4,
- MAT3: 9,
- MAT4: 16,
- }
- const ATTRIBUTES = {
- POSITION: 'position',
- NORMAL: 'normal',
- TANGENT: 'tangent',
- TEXCOORD_0: 'uv',
- TEXCOORD_1: 'uv2',
- COLOR_0: 'color',
- WEIGHTS_0: 'skinWeight',
- JOINTS_0: 'skinIndex',
- }
- const PATH_PROPERTIES = {
- scale: 'scale',
- translation: 'position',
- rotation: 'quaternion',
- weights: 'morphTargetInfluences',
- }
- const 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,
- }
- const ALPHA_MODES = {
- OPAQUE: 'OPAQUE',
- MASK: 'MASK',
- BLEND: 'BLEND',
- }
- /**
- * 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 cache['DefaultMaterial']
- }
- function addUnknownExtensionsToUserData(knownExtensions, object, objectDef) {
- // Add unknown glTF extensions to an object's userData.
- for (const name in objectDef.extensions) {
- if (knownExtensions[name] === undefined) {
- object.userData.gltfExtensions = object.userData.gltfExtensions || {}
- object.userData.gltfExtensions[name] = objectDef.extensions[name]
- }
- }
- }
- /**
- * @param {Object3D|Material|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 {BufferGeometry} geometry
- * @param {Array<GLTF.Target>} targets
- * @param {GLTFParser} parser
- * @return {Promise<BufferGeometry>}
- */
- function addMorphTargets(geometry, targets, parser) {
- let hasMorphPosition = false
- let hasMorphNormal = false
- let hasMorphColor = false
- for (let i = 0, il = targets.length; i < il; i++) {
- const target = targets[i]
- if (target.POSITION !== undefined) hasMorphPosition = true
- if (target.NORMAL !== undefined) hasMorphNormal = true
- if (target.COLOR_0 !== undefined) hasMorphColor = true
- if (hasMorphPosition && hasMorphNormal && hasMorphColor) break
- }
- if (!hasMorphPosition && !hasMorphNormal && !hasMorphColor) return Promise.resolve(geometry)
- const pendingPositionAccessors = []
- const pendingNormalAccessors = []
- const pendingColorAccessors = []
- for (let i = 0, il = targets.length; i < il; i++) {
- const target = targets[i]
- if (hasMorphPosition) {
- const pendingAccessor = target.POSITION !== undefined ? parser.getDependency('accessor', target.POSITION) : geometry.attributes.position
- pendingPositionAccessors.push(pendingAccessor)
- }
- if (hasMorphNormal) {
- const pendingAccessor = target.NORMAL !== undefined ? parser.getDependency('accessor', target.NORMAL) : geometry.attributes.normal
- pendingNormalAccessors.push(pendingAccessor)
- }
- if (hasMorphColor) {
- const pendingAccessor = target.COLOR_0 !== undefined ? parser.getDependency('accessor', target.COLOR_0) : geometry.attributes.color
- pendingColorAccessors.push(pendingAccessor)
- }
- }
- return Promise.all([Promise.all(pendingPositionAccessors), Promise.all(pendingNormalAccessors), Promise.all(pendingColorAccessors)]).then(function (accessors) {
- const morphPositions = accessors[0]
- const morphNormals = accessors[1]
- const morphColors = accessors[2]
- if (hasMorphPosition) geometry.morphAttributes.position = morphPositions
- if (hasMorphNormal) geometry.morphAttributes.normal = morphNormals
- if (hasMorphColor) geometry.morphAttributes.color = morphColors
- geometry.morphTargetsRelative = true
- return geometry
- })
- }
- /**
- * @param {Mesh} mesh
- * @param {GLTF.Mesh} meshDef
- */
- function updateMorphTargets(mesh, meshDef) {
- mesh.updateMorphTargets()
- if (meshDef.weights !== undefined) {
- for (let i = 0, il = meshDef.weights.length; i < il; i++) {
- mesh.morphTargetInfluences[i] = meshDef.weights[i]
- }
- } // .extras has user-defined data, so check that .extras.targetNames is an array.
- if (meshDef.extras && Array.isArray(meshDef.extras.targetNames)) {
- const targetNames = meshDef.extras.targetNames
- if (mesh.morphTargetInfluences.length === targetNames.length) {
- mesh.morphTargetDictionary = {}
- for (let i = 0, il = targetNames.length; i < il; i++) {
- mesh.morphTargetDictionary[targetNames[i]] = i
- }
- } else {
- console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.')
- }
- }
- }
- function createPrimitiveKey(primitiveDef) {
- const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]
- let geometryKey
- if (dracoExtension) {
- geometryKey = 'draco:' + dracoExtension.bufferView + ':' + dracoExtension.indices + ':' + createAttributesKey(dracoExtension.attributes)
- } else {
- geometryKey = primitiveDef.indices + ':' + createAttributesKey(primitiveDef.attributes) + ':' + primitiveDef.mode
- }
- return geometryKey
- }
- function createAttributesKey(attributes) {
- let attributesKey = ''
- const keys = Object.keys(attributes).sort()
- for (let i = 0, il = keys.length; i < il; i++) {
- attributesKey += keys[i] + ':' + attributes[keys[i]] + ';'
- }
- return attributesKey
- }
- function getNormalizedComponentScale(constructor) {
- // Reference:
- // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
- switch (constructor) {
- case Int8Array:
- return 1 / 127
- case Uint8Array:
- return 1 / 255
- case Int16Array:
- return 1 / 32767
- case Uint16Array:
- return 1 / 65535
- default:
- throw new Error('THREE.GLTFLoader: Unsupported normalized accessor component type.')
- }
- }
- function getImageURIMimeType(uri) {
- if (uri.search(/\.jpe?g($|\?)/i) > 0 || uri.search(/^data\:image\/jpeg/) === 0) return 'image/jpeg'
- if (uri.search(/\.webp($|\?)/i) > 0 || uri.search(/^data\:image\/webp/) === 0) return 'image/webp'
- return 'image/png'
- }
- /* GLTF PARSER */
- class GLTFParser {
- constructor(json = {}, options = {}) {
- this.json = json
- this.extensions = {}
- this.plugins = {}
- this.options = options // loader object cache
- this.cache = new GLTFRegistry() // associations between Three.js objects and glTF elements
- this.associations = new Map() // THREE.BufferGeometry caching
- this.primitiveCache = {} // THREE.Object3D instance caches
- this.meshCache = {
- refs: {},
- uses: {},
- }
- this.cameraCache = {
- refs: {},
- uses: {},
- }
- this.lightCache = {
- refs: {},
- uses: {},
- }
- this.sourceCache = {}
- this.textureCache = {} // Track node names, to ensure no duplicates
- this.nodeNamesUsed = {} // Use an THREE.ImageBitmapLoader if imageBitmaps are supported. Moves much of the
- // expensive work of uploading a texture to the GPU off the main thread.
- const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) === true
- const isFirefox = navigator.userAgent.indexOf('Firefox') > -1
- const firefoxVersion = isFirefox ? navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1] : -1
- if (typeof createImageBitmap === 'undefined' || isSafari || (isFirefox && firefoxVersion < 98)) {
- this.textureLoader = new THREE.TextureLoader(this.options.manager)
- } else {
- this.textureLoader = new THREE.ImageBitmapLoader(this.options.manager)
- }
- this.textureLoader.setCrossOrigin(this.options.crossOrigin)
- this.textureLoader.setRequestHeader(this.options.requestHeader)
- this.fileLoader = new THREE.FileLoader(this.options.manager)
- this.fileLoader.setResponseType('arraybuffer')
- if (this.options.crossOrigin === 'use-credentials') {
- this.fileLoader.setWithCredentials(true)
- }
- }
- setExtensions(extensions) {
- this.extensions = extensions
- }
- setPlugins(plugins) {
- this.plugins = plugins
- }
- parse(onLoad, onError) {
- const parser = this
- const json = this.json
- const extensions = this.extensions // 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._invokeAll(function (ext) {
- return ext.beforeRoot && ext.beforeRoot()
- })
- )
- .then(function () {
- return Promise.all([parser.getDependencies('scene'), parser.getDependencies('animation'), parser.getDependencies('camera')])
- })
- .then(function (dependencies) {
- const result = {
- scene: dependencies[0][json.scene || 0],
- scenes: dependencies[0],
- animations: dependencies[1],
- cameras: dependencies[2],
- asset: json.asset,
- parser: parser,
- userData: {},
- }
- addUnknownExtensionsToUserData(extensions, result, json)
- assignExtrasToUserData(result, json)
- Promise.all(
- parser._invokeAll(function (ext) {
- return ext.afterRoot && ext.afterRoot(result)
- })
- ).then(function () {
- onLoad(result)
- })
- })
- .catch(onError)
- }
- /**
- * Marks the special nodes/meshes in json for efficient parse.
- */
- _markDefs() {
- const nodeDefs = this.json.nodes || []
- const skinDefs = this.json.skins || []
- const meshDefs = this.json.meshes || [] // Nothing in the node definition indicates whether it is a THREE.Bone or an
- // THREE.Object3D. Use the skins' joint references to mark bones.
- for (let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex++) {
- const joints = skinDefs[skinIndex].joints
- for (let i = 0, il = joints.length; i < il; i++) {
- nodeDefs[joints[i]].isBone = true
- }
- } // Iterate over all nodes, marking references to shared resources,
- // as well as skeleton joints.
- for (let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) {
- const nodeDef = nodeDefs[nodeIndex]
- if (nodeDef.mesh !== undefined) {
- this._addNodeRef(this.meshCache, nodeDef.mesh) // Nothing in the mesh definition indicates whether it is
- // a THREE.SkinnedMesh or THREE.Mesh. Use the node's mesh reference
- // to mark THREE.SkinnedMesh if node has skin.
- if (nodeDef.skin !== undefined) {
- meshDefs[nodeDef.mesh].isSkinnedMesh = true
- }
- }
- if (nodeDef.camera !== undefined) {
- this._addNodeRef(this.cameraCache, nodeDef.camera)
- }
- }
- }
- /**
- * Counts references to shared node / THREE.Object3D resources. These resources
- * can be reused, or "instantiated", at multiple nodes in the scene
- * hierarchy. THREE.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.
- */
- _addNodeRef(cache, index) {
- if (index === undefined) return
- if (cache.refs[index] === undefined) {
- cache.refs[index] = cache.uses[index] = 0
- }
- cache.refs[index]++
- }
- /** Returns a reference to a shared resource, cloning it if necessary. */
- _getNodeRef(cache, index, object) {
- if (cache.refs[index] <= 1) return object
- const ref = object.clone() // Propagates mappings to the cloned object, prevents mappings on the
- // original object from being lost.
- const updateMappings = (original, clone) => {
- const mappings = this.associations.get(original)
- if (mappings != null) {
- this.associations.set(clone, mappings)
- }
- for (const [i, child] of original.children.entries()) {
- updateMappings(child, clone.children[i])
- }
- }
- updateMappings(object, ref)
- ref.name += '_instance_' + cache.uses[index]++
- return ref
- }
- _invokeOne(func) {
- const extensions = Object.values(this.plugins)
- extensions.push(this)
- for (let i = 0; i < extensions.length; i++) {
- const result = func(extensions[i])
- if (result) return result
- }
- return null
- }
- _invokeAll(func) {
- const extensions = Object.values(this.plugins)
- extensions.unshift(this)
- const pending = []
- for (let i = 0; i < extensions.length; i++) {
- const result = func(extensions[i])
- if (result) pending.push(result)
- }
- return pending
- }
- /**
- * Requests the specified dependency asynchronously, with caching.
- * @param {string} type
- * @param {number} index
- * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>}
- */
- getDependency(type, index) {
- const cacheKey = type + ':' + index
- let 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 = this._invokeOne(function (ext) {
- return ext.loadBufferView && ext.loadBufferView(index)
- })
- break
- case 'buffer':
- dependency = this.loadBuffer(index)
- break
- case 'material':
- dependency = this._invokeOne(function (ext) {
- return ext.loadMaterial && ext.loadMaterial(index)
- })
- break
- case 'texture':
- dependency = this._invokeOne(function (ext) {
- return ext.loadTexture && ext.loadTexture(index)
- })
- break
- case 'skin':
- dependency = this.loadSkin(index)
- break
- case 'animation':
- dependency = this._invokeOne(function (ext) {
- return ext.loadAnimation && ext.loadAnimation(index)
- })
- break
- case 'camera':
- dependency = this.loadCamera(index)
- break
- default:
- throw new Error('Unknown type: ' + type)
- }
- this.cache.add(cacheKey, dependency)
- }
- return dependency
- }
- /**
- * Requests all dependencies of the specified type asynchronously, with caching.
- * @param {string} type
- * @return {Promise<Array<Object>>}
- */
- getDependencies(type) {
- let dependencies = this.cache.get(type)
- if (!dependencies) {
- const parser = this
- const defs = this.json[type + (type === 'mesh' ? 'es' : 's')] || []
- dependencies = Promise.all(
- defs.map(function (def, index) {
- return parser.getDependency(type, index)
- })
- )
- 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<ArrayBuffer>}
- */
- loadBuffer(bufferIndex) {
- const bufferDef = this.json.buffers[bufferIndex]
- const loader = this.fileLoader
- if (bufferDef.type && bufferDef.type !== 'arraybuffer') {
- throw new Error('THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.')
- } // 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)
- }
- const options = this.options
- return new Promise(function (resolve, reject) {
- loader.load(THREE.LoaderUtils.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<ArrayBuffer>}
- */
- loadBufferView(bufferViewIndex) {
- const bufferViewDef = this.json.bufferViews[bufferViewIndex]
- return this.getDependency('buffer', bufferViewDef.buffer).then(function (buffer) {
- const byteLength = bufferViewDef.byteLength || 0
- const 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<BufferAttribute|InterleavedBufferAttribute>}
- */
- loadAccessor(accessorIndex) {
- const parser = this
- const json = this.json
- const 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)
- }
- const 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) {
- const bufferView = bufferViews[0]
- const itemSize = WEBGL_TYPE_SIZES[accessorDef.type]
- const TypedArray = WEBGL_COMPONENT_TYPES[accessorDef.componentType] // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
- const elementBytes = TypedArray.BYTES_PER_ELEMENT
- const itemBytes = elementBytes * itemSize
- const byteOffset = accessorDef.byteOffset || 0
- const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[accessorDef.bufferView].byteStride : undefined
- const normalized = accessorDef.normalized === true
- let 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 THREE.InterleavedBuffer
- // This makes sure that IBA.count reflects accessor.count properly
- const ibSlice = Math.floor(byteOffset / byteStride)
- const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count
- let 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)
- }
- 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)
- }
- bufferAttribute = new THREE.BufferAttribute(array, itemSize, normalized)
- } // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
- if (accessorDef.sparse !== undefined) {
- const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR
- const TypedArrayIndices = WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType]
- const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0
- const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0
- const sparseIndices = new TypedArrayIndices(bufferViews[1], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices)
- const 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)
- }
- for (let i = 0, il = sparseIndices.length; i < il; i++) {
- const index = sparseIndices[i]
- 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 THREE.BufferAttribute.')
- }
- }
- return bufferAttribute
- })
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures
- * @param {number} textureIndex
- * @return {Promise<THREE.Texture>}
- */
- loadTexture(textureIndex) {
- const json = this.json
- const options = this.options
- const textureDef = json.textures[textureIndex]
- const sourceIndex = textureDef.source
- const sourceDef = json.images[sourceIndex]
- let loader = this.textureLoader
- if (sourceDef.uri) {
- const handler = options.manager.getHandler(sourceDef.uri)
- if (handler !== null) loader = handler
- }
- return this.loadTextureImage(textureIndex, sourceIndex, loader)
- }
- loadTextureImage(textureIndex, sourceIndex, loader) {
- const parser = this
- const json = this.json
- const textureDef = json.textures[textureIndex]
- const sourceDef = json.images[sourceIndex]
- const cacheKey = (sourceDef.uri || sourceDef.bufferView) + ':' + textureDef.sampler
- if (this.textureCache[cacheKey]) {
- // See https://github.com/mrdoob/three.js/issues/21559.
- return this.textureCache[cacheKey]
- }
- const promise = this.loadImageSource(sourceIndex, loader)
- .then(function (texture) {
- texture.flipY = false
- if (textureDef.name) texture.name = textureDef.name
- const samplers = json.samplers || {}
- const sampler = samplers[textureDef.sampler] || {}
- texture.magFilter = WEBGL_FILTERS[sampler.magFilter] || THREE.LinearFilter
- texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || THREE.LinearMipmapLinearFilter
- texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || THREE.RepeatWrapping
- texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || THREE.RepeatWrapping
- parser.associations.set(texture, {
- textures: textureIndex,
- })
- return texture
- })
- .catch(function () {
- return null
- })
- this.textureCache[cacheKey] = promise
- return promise
- }
- loadImageSource(sourceIndex, loader) {
- const parser = this
- const json = this.json
- const options = this.options
- if (this.sourceCache[sourceIndex] !== undefined) {
- return this.sourceCache[sourceIndex].then(texture => texture.clone())
- }
- const sourceDef = json.images[sourceIndex]
- const URL = self.URL || self.webkitURL
- let sourceURI = sourceDef.uri || ''
- let isObjectURL = false
- if (sourceDef.bufferView !== undefined) {
- // Load binary image data from bufferView, if provided.
- sourceURI = parser.getDependency('bufferView', sourceDef.bufferView).then(function (bufferView) {
- isObjectURL = true
- const blob = new Blob([bufferView], {
- type: sourceDef.mimeType,
- })
- sourceURI = URL.createObjectURL(blob)
- return sourceURI
- })
- } else if (sourceDef.uri === undefined) {
- throw new Error('THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView')
- }
- const promise = Promise.resolve(sourceURI)
- .then(function (sourceURI) {
- return new Promise(function (resolve, reject) {
- let onLoad = resolve
- if (loader.isImageBitmapLoader === true) {
- onLoad = function (imageBitmap) {
- const texture = new THREE.Texture(imageBitmap)
- texture.needsUpdate = true
- resolve(texture)
- }
- }
- loader.load(THREE.LoaderUtils.resolveURL(sourceURI, options.path), onLoad, undefined, reject)
- })
- })
- .then(function (texture) {
- // Clean up resources and configure THREE.Texture.
- if (isObjectURL === true) {
- URL.revokeObjectURL(sourceURI)
- }
- texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType(sourceDef.uri)
- return texture
- })
- .catch(function (error) {
- console.error("THREE.GLTFLoader: Couldn't load texture", sourceURI)
- throw error
- })
- this.sourceCache[sourceIndex] = promise
- return promise
- }
- /**
- * Asynchronously assigns a texture to the given material parameters.
- * @param {Object} materialParams
- * @param {string} mapName
- * @param {Object} mapDef
- * @return {Promise<Texture>}
- */
- assignTexture(materialParams, mapName, mapDef, encoding) {
- const parser = this
- return this.getDependency('texture', mapDef.index).then(function (texture) {
- // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured
- // However, we will copy UV set 0 to UV set 1 on demand for aoMap
- if (mapDef.texCoord !== undefined && mapDef.texCoord != 0 && !(mapName === 'aoMap' && mapDef.texCoord == 1)) {
- console.warn('THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.')
- }
- if (parser.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM]) {
- const transform = mapDef.extensions !== undefined ? mapDef.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM] : undefined
- if (transform) {
- const gltfReference = parser.associations.get(texture)
- texture = parser.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM].extendTexture(texture, transform)
- parser.associations.set(texture, gltfReference)
- }
- }
- if (encoding !== undefined) {
- texture.encoding = encoding
- }
- materialParams[mapName] = texture
- return texture
- })
- }
- /**
- * Assigns final material to a THREE.Mesh, THREE.Line, or THREE.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 accommodate different primitive types, defines, etc. New materials will
- * be created if necessary, and reused from a cache.
- * @param {Object3D} mesh THREE.Mesh, THREE.Line, or THREE.Points instance.
- */
- assignFinalMaterial(mesh) {
- const geometry = mesh.geometry
- let material = mesh.material
- const useDerivativeTangents = geometry.attributes.tangent === undefined
- const useVertexColors = geometry.attributes.color !== undefined
- const useFlatShading = geometry.attributes.normal === undefined
- if (mesh.isPoints) {
- const cacheKey = 'PointsMaterial:' + material.uuid
- let 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) {
- const cacheKey = 'LineBasicMaterial:' + material.uuid
- let lineMaterial = this.cache.get(cacheKey)
- if (!lineMaterial) {
- lineMaterial = new THREE.LineBasicMaterial()
- THREE.Material.prototype.copy.call(lineMaterial, material)
- lineMaterial.color.copy(material.color)
- this.cache.add(cacheKey, lineMaterial)
- }
- material = lineMaterial
- } // Clone the material if it will be modified
- if (useDerivativeTangents || useVertexColors || useFlatShading) {
- let cacheKey = 'ClonedMaterial:' + material.uuid + ':'
- if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:'
- if (useDerivativeTangents) cacheKey += 'derivative-tangents:'
- if (useVertexColors) cacheKey += 'vertex-colors:'
- if (useFlatShading) cacheKey += 'flat-shading:'
- let cachedMaterial = this.cache.get(cacheKey)
- if (!cachedMaterial) {
- cachedMaterial = material.clone()
- if (useVertexColors) cachedMaterial.vertexColors = true
- if (useFlatShading) cachedMaterial.flatShading = true
- if (useDerivativeTangents) {
- // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
- if (cachedMaterial.normalScale) cachedMaterial.normalScale.y *= -1
- if (cachedMaterial.clearcoatNormalScale) cachedMaterial.clearcoatNormalScale.y *= -1
- }
- this.cache.add(cacheKey, cachedMaterial)
- this.associations.set(cachedMaterial, this.associations.get(material))
- }
- material = cachedMaterial
- } // workarounds for mesh and geometry
- if (material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined) {
- geometry.setAttribute('uv2', geometry.attributes.uv)
- }
- mesh.material = material
- }
- getMaterialType() {
- return THREE.MeshStandardMaterial
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
- * @param {number} materialIndex
- * @return {Promise<Material>}
- */
- loadMaterial(materialIndex) {
- const parser = this
- const json = this.json
- const extensions = this.extensions
- const materialDef = json.materials[materialIndex]
- let materialType
- const materialParams = {}
- const materialExtensions = materialDef.extensions || {}
- const pending = []
- if (materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]) {
- const 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]) {
- const 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
- const metallicRoughness = materialDef.pbrMetallicRoughness || {}
- materialParams.color = new THREE.Color(1.0, 1.0, 1.0)
- materialParams.opacity = 1.0
- if (Array.isArray(metallicRoughness.baseColorFactor)) {
- const array = metallicRoughness.baseColorFactor
- materialParams.color.fromArray(array)
- materialParams.opacity = array[3]
- }
- if (metallicRoughness.baseColorTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'map', metallicRoughness.baseColorTexture, THREE.sRGBEncoding))
- }
- materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0
- materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0
- if (metallicRoughness.metallicRoughnessTexture !== undefined) {
- pending.push(parser.assignTexture(materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture))
- pending.push(parser.assignTexture(materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture))
- }
- materialType = this._invokeOne(function (ext) {
- return ext.getMaterialType && ext.getMaterialType(materialIndex)
- })
- pending.push(
- Promise.all(
- this._invokeAll(function (ext) {
- return ext.extendMaterialParams && ext.extendMaterialParams(materialIndex, materialParams)
- })
- )
- )
- }
- if (materialDef.doubleSided === true) {
- materialParams.side = THREE.DoubleSide
- }
- const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE
- if (alphaMode === ALPHA_MODES.BLEND) {
- materialParams.transparent = true // See: https://github.com/mrdoob/three.js/issues/17706
- materialParams.depthWrite = false
- } 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) {
- pending.push(parser.assignTexture(materialParams, 'normalMap', materialDef.normalTexture))
- materialParams.normalScale = new THREE.Vector2(1, 1)
- if (materialDef.normalTexture.scale !== undefined) {
- const scale = materialDef.normalTexture.scale
- materialParams.normalScale.set(scale, scale)
- }
- }
- if (materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {
- pending.push(parser.assignTexture(materialParams, 'aoMap', materialDef.occlusionTexture))
- if (materialDef.occlusionTexture.strength !== undefined) {
- materialParams.aoMapIntensity = materialDef.occlusionTexture.strength
- }
- }
- if (materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial) {
- materialParams.emissive = new THREE.Color().fromArray(materialDef.emissiveFactor)
- }
- if (materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {
- pending.push(parser.assignTexture(materialParams, 'emissiveMap', materialDef.emissiveTexture, THREE.sRGBEncoding))
- }
- return Promise.all(pending).then(function () {
- let material
- if (materialType === GLTFMeshStandardSGMaterial) {
- material = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams)
- } else {
- material = new materialType(materialParams)
- }
- if (materialDef.name) material.name = materialDef.name
- assignExtrasToUserData(material, materialDef)
- parser.associations.set(material, {
- materials: materialIndex,
- })
- if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef)
- return material
- })
- }
- /** When THREE.Object3D instances are targeted by animation, they need unique names. */
- createUniqueName(originalName) {
- const sanitizedName = THREE.PropertyBinding.sanitizeNodeName(originalName || '')
- let name = sanitizedName
- for (let i = 1; this.nodeNamesUsed[name]; ++i) {
- name = sanitizedName + '_' + i
- }
- this.nodeNamesUsed[name] = true
- return name
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
- *
- * Creates BufferGeometries from primitives.
- *
- * @param {Array<GLTF.Primitive>} primitives
- * @return {Promise<Array<BufferGeometry>>}
- */
- loadGeometries(primitives) {
- const parser = this
- const extensions = this.extensions
- const cache = this.primitiveCache
- function createDracoPrimitive(primitive) {
- return extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION].decodePrimitive(primitive, parser).then(function (geometry) {
- return addPrimitiveAttributes(geometry, primitive, parser)
- })
- }
- const pending = []
- for (let i = 0, il = primitives.length; i < il; i++) {
- const primitive = primitives[i]
- const cacheKey = createPrimitiveKey(primitive) // See if we've already created this geometry
- const cached = cache[cacheKey]
- if (cached) {
- // Use the cached geometry if it exists
- pending.push(cached.promise)
- } else {
- let 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)
- } // Cache this geometry
- cache[cacheKey] = {
- primitive: primitive,
- promise: 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<Group|Mesh|SkinnedMesh>}
- */
- loadMesh(meshIndex) {
- const parser = this
- const json = this.json
- const extensions = this.extensions
- const meshDef = json.meshes[meshIndex]
- const primitives = meshDef.primitives
- const pending = []
- for (let i = 0, il = primitives.length; i < il; i++) {
- const material = primitives[i].material === undefined ? createDefaultMaterial(this.cache) : this.getDependency('material', primitives[i].material)
- pending.push(material)
- }
- pending.push(parser.loadGeometries(primitives))
- return Promise.all(pending).then(function (results) {
- const materials = results.slice(0, results.length - 1)
- const geometries = results[results.length - 1]
- const meshes = []
- for (let i = 0, il = geometries.length; i < il; i++) {
- const geometry = geometries[i]
- const primitive = primitives[i] // 1. create THREE.Mesh
- let mesh
- const material = materials[i]
- 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 (mesh.isSkinnedMesh === true && !mesh.geometry.attributes.skinWeight.normalized) {
- // we normalize floating point skin weight array to fix malformed assets (see #15319)
- // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
- mesh.normalizeSkinWeights()
- }
- 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 (Object.keys(mesh.geometry.morphAttributes).length > 0) {
- updateMorphTargets(mesh, meshDef)
- }
- mesh.name = parser.createUniqueName(meshDef.name || 'mesh_' + meshIndex)
- assignExtrasToUserData(mesh, meshDef)
- if (primitive.extensions) addUnknownExtensionsToUserData(extensions, mesh, primitive)
- parser.assignFinalMaterial(mesh)
- meshes.push(mesh)
- }
- for (let i = 0, il = meshes.length; i < il; i++) {
- parser.associations.set(meshes[i], {
- meshes: meshIndex,
- primitives: i,
- })
- }
- if (meshes.length === 1) {
- return meshes[0]
- }
- const group = new THREE.Group()
- parser.associations.set(group, {
- meshes: meshIndex,
- })
- for (let i = 0, il = meshes.length; i < il; i++) {
- group.add(meshes[i])
- }
- return group
- })
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
- * @param {number} cameraIndex
- * @return {Promise<THREE.Camera>}
- */
- loadCamera(cameraIndex) {
- let camera
- const cameraDef = this.json.cameras[cameraIndex]
- const 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<Object>}
- */
- loadSkin(skinIndex) {
- const skinDef = this.json.skins[skinIndex]
- const 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<AnimationClip>}
- */
- loadAnimation(animationIndex) {
- const json = this.json
- const animationDef = json.animations[animationIndex]
- const pendingNodes = []
- const pendingInputAccessors = []
- const pendingOutputAccessors = []
- const pendingSamplers = []
- const pendingTargets = []
- for (let i = 0, il = animationDef.channels.length; i < il; i++) {
- const channel = animationDef.channels[i]
- const sampler = animationDef.samplers[channel.sampler]
- const target = channel.target
- const name = target.node
- const input = animationDef.parameters !== undefined ? animationDef.parameters[sampler.input] : sampler.input
- const 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) {
- const nodes = dependencies[0]
- const inputAccessors = dependencies[1]
- const outputAccessors = dependencies[2]
- const samplers = dependencies[3]
- const targets = dependencies[4]
- const tracks = []
- for (let i = 0, il = nodes.length; i < il; i++) {
- const node = nodes[i]
- const inputAccessor = inputAccessors[i]
- const outputAccessor = outputAccessors[i]
- const sampler = samplers[i]
- const target = targets[i]
- if (node === undefined) continue
- node.updateMatrix()
- let TypedKeyframeTrack
- switch (PATH_PROPERTIES[target.path]) {
- case PATH_PROPERTIES.weights:
- TypedKeyframeTrack = THREE.NumberKeyframeTrack
- break
- case PATH_PROPERTIES.rotation:
- TypedKeyframeTrack = THREE.QuaternionKeyframeTrack
- break
- case PATH_PROPERTIES.position:
- case PATH_PROPERTIES.scale:
- default:
- TypedKeyframeTrack = THREE.VectorKeyframeTrack
- break
- }
- const targetName = node.name ? node.name : node.uuid
- const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : THREE.InterpolateLinear
- const targetNames = []
- if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) {
- node.traverse(function (object) {
- if (object.morphTargetInfluences) {
- targetNames.push(object.name ? object.name : object.uuid)
- }
- })
- } else {
- targetNames.push(targetName)
- }
- let outputArray = outputAccessor.array
- if (outputAccessor.normalized) {
- const scale = getNormalizedComponentScale(outputArray.constructor)
- const scaled = new Float32Array(outputArray.length)
- for (let j = 0, jl = outputArray.length; j < jl; j++) {
- scaled[j] = outputArray[j] * scale
- }
- outputArray = scaled
- }
- for (let j = 0, jl = targetNames.length; j < jl; j++) {
- const track = new TypedKeyframeTrack(targetNames[j] + '.' + PATH_PROPERTIES[target.path], inputAccessor.array, outputArray, interpolation) // Override interpolation with custom factory method.
- if (sampler.interpolation === 'CUBICSPLINE') {
- track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline(result) {
- // A CUBICSPLINE keyframe in glTF has three output values for each input value,
- // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
- // must be divided by three to get the interpolant's sampleSize argument.
- const interpolantType = this instanceof THREE.QuaternionKeyframeTrack ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant
- return new interpolantType(this.times, this.values, this.getValueSize() / 3, result)
- } // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
- track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true
- }
- tracks.push(track)
- }
- }
- const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex
- return new THREE.AnimationClip(name, undefined, tracks)
- }
- )
- }
- createNodeMesh(nodeIndex) {
- const json = this.json
- const parser = this
- const nodeDef = json.nodes[nodeIndex]
- if (nodeDef.mesh === undefined) return null
- return parser.getDependency('mesh', nodeDef.mesh).then(function (mesh) {
- const node = parser._getNodeRef(parser.meshCache, nodeDef.mesh, mesh) // if weights are provided on the node, override weights on the mesh.
- if (nodeDef.weights !== undefined) {
- node.traverse(function (o) {
- if (!o.isMesh) return
- for (let i = 0, il = nodeDef.weights.length; i < il; i++) {
- o.morphTargetInfluences[i] = nodeDef.weights[i]
- }
- })
- }
- return node
- })
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
- * @param {number} nodeIndex
- * @return {Promise<Object3D>}
- */
- loadNode(nodeIndex) {
- const json = this.json
- const extensions = this.extensions
- const parser = this
- const nodeDef = json.nodes[nodeIndex] // reserve node's name before its dependencies, so the root has the intended name.
- const nodeName = nodeDef.name ? parser.createUniqueName(nodeDef.name) : ''
- return (function () {
- const pending = []
- const meshPromise = parser._invokeOne(function (ext) {
- return ext.createNodeMesh && ext.createNodeMesh(nodeIndex)
- })
- if (meshPromise) {
- pending.push(meshPromise)
- }
- if (nodeDef.camera !== undefined) {
- pending.push(
- parser.getDependency('camera', nodeDef.camera).then(function (camera) {
- return parser._getNodeRef(parser.cameraCache, nodeDef.camera, camera)
- })
- )
- }
- parser
- ._invokeAll(function (ext) {
- return ext.createNodeAttachment && ext.createNodeAttachment(nodeIndex)
- })
- .forEach(function (promise) {
- pending.push(promise)
- })
- return Promise.all(pending)
- })().then(function (objects) {
- let 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 (node !== objects[0]) {
- for (let i = 0, il = objects.length; i < il; i++) {
- node.add(objects[i])
- }
- }
- if (nodeDef.name) {
- node.userData.name = nodeDef.name
- node.name = nodeName
- }
- assignExtrasToUserData(node, nodeDef)
- if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef)
- if (nodeDef.matrix !== undefined) {
- const matrix = new THREE.Matrix4()
- matrix.fromArray(nodeDef.matrix)
- node.applyMatrix4(matrix)
- } else {
- if (nodeDef.translation !== undefined) {
- node.position.fromArray(nodeDef.translation)
- }
- if (nodeDef.rotation !== undefined) {
- node.quaternion.fromArray(nodeDef.rotation)
- }
- if (nodeDef.scale !== undefined) {
- node.scale.fromArray(nodeDef.scale)
- }
- }
- if (!parser.associations.has(node)) {
- parser.associations.set(node, {})
- }
- parser.associations.get(node).nodes = nodeIndex
- return node
- })
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
- * @param {number} sceneIndex
- * @return {Promise<Group>}
- */
- loadScene(sceneIndex) {
- const json = this.json
- const extensions = this.extensions
- const sceneDef = this.json.scenes[sceneIndex]
- const parser = this // THREE.Loader returns THREE.Group, not Scene.
- // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172
- const scene = new THREE.Group()
- if (sceneDef.name) scene.name = parser.createUniqueName(sceneDef.name)
- assignExtrasToUserData(scene, sceneDef)
- if (sceneDef.extensions) addUnknownExtensionsToUserData(extensions, scene, sceneDef)
- const nodeIds = sceneDef.nodes || []
- const pending = []
- for (let i = 0, il = nodeIds.length; i < il; i++) {
- pending.push(buildNodeHierarchy(nodeIds[i], scene, json, parser))
- }
- return Promise.all(pending).then(function () {
- // Removes dangling associations, associations that reference a node that
- // didn't make it into the scene.
- const reduceAssociations = node => {
- const reducedAssociations = new Map()
- for (const [key, value] of parser.associations) {
- if (key instanceof THREE.Material || key instanceof THREE.Texture) {
- reducedAssociations.set(key, value)
- }
- }
- node.traverse(node => {
- const mappings = parser.associations.get(node)
- if (mappings != null) {
- reducedAssociations.set(node, mappings)
- }
- })
- return reducedAssociations
- }
- parser.associations = reduceAssociations(scene)
- return scene
- })
- }
- }
- function buildNodeHierarchy(nodeId, parentObject, json, parser) {
- const nodeDef = json.nodes[nodeId]
- return parser
- .getDependency('node', nodeId)
- .then(function (node) {
- if (nodeDef.skin === undefined) return node // build skeleton here as well
- let skinEntry
- return parser
- .getDependency('skin', nodeDef.skin)
- .then(function (skin) {
- skinEntry = skin
- const pendingJoints = []
- for (let i = 0, il = skinEntry.joints.length; i < il; i++) {
- pendingJoints.push(parser.getDependency('node', skinEntry.joints[i]))
- }
- return Promise.all(pendingJoints)
- })
- .then(function (jointNodes) {
- node.traverse(function (mesh) {
- if (!mesh.isMesh) return
- const bones = []
- const boneInverses = []
- for (let j = 0, jl = jointNodes.length; j < jl; j++) {
- const jointNode = jointNodes[j]
- if (jointNode) {
- bones.push(jointNode)
- const mat = new THREE.Matrix4()
- if (skinEntry.inverseBindMatrices !== undefined) {
- mat.fromArray(skinEntry.inverseBindMatrices.array, j * 16)
- }
- boneInverses.push(mat)
- } else {
- console.warn('THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[j])
- }
- }
- mesh.bind(new THREE.Skeleton(bones, boneInverses), mesh.matrixWorld)
- })
- return node
- })
- })
- .then(function (node) {
- // build node hierachy
- parentObject.add(node)
- const pending = []
- if (nodeDef.children) {
- const children = nodeDef.children
- for (let i = 0, il = children.length; i < il; i++) {
- const child = children[i]
- pending.push(buildNodeHierarchy(child, node, json, parser))
- }
- }
- return Promise.all(pending)
- })
- }
- /**
- * @param {BufferGeometry} geometry
- * @param {GLTF.Primitive} primitiveDef
- * @param {GLTFParser} parser
- */
- function computeBounds(geometry, primitiveDef, parser) {
- const attributes = primitiveDef.attributes
- const box = new THREE.Box3()
- if (attributes.POSITION !== undefined) {
- const accessor = parser.json.accessors[attributes.POSITION]
- const min = accessor.min
- const max = accessor.max // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
- 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]))
- if (accessor.normalized) {
- const boxScale = getNormalizedComponentScale(WEBGL_COMPONENT_TYPES[accessor.componentType])
- box.min.multiplyScalar(boxScale)
- box.max.multiplyScalar(boxScale)
- }
- } else {
- console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.')
- return
- }
- } else {
- return
- }
- const targets = primitiveDef.targets
- if (targets !== undefined) {
- const maxDisplacement = new THREE.Vector3()
- const vector = new THREE.Vector3()
- for (let i = 0, il = targets.length; i < il; i++) {
- const target = targets[i]
- if (target.POSITION !== undefined) {
- const accessor = parser.json.accessors[target.POSITION]
- const min = accessor.min
- const max = accessor.max // 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 (accessor.normalized) {
- const boxScale = getNormalizedComponentScale(WEBGL_COMPONENT_TYPES[accessor.componentType])
- vector.multiplyScalar(boxScale)
- } // 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
- const sphere = new THREE.Sphere()
- box.getCenter(sphere.center)
- sphere.radius = box.min.distanceTo(box.max) / 2
- geometry.boundingSphere = sphere
- }
- /**
- * @param {BufferGeometry} geometry
- * @param {GLTF.Primitive} primitiveDef
- * @param {GLTFParser} parser
- * @return {Promise<BufferGeometry>}
- */
- function addPrimitiveAttributes(geometry, primitiveDef, parser) {
- const attributes = primitiveDef.attributes
- const pending = []
- function assignAttributeAccessor(accessorIndex, attributeName) {
- return parser.getDependency('accessor', accessorIndex).then(function (accessor) {
- geometry.setAttribute(attributeName, accessor)
- })
- }
- for (const gltfAttributeName in attributes) {
- const threeAttributeName = ATTRIBUTES[gltfAttributeName] || gltfAttributeName.toLowerCase() // Skip attributes already provided by e.g. Draco extension.
- if (threeAttributeName in geometry.attributes) continue
- pending.push(assignAttributeAccessor(attributes[gltfAttributeName], threeAttributeName))
- }
- if (primitiveDef.indices !== undefined && !geometry.index) {
- const accessor = parser.getDependency('accessor', primitiveDef.indices).then(function (accessor) {
- geometry.setIndex(accessor)
- })
- pending.push(accessor)
- }
- assignExtrasToUserData(geometry, primitiveDef)
- computeBounds(geometry, primitiveDef, parser)
- return Promise.all(pending).then(function () {
- return primitiveDef.targets !== undefined ? addMorphTargets(geometry, primitiveDef.targets, parser) : geometry
- })
- }
- /**
- * @param {BufferGeometry} geometry
- * @param {Number} drawMode
- * @return {BufferGeometry}
- */
- function toTrianglesDrawMode(geometry, drawMode) {
- let index = geometry.getIndex() // generate index if not present
- if (index === null) {
- const indices = []
- const position = geometry.getAttribute('position')
- if (position !== undefined) {
- for (let 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
- }
- } //
- const numberOfTriangles = index.count - 2
- const newIndices = []
- if (drawMode === THREE.TriangleFanDrawMode) {
- // gl.TRIANGLE_FAN
- for (let 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 (let 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))
- }
- }
- }
- if (newIndices.length / 3 !== numberOfTriangles) {
- console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.')
- } // build final geometry
- const newGeometry = geometry.clone()
- newGeometry.setIndex(newIndices)
- return newGeometry
- }
- THREE.GLTFLoader = GLTFLoader
- })()
|