| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942 | import BoundingSphere from "../Core/BoundingSphere.js";import Cartesian3 from "../Core/Cartesian3.js";import Cartesian4 from "../Core/Cartesian4.js";import Cartographic from "../Core/Cartographic.js";import Check from "../Core/Check.js";import clone from "../Core/clone.js";import Color from "../Core/Color.js";import combine from "../Core/combine.js";import createGuid from "../Core/createGuid.js";import Credit from "../Core/Credit.js";import defaultValue from "../Core/defaultValue.js";import defer from "../Core/defer.js";import defined from "../Core/defined.js";import deprecationWarning from "../Core/deprecationWarning.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js";import FeatureDetection from "../Core/FeatureDetection.js";import getAbsoluteUri from "../Core/getAbsoluteUri.js";import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";import getMagic from "../Core/getMagic.js";import getStringFromTypedArray from "../Core/getStringFromTypedArray.js";import IndexDatatype from "../Core/IndexDatatype.js";import ImageBasedLighting from "./ImageBasedLighting.js";import loadImageFromTypedArray from "../Core/loadImageFromTypedArray.js";import loadKTX2 from "../Core/loadKTX2.js";import CesiumMath from "../Core/Math.js";import Matrix3 from "../Core/Matrix3.js";import Matrix4 from "../Core/Matrix4.js";import PixelFormat from "../Core/PixelFormat.js";import PrimitiveType from "../Core/PrimitiveType.js";import Quaternion from "../Core/Quaternion.js";import Resource from "../Core/Resource.js";import Transforms from "../Core/Transforms.js";import WebGLConstants from "../Core/WebGLConstants.js";import Buffer from "../Renderer/Buffer.js";import BufferUsage from "../Renderer/BufferUsage.js";import DrawCommand from "../Renderer/DrawCommand.js";import Pass from "../Renderer/Pass.js";import RenderState from "../Renderer/RenderState.js";import Sampler from "../Renderer/Sampler.js";import ShaderProgram from "../Renderer/ShaderProgram.js";import ShaderSource from "../Renderer/ShaderSource.js";import Texture from "../Renderer/Texture.js";import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";import TextureWrap from "../Renderer/TextureWrap.js";import VertexArray from "../Renderer/VertexArray.js";import addDefaults from "./GltfPipeline/addDefaults.js";import addPipelineExtras from "./GltfPipeline/addPipelineExtras.js";import ForEach from "./GltfPipeline/ForEach.js";import getAccessorByteStride from "./GltfPipeline/getAccessorByteStride.js";import usesExtension from "./GltfPipeline/usesExtension.js";import numberOfComponentsForType from "./GltfPipeline/numberOfComponentsForType.js";import parseGlb from "./GltfPipeline/parseGlb.js";import updateVersion from "./GltfPipeline/updateVersion.js";import Axis from "./Axis.js";import BlendingState from "./BlendingState.js";import ClippingPlaneCollection from "./ClippingPlaneCollection.js";import ColorBlendMode from "./ColorBlendMode.js";import DepthFunction from "./DepthFunction.js";import DracoLoader from "./DracoLoader.js";import getClipAndStyleCode from "./getClipAndStyleCode.js";import getClippingFunction from "./getClippingFunction.js";import HeightReference from "./HeightReference.js";import JobType from "./JobType.js";import ModelAnimationCache from "./ModelAnimationCache.js";import ModelAnimationCollection from "./ModelAnimationCollection.js";import ModelLoadResources from "./ModelLoadResources.js";import ModelMaterial from "./ModelMaterial.js";import ModelMesh from "./ModelMesh.js";import ModelNode from "./ModelNode.js";import ModelOutlineLoader from "./ModelOutlineLoader.js";import ModelUtility from "./ModelUtility.js";import OctahedralProjectedCubeMap from "./OctahedralProjectedCubeMap.js";import processModelMaterialsCommon from "./processModelMaterialsCommon.js";import processPbrMaterials from "./processPbrMaterials.js";import SceneMode from "./SceneMode.js";import ShadowMode from "./ShadowMode.js";import SplitDirection from "./SplitDirection.js";import Splitter from "./Splitter.js";import StencilConstants from "./StencilConstants.js";const boundingSphereCartesian3Scratch = new Cartesian3();const ModelState = ModelUtility.ModelState;// glTF MIME types discussed in https://github.com/KhronosGroup/glTF/issues/412 and https://github.com/KhronosGroup/glTF/issues/943const defaultModelAccept =  "model/gltf-binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01";const articulationEpsilon = CesiumMath.EPSILON16;///////////////////////////////////////////////////////////////////////////function setCachedGltf(model, cachedGltf) {  model._cachedGltf = cachedGltf;}// glTF JSON can be big given embedded geometry, textures, and animations, so we// cache it across all models using the same url/cache-key.  This also reduces the// slight overhead in assigning defaults to missing values.//// Note that this is a global cache, compared to renderer resources, which// are cached per context.function CachedGltf(options) {  this._gltf = options.gltf;  this.ready = options.ready;  this.modelsToLoad = [];  this.count = 0;}Object.defineProperties(CachedGltf.prototype, {  gltf: {    set: function (value) {      this._gltf = value;    },    get: function () {      return this._gltf;    },  },});CachedGltf.prototype.makeReady = function (gltfJson) {  this.gltf = gltfJson;  const models = this.modelsToLoad;  const length = models.length;  for (let i = 0; i < length; ++i) {    const m = models[i];    if (!m.isDestroyed()) {      setCachedGltf(m, this);    }  }  this.modelsToLoad = undefined;  this.ready = true;};const gltfCache = {};const uriToGuid = {};////////////////////////////////////////////////////////////////////////////** * A 3D model based on glTF, the runtime asset format for WebGL, OpenGL ES, and OpenGL. * <p> * Cesium includes support for geometry and materials, glTF animations, and glTF skinning. * In addition, individual glTF nodes are pickable with {@link Scene#pick} and animatable * with {@link Model#getNode}.  glTF cameras and lights are not currently supported. * </p> * <p> * An external glTF asset is created with {@link Model.fromGltf}.  glTF JSON can also be * created at runtime and passed to this constructor function.  In either case, the * {@link Model#readyPromise} is resolved when the model is ready to render, i.e., * when the external binary, image, and shader files are downloaded and the WebGL * resources are created. * </p> * <p> * Cesium supports glTF assets with the following extensions: * <ul> * <li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations} * </li><li> * {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression} * </li><li> * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness} * </li><li> * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_basisu|KHR_texture_basisu} * </li> * </ul> * </p> * <p> * Note: for models with compressed textures using the KHR_texture_basisu extension, we recommend power of 2 textures in both dimensions * for maximum compatibility. This is because some samplers require power of 2 textures ({@link https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL|Using textures in WebGL}) * and KHR_texture_basisu requires multiple of 4 dimensions ({@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_basisu/README.md#additional-requirements|KHR_texture_basisu additional requirements}). * </p> * <p> * For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated * relative to a local origin. * </p> * * @alias Model * @constructor * * @param {Object} [options] Object with the following properties: * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] A glTF JSON object, or a binary glTF buffer. * @param {Resource|String} [options.basePath=''] The base path that paths in the glTF JSON are relative to. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom. * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model. * @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead. * @param {ImageBasedLighting} [options.imageBasedLighting] The properties for managing image-based lighting on this model. * @param {Cartesian2} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] Scales diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {String} [options.specularEnvironmentMaps] A URL to a KTX2 file that contains a cube map of the specular lighting and the convoluted specular mipmaps. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. * @param {Boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0. * @param {Boolean} [options.showOutline=true] Whether to display the outline for models using the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. When true, outlines are displayed. When false, outlines are not displayed. * @param {SplitDirection} [options.splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this model. * * * @see Model.fromGltf * * @demo {@link https://sandcastle.cesium.com/index.html?src=3D%20Models.html|Cesium Sandcastle Models Demo} */function Model(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const cacheKey = options.cacheKey;  this._cacheKey = cacheKey;  this._cachedGltf = undefined;  this._releaseGltfJson = defaultValue(options.releaseGltfJson, false);  let cachedGltf;  if (    defined(cacheKey) &&    defined(gltfCache[cacheKey]) &&    gltfCache[cacheKey].ready  ) {    // glTF JSON is in cache and ready    cachedGltf = gltfCache[cacheKey];    ++cachedGltf.count;  } else {    // glTF was explicitly provided, e.g., when a user uses the Model constructor directly    let gltf = options.gltf;    if (defined(gltf)) {      if (gltf instanceof ArrayBuffer) {        gltf = new Uint8Array(gltf);      }      if (gltf instanceof Uint8Array) {        // Binary glTF        const parsedGltf = parseGlb(gltf);        cachedGltf = new CachedGltf({          gltf: parsedGltf,          ready: true,        });      } else {        // Normal glTF (JSON)        cachedGltf = new CachedGltf({          gltf: options.gltf,          ready: true,        });      }      cachedGltf.count = 1;      if (defined(cacheKey)) {        gltfCache[cacheKey] = cachedGltf;      }    }  }  setCachedGltf(this, cachedGltf);  const basePath = defaultValue(options.basePath, "");  this._resource = Resource.createIfNeeded(basePath);  // User specified credit  let credit = options.credit;  if (typeof credit === "string") {    credit = new Credit(credit);  }  this._credit = credit;  // List of credits to be added from the Resource if it is an IonResource  this._resourceCredits = [];  // List of credits to be added from the glTF  this._gltfCredits = [];  this._showCreditsOnScreen = defaultValue(options.showCreditsOnScreen, false);  /**   * Determines if the model primitive will be shown.   *   * @type {Boolean}   *   * @default true   */  this.show = defaultValue(options.show, true);  /**   * The silhouette color.   *   * @type {Color}   *   * @default Color.RED   */  this.silhouetteColor = defaultValue(options.silhouetteColor, Color.RED);  this._silhouetteColor = new Color();  this._silhouetteColorPreviousAlpha = 1.0;  this._normalAttributeName = undefined;  /**   * The size of the silhouette in pixels.   *   * @type {Number}   *   * @default 0.0   */  this.silhouetteSize = defaultValue(options.silhouetteSize, 0.0);  /**   * The 4x4 transformation matrix that transforms the model from model to world coordinates.   * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.   * Local reference frames can be used by providing a different transformation matrix, like that returned   * by {@link Transforms.eastNorthUpToFixedFrame}.   *   * @type {Matrix4}   *   * @default {@link Matrix4.IDENTITY}   *   * @example   * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);   * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);   */  this.modelMatrix = Matrix4.clone(    defaultValue(options.modelMatrix, Matrix4.IDENTITY)  );  this._modelMatrix = Matrix4.clone(this.modelMatrix);  this._clampedModelMatrix = undefined;  /**   * A uniform scale applied to this model before the {@link Model#modelMatrix}.   * Values greater than <code>1.0</code> increase the size of the model; values   * less than <code>1.0</code> decrease.   *   * @type {Number}   *   * @default 1.0   */  this.scale = defaultValue(options.scale, 1.0);  this._scale = this.scale;  /**   * The approximate minimum pixel size of the model regardless of zoom.   * This can be used to ensure that a model is visible even when the viewer   * zooms out.  When <code>0.0</code>, no minimum size is enforced.   *   * @type {Number}   *   * @default 0.0   */  this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0);  this._minimumPixelSize = this.minimumPixelSize;  /**   * The maximum scale size for a model. This can be used to give   * an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model   * is never an unreasonable scale.   *   * @type {Number}   */  this.maximumScale = options.maximumScale;  this._maximumScale = this.maximumScale;  /**   * User-defined object returned when the model is picked.   *   * @type Object   *   * @default undefined   *   * @see Scene#pick   */  this.id = options.id;  this._id = options.id;  /**   * Returns the height reference of the model   *   * @type {HeightReference}   *   * @default HeightReference.NONE   */  this.heightReference = defaultValue(    options.heightReference,    HeightReference.NONE  );  this._heightReference = this.heightReference;  this._heightChanged = false;  this._removeUpdateHeightCallback = undefined;  const scene = options.scene;  this._scene = scene;  if (defined(scene) && defined(scene.terrainProviderChanged)) {    this._terrainProviderChangedCallback = scene.terrainProviderChanged.addEventListener(      function () {        this._heightChanged = true;      },      this    );  }  /**   * Used for picking primitives that wrap a model.   *   * @private   */  this._pickObject = options.pickObject;  this._allowPicking = defaultValue(options.allowPicking, true);  this._ready = false;  this._readyPromise = defer();  /**   * The currently playing glTF animations.   *   * @type {ModelAnimationCollection}   */  this.activeAnimations = new ModelAnimationCollection(this);  /**   * Determines if the model's animations should hold a pose over frames where no keyframes are specified.   *   * @type {Boolean}   */  this.clampAnimations = defaultValue(options.clampAnimations, true);  this._defaultTexture = undefined;  this._incrementallyLoadTextures = defaultValue(    options.incrementallyLoadTextures,    true  );  this._asynchronous = defaultValue(options.asynchronous, true);  /**   * Determines whether the model casts or receives shadows from light sources.   *   * @type {ShadowMode}   *   * @default ShadowMode.ENABLED   */  this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);  this._shadows = this.shadows;  /**   * A color that blends with the model's rendered color.   *   * @type {Color}   *   * @default Color.WHITE   */  this.color = Color.clone(defaultValue(options.color, Color.WHITE));  this._colorPreviousAlpha = 1.0;  /**   * Defines how the color blends with the model.   *   * @type {ColorBlendMode}   *   * @default ColorBlendMode.HIGHLIGHT   */  this.colorBlendMode = defaultValue(    options.colorBlendMode,    ColorBlendMode.HIGHLIGHT  );  /**   * Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>.   * A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with   * any value in-between resulting in a mix of the two.   *   * @type {Number}   *   * @default 0.5   */  this.colorBlendAmount = defaultValue(options.colorBlendAmount, 0.5);  this._colorShadingEnabled = false;  this._clippingPlanes = undefined;  this.clippingPlanes = options.clippingPlanes;  // Used for checking if shaders need to be regenerated due to clipping plane changes.  this._clippingPlanesState = 0;  // If defined, use this matrix to transform miscellaneous properties like  // clipping planes and IBL instead of the modelMatrix. This is so that when  // models are part of a tileset these properties get transformed relative to  // a common reference (such as the root).  this.referenceMatrix = undefined;  /**   * Whether to cull back-facing geometry. When true, back face culling is   * determined by the material's doubleSided property; when false, back face   * culling is disabled. Back faces are not culled if {@link Model#color} is   * translucent or {@link Model#silhouetteSize} is greater than 0.0.   *   * @type {Boolean}   *   * @default true   */  this.backFaceCulling = defaultValue(options.backFaceCulling, true);  /**   * Whether to display the outline for models using the   * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension.   * When true, outlines are displayed. When false, outlines are not displayed.   *   * @type {Boolean}   * @readonly   *   * @default true   */  this.showOutline = defaultValue(options.showOutline, true);  /**   * The {@link SplitDirection} to apply to this model.   *   * @type {SplitDirection}   * @default {@link SplitDirection.NONE}   */  this.splitDirection = defaultValue(    options.splitDirection,    SplitDirection.NONE  );  this._splittingEnabled = false;  /**   * This property is for debugging only; it is not for production use nor is it optimized.   * <p>   * Draws the bounding sphere for each draw command in the model.  A glTF primitive corresponds   * to one draw command.  A glTF mesh has an array of primitives, often of length one.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugShowBoundingVolume = defaultValue(    options.debugShowBoundingVolume,    false  );  this._debugShowBoundingVolume = false;  /**   * This property is for debugging only; it is not for production use nor is it optimized.   * <p>   * Draws the model in wireframe.   * </p>   *   * @type {Boolean}   *   * @default false   */  this.debugWireframe = defaultValue(options.debugWireframe, false);  this._debugWireframe = false;  this._distanceDisplayCondition = options.distanceDisplayCondition;  // Undocumented options  this._addBatchIdToGeneratedShaders = options.addBatchIdToGeneratedShaders;  this._precreatedAttributes = options.precreatedAttributes;  this._vertexShaderLoaded = options.vertexShaderLoaded;  this._fragmentShaderLoaded = options.fragmentShaderLoaded;  this._uniformMapLoaded = options.uniformMapLoaded;  this._pickIdLoaded = options.pickIdLoaded;  this._ignoreCommands = defaultValue(options.ignoreCommands, false);  this._requestType = options.requestType;  this._upAxis = defaultValue(options.upAxis, Axis.Y);  this._gltfForwardAxis = Axis.Z;  this._forwardAxis = options.forwardAxis;  /**   * @private   * @readonly   */  this.cull = defaultValue(options.cull, true);  /**   * @private   * @readonly   */  this.opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);  this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale  this._clippingPlanesMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from reference matrix and the current view matrix  this._iblReferenceFrameMatrix = Matrix3.clone(Matrix3.IDENTITY); // Derived from reference matrix and the current view matrix  this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins  this._boundingSphere = undefined;  this._scaledBoundingSphere = new BoundingSphere();  this._state = ModelState.NEEDS_LOAD;  this._loadResources = undefined;  this._mode = undefined;  this._perNodeShowDirty = false; // true when the Cesium API was used to change a node's show property  this._cesiumAnimationsDirty = false; // true when the Cesium API, not a glTF animation, changed a node transform  this._dirty = false; // true when the model was transformed this frame  this._maxDirtyNumber = 0; // Used in place of a dirty boolean flag to avoid an extra graph traversal  this._runtime = {    animations: undefined,    articulationsByName: undefined,    articulationsByStageKey: undefined,    stagesByKey: undefined,    rootNodes: undefined,    nodes: undefined, // Indexed with the node's index    nodesByName: undefined, // Indexed with name property in the node    skinnedNodes: undefined,    meshesByName: undefined, // Indexed with the name property in the mesh    materialsByName: undefined, // Indexed with the name property in the material    materialsById: undefined, // Indexed with the material's index  };  this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation  this._extensionsUsed = undefined; // Cached used glTF extensions  this._extensionsRequired = undefined; // Cached required glTF extensions  this._quantizedUniforms = {}; // Quantized uniforms for each program for WEB3D_quantized_attributes  this._programPrimitives = {};  this._rendererResources = {    // Cached between models with the same url/cache-key    buffers: {},    vertexArrays: {},    programs: {},    sourceShaders: {},    silhouettePrograms: {},    textures: {},    samplers: {},    renderStates: {},  };  this._cachedRendererResources = undefined;  this._loadRendererResourcesFromCache = false;  this._dequantizeInShader = defaultValue(options.dequantizeInShader, true);  this._decodedData = {};  this._cachedGeometryByteLength = 0;  this._cachedTexturesByteLength = 0;  this._geometryByteLength = 0;  this._texturesByteLength = 0;  this._trianglesLength = 0;  this._pointsLength = 0;  // Hold references for shader reconstruction.  // Hold these separately because _cachedGltf may get released (this.releaseGltfJson)  this._sourceTechniques = {};  this._sourcePrograms = {};  this._quantizedVertexShaders = {};  this._nodeCommands = [];  this._pickIds = [];  // CESIUM_RTC extension  this._rtcCenter = undefined; // reference to either 3D or 2D  this._rtcCenterEye = undefined; // in eye coordinates  this._rtcCenter3D = undefined; // in world coordinates  this._rtcCenter2D = undefined; // in projected world coordinates  this._sourceVersion = undefined;  this._sourceKHRTechniquesWebGL = undefined;  this._lightColor = Cartesian3.clone(options.lightColor);  const hasIndividualIBLParameters =    defined(options.imageBasedLightingFactor) ||    defined(options.luminanceAtZenith) ||    defined(options.sphericalHarmonicCoefficients) ||    defined(options.specularEnvironmentMaps);  if (defined(options.imageBasedLighting)) {    this._imageBasedLighting = options.imageBasedLighting;    this._shouldDestroyImageBasedLighting = false;  } else if (hasIndividualIBLParameters) {    deprecationWarning(      "ImageBasedLightingConstructor",      "Individual image-based lighting parameters were deprecated in Cesium 1.92. They will be removed in version 1.94. Use options.imageBasedLighting instead."    );    // Create image-based lighting from the old constructor parameters.    this._imageBasedLighting = new ImageBasedLighting({      imageBasedLightingFactor: options.imageBasedLightingFactor,      luminanceAtZenith: options.luminanceAtZenith,      sphericalHarmonicCoefficients: options.sphericalHarmonicCoefficients,      specularEnvironmentMaps: options.specularEnvironmentMaps,    });    this._shouldDestroyImageBasedLighting = true;  } else {    this._imageBasedLighting = new ImageBasedLighting();    this._shouldDestroyImageBasedLighting = true;  }  this._shouldRegenerateShaders = false;}Object.defineProperties(Model.prototype, {  /**   * The object for the glTF JSON, including properties with default values omitted   * from the JSON provided to this model.   *   * @memberof Model.prototype   *   * @type {Object}   * @readonly   *   * @default undefined   */  gltf: {    get: function () {      return defined(this._cachedGltf) ? this._cachedGltf.gltf : undefined;    },  },  /**   * When <code>true</code>, the glTF JSON is not stored with the model once the model is   * loaded (when {@link Model#ready} is <code>true</code>).  This saves memory when   * geometry, textures, and animations are embedded in the .gltf file.   * This is especially useful for cases like 3D buildings, where each .gltf model is unique   * and caching the glTF JSON is not effective.   *   * @memberof Model.prototype   *   * @type {Boolean}   * @readonly   *   * @default false   *   * @private   */  releaseGltfJson: {    get: function () {      return this._releaseGltfJson;    },  },  /**   * The key identifying this model in the model cache for glTF JSON, renderer resources, and animations.   * Caching saves memory and improves loading speed when several models with the same url are created.   * <p>   * This key is automatically generated when the model is created with {@link Model.fromGltf}.  If the model   * is created directly from glTF JSON using the {@link Model} constructor, this key can be manually   * provided; otherwise, the model will not be changed.   * </p>   *   * @memberof Model.prototype   *   * @type {String}   * @readonly   *   * @private   */  cacheKey: {    get: function () {      return this._cacheKey;    },  },  /**   * The base path that paths in the glTF JSON are relative to.  The base   * path is the same path as the path containing the .gltf file   * minus the .gltf file, when binary, image, and shader files are   * in the same directory as the .gltf.  When this is <code>''</code>,   * the app's base path is used.   *   * @memberof Model.prototype   *   * @type {String}   * @readonly   *   * @default ''   */  basePath: {    get: function () {      return this._resource.url;    },  },  /**   * The model's bounding sphere in its local coordinate system.  This does not take into   * account glTF animations and skins nor does it take into account {@link Model#minimumPixelSize}.   *   * @memberof Model.prototype   *   * @type {BoundingSphere}   * @readonly   *   * @default undefined   *   * @exception {DeveloperError} The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true.   *   * @example   * // Center in WGS84 coordinates   * const center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());   */  boundingSphere: {    get: function () {      //>>includeStart('debug', pragmas.debug);      if (this._state !== ModelState.LOADED) {        throw new DeveloperError(          "The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true."        );      }      //>>includeEnd('debug');      let modelMatrix = this.modelMatrix;      if (        this.heightReference !== HeightReference.NONE &&        this._clampedModelMatrix      ) {        modelMatrix = this._clampedModelMatrix;      }      const nonUniformScale = Matrix4.getScale(        modelMatrix,        boundingSphereCartesian3Scratch      );      const scale = defined(this.maximumScale)        ? Math.min(this.maximumScale, this.scale)        : this.scale;      Cartesian3.multiplyByScalar(nonUniformScale, scale, nonUniformScale);      const scaledBoundingSphere = this._scaledBoundingSphere;      scaledBoundingSphere.center = Cartesian3.multiplyComponents(        this._boundingSphere.center,        nonUniformScale,        scaledBoundingSphere.center      );      scaledBoundingSphere.radius =        Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius;      if (defined(this._rtcCenter)) {        Cartesian3.add(          this._rtcCenter,          scaledBoundingSphere.center,          scaledBoundingSphere.center        );      }      return scaledBoundingSphere;    },  },  /**   * When <code>true</code>, this model is ready to render, i.e., the external binary, image,   * and shader files were downloaded and the WebGL resources were created.  This is set to   * <code>true</code> right before {@link Model#readyPromise} is resolved.   *   * @memberof Model.prototype   *   * @type {Boolean}   * @readonly   *   * @default false   */  ready: {    get: function () {      return this._ready;    },  },  /**   * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image,   * and shader files were downloaded and the WebGL resources were created.   * <p>   * This promise is resolved at the end of the frame before the first frame the model is rendered in.   * </p>   *   * @memberof Model.prototype   * @type {Promise.<Model>}   * @readonly   *   * @example   * // Play all animations at half-speed when the model is ready to render   * Promise.resolve(model.readyPromise).then(function(model) {   *   model.activeAnimations.addAll({   *     multiplier : 0.5   *   });   * }).catch(function(error){   *   window.alert(error);   * });   *   * @see Model#ready   */  readyPromise: {    get: function () {      return this._readyPromise.promise;    },  },  /**   * Determines if model WebGL resource creation will be spread out over several frames or   * block until completion once all glTF files are loaded.   *   * @memberof Model.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  asynchronous: {    get: function () {      return this._asynchronous;    },  },  /**   * When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.  When <code>false</code>, GPU memory is saved.   *   * @memberof Model.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  allowPicking: {    get: function () {      return this._allowPicking;    },  },  /**   * Determine if textures may continue to stream in after the model is loaded.   *   * @memberof Model.prototype   *   * @type {Boolean}   * @readonly   *   * @default true   */  incrementallyLoadTextures: {    get: function () {      return this._incrementallyLoadTextures;    },  },  /**   * Return the number of pending texture loads.   *   * @memberof Model.prototype   *   * @type {Number}   * @readonly   */  pendingTextureLoads: {    get: function () {      return defined(this._loadResources)        ? this._loadResources.pendingTextureLoads        : 0;    },  },  /**   * Returns true if the model was transformed this frame   *   * @memberof Model.prototype   *   * @type {Boolean}   * @readonly   *   * @private   */  dirty: {    get: function () {      return this._dirty;    },  },  /**   * Gets or sets the condition specifying at what distance from the camera that this model will be displayed.   * @memberof Model.prototype   * @type {DistanceDisplayCondition}   * @default undefined   */  distanceDisplayCondition: {    get: function () {      return this._distanceDisplayCondition;    },    set: function (value) {      //>>includeStart('debug', pragmas.debug);      if (defined(value) && value.far <= value.near) {        throw new DeveloperError("far must be greater than near");      }      //>>includeEnd('debug');      this._distanceDisplayCondition = DistanceDisplayCondition.clone(        value,        this._distanceDisplayCondition      );    },  },  extensionsUsed: {    get: function () {      if (!defined(this._extensionsUsed)) {        this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf);      }      return this._extensionsUsed;    },  },  extensionsRequired: {    get: function () {      if (!defined(this._extensionsRequired)) {        this._extensionsRequired = ModelUtility.getRequiredExtensions(          this.gltf        );      }      return this._extensionsRequired;    },  },  /**   * Gets the model's up-axis.   * By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up.   *   * @memberof Model.prototype   *   * @type {Number}   * @default Axis.Y   * @readonly   *   * @private   */  upAxis: {    get: function () {      return this._upAxis;    },  },  /**   * Gets the model's forward axis.   * By default, glTF 2.0 models are z-forward according to the glTF spec, however older   * glTF (1.0, 0.8) models used x-forward.  Note that only Axis.X and Axis.Z are supported.   *   * @memberof Model.prototype   *   * @type {Number}   * @default Axis.Z   * @readonly   *   * @private   */  forwardAxis: {    get: function () {      if (defined(this._forwardAxis)) {        return this._forwardAxis;      }      return this._gltfForwardAxis;    },  },  /**   * Gets the model's triangle count.   *   * @private   */  trianglesLength: {    get: function () {      return this._trianglesLength;    },  },  /**   * Gets the model's point count.   *   * @private   */  pointsLength: {    get: function () {      return this._pointsLength;    },  },  /**   * Gets the model's geometry memory in bytes. This includes all vertex and index buffers.   *   * @private   */  geometryByteLength: {    get: function () {      return this._geometryByteLength;    },  },  /**   * Gets the model's texture memory in bytes.   *   * @private   */  texturesByteLength: {    get: function () {      return this._texturesByteLength;    },  },  /**   * Gets the model's cached geometry memory in bytes. This includes all vertex and index buffers.   *   * @private   */  cachedGeometryByteLength: {    get: function () {      return this._cachedGeometryByteLength;    },  },  /**   * Gets the model's cached texture memory in bytes.   *   * @private   */  cachedTexturesByteLength: {    get: function () {      return this._cachedTexturesByteLength;    },  },  /**   * The {@link ClippingPlaneCollection} used to selectively disable rendering the model.   *   * @memberof Model.prototype   *   * @type {ClippingPlaneCollection}   */  clippingPlanes: {    get: function () {      return this._clippingPlanes;    },    set: function (value) {      if (value === this._clippingPlanes) {        return;      }      // Handle destroying, checking of unknown, checking for existing ownership      ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");    },  },  /**   * @private   */  pickIds: {    get: function () {      return this._pickIds;    },  },  /**   * The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.   * <p>   * For example, disabling additional light sources by setting <code>model.imageBasedLightingFactor = new Cesium.Cartesian2(0.0, 0.0)</code> will make the   * model much darker. Here, increasing the intensity of the light source will make the model brighter.   * </p>   *   * @memberof Model.prototype   *   * @type {Cartesian3}   * @default undefined   */  lightColor: {    get: function () {      return this._lightColor;    },    set: function (value) {      const lightColor = this._lightColor;      if (value === lightColor || Cartesian3.equals(value, lightColor)) {        return;      }      this._shouldRegenerateShaders =        this._shouldRegenerateShaders ||        (defined(lightColor) && !defined(value)) ||        (defined(value) && !defined(lightColor));      this._lightColor = Cartesian3.clone(value, lightColor);    },  },  /**   * The properties for managing image-based lighting on this model.   *   * @memberof Model.prototype   *   * @type {ImageBasedLighting}   */  imageBasedLighting: {    get: function () {      return this._imageBasedLighting;    },    set: function (value) {      //>>includeStart('debug', pragmas.debug);      Check.typeOf.object("imageBasedLighting", this._imageBasedLighting);      //>>includeEnd('debug');      if (value !== this._imageBasedLighting) {        if (          this._shouldDestroyImageBasedLighting &&          !this._imageBasedLighting.isDestroyed()        ) {          this._imageBasedLighting.destroy();        }        this._imageBasedLighting = value;        this._shouldDestroyImageBasedLighting = false;        this._shouldRegenerateShaders = true;      }    },  },  /**   * Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final   * diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources.   *   * @memberof Model.prototype   *   * @type {Cartesian2}   * @default Cartesian2(1.0, 1.0)   */  imageBasedLightingFactor: {    get: function () {      return this._imageBasedLighting.imageBasedLightingFactor;    },    set: function (value) {      this._imageBasedLighting.imageBasedLightingFactor = value;    },  },  /**   * The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.   * This is used when {@link Model#specularEnvironmentMaps} and {@link Model#sphericalHarmonicCoefficients} are not defined.   *   * @memberof Model.prototype   *   * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}   * @type {Number}   * @default 0.2   */  luminanceAtZenith: {    get: function () {      return this._imageBasedLighting.luminanceAtZenith;    },    set: function (value) {      this._imageBasedLighting.luminanceAtZenith = value;    },  },  /**   * The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. When <code>undefined</code>, a diffuse irradiance   * computed from the atmosphere color is used.   * <p>   * There are nine <code>Cartesian3</code> coefficients.   * The order of the coefficients is: L<sub>0,0</sub>, L<sub>1,-1</sub>, L<sub>1,0</sub>, L<sub>1,1</sub>, L<sub>2,-2</sub>, L<sub>2,-1</sub>, L<sub>2,0</sub>, L<sub>2,1</sub>, L<sub>2,2</sub>   * </p>   *   * These values can be obtained by preprocessing the environment map using the <code>cmgen</code> tool of   * {@link https://github.com/google/filament/releases|Google's Filament project}. This will also generate a KTX file that can be   * supplied to {@link Model#specularEnvironmentMaps}.   *   * @memberof Model.prototype   *   * @type {Cartesian3[]}   * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}   * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps}   */  sphericalHarmonicCoefficients: {    get: function () {      return this._imageBasedLighting.sphericalHarmonicCoefficients;    },    set: function (value) {      this._imageBasedLighting.sphericalHarmonicCoefficients = value;    },  },  /**   * A URL to a KTX2 file that contains a cube map of the specular lighting and the convoluted specular mipmaps.   *   * @memberof Model.prototype   * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}   * @type {String}   * @see Model#sphericalHarmonicCoefficients   */  specularEnvironmentMaps: {    get: function () {      return this._imageBasedLighting.specularEnvironmentMaps;    },    set: function (value) {      this._imageBasedLighting.specularEnvironmentMaps = value;    },  },  /**   * Gets the credit that will be displayed for the model   * @memberof Model.prototype   * @type {Credit}   */  credit: {    get: function () {      return this._credit;    },  },  /**   * Gets or sets whether the credits of the model will be displayed on the screen   * @memberof Model.prototype   * @type {Boolean}   */  showCreditsOnScreen: {    get: function () {      return this._showCreditsOnScreen;    },    set: function (value) {      if (this._showCreditsOnScreen !== value) {        if (defined(this._credit)) {          this._credit.showOnScreen = value;        }        const resourceCreditsLength = this._resourceCredits.length;        for (let i = 0; i < resourceCreditsLength; i++) {          this._resourceCredits[i].showOnScreen = value;        }        const gltfCreditsLength = this._gltfCredits.length;        for (let i = 0; i < gltfCreditsLength; i++) {          this._gltfCredits[i].showOnScreen = value;        }      }      this._showCreditsOnScreen = value;    },  },});function silhouetteSupported(context) {  return context.stencilBuffer;}function isColorShadingEnabled(model) {  return (    !Color.equals(model.color, Color.WHITE) ||    model.colorBlendMode !== ColorBlendMode.HIGHLIGHT  );}function isClippingEnabled(model) {  const clippingPlanes = model._clippingPlanes;  return (    defined(clippingPlanes) &&    clippingPlanes.enabled &&    clippingPlanes.length !== 0  );}/** * Determines if silhouettes are supported. * * @param {Scene} scene The scene. * @returns {Boolean} <code>true</code> if silhouettes are supported; otherwise, returns <code>false</code> */Model.silhouetteSupported = function (scene) {  return silhouetteSupported(scene.context);};function containsGltfMagic(uint8Array) {  const magic = getMagic(uint8Array);  return magic === "glTF";}/** * <p> * Creates a model from a glTF asset.  When the model is ready to render, i.e., when the external binary, image, * and shader files are downloaded and the WebGL resources are created, the {@link Model#readyPromise} is resolved. * </p> * <p> * The model can be a traditional glTF asset with a .gltf extension or a Binary glTF using the .glb extension. * </p> * <p> * Cesium supports glTF assets with the following extensions: * <ul> * <li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations} * </li><li> * {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression} * </li><li> * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness} * </li><li> * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform} * </li><li> * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_basisu/README.md|KHR_texture_basisu} * </li> * </ul> * </p> * <p> * For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated * relative to a local origin. * </p> * * @param {Object} options Object with the following properties: * @param {Resource|String} options.url The url to the .gltf file. * @param {Resource|String} [options.basePath] The base path that paths in the glTF JSON are relative to. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom. * @param {Number} [options.maximumScale] The maximum scale for the model. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model. * @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead. * @param {ImageBasedLighting} [options.imageBasedLighting] The properties for managing image-based lighting for this tileset. * @param {Cartesian2} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] Scales diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {String} [options.specularEnvironmentMaps] A URL to a KTX2 file that contains a cube map of the specular lighting and the convoluted specular mipmaps. Deprecated in Cesium 1.92, will be removed in Cesium 1.94. * @param {Credit|String} [options.credit] A credit for the model, which is displayed on the canvas. * @param {Boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0. * @param {Boolean} [options.showOutline=true] Whether to display the outline for models using the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. When true, outlines are displayed. When false, outlines are not displayed. * @returns {Model} The newly created model. * * @example * // Example 1. Create a model from a glTF asset * const model = scene.primitives.add(Cesium.Model.fromGltf({ *   url : './duck/duck.gltf' * })); * * @example * // Example 2. Create model and provide all properties and events * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0); * const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin); * * const model = scene.primitives.add(Cesium.Model.fromGltf({ *   url : './duck/duck.gltf', *   show : true,                     // default *   modelMatrix : modelMatrix, *   scale : 2.0,                     // double size *   minimumPixelSize : 128,          // never smaller than 128 pixels *   maximumScale: 20000,             // never larger than 20000 * model size (overrides minimumPixelSize) *   allowPicking : false,            // not pickable *   debugShowBoundingVolume : false, // default *   debugWireframe : false * })); * * model.readyPromise.then(function(model) { *   // Play all animations when the model is ready to render *   model.activeAnimations.addAll(); * }); */Model.fromGltf = function (options) {  //>>includeStart('debug', pragmas.debug);  if (!defined(options) || !defined(options.url)) {    throw new DeveloperError("options.url is required");  }  //>>includeEnd('debug');  const url = options.url;  options = clone(options);  // Create resource for the model file  const modelResource = Resource.createIfNeeded(url);  // Setup basePath to get dependent files  const basePath = defaultValue(options.basePath, modelResource.clone());  const resource = Resource.createIfNeeded(basePath);  // If no cache key is provided, use a GUID.  // Check using a URI to GUID dictionary that we have not already added this model.  let cacheKey = defaultValue(    options.cacheKey,    uriToGuid[getAbsoluteUri(modelResource.url)]  );  if (!defined(cacheKey)) {    cacheKey = createGuid();    uriToGuid[getAbsoluteUri(modelResource.url)] = cacheKey;  }  if (defined(options.basePath) && !defined(options.cacheKey)) {    cacheKey += resource.url;  }  options.cacheKey = cacheKey;  options.basePath = resource;  const model = new Model(options);  let cachedGltf = gltfCache[cacheKey];  if (!defined(cachedGltf)) {    cachedGltf = new CachedGltf({      ready: false,    });    cachedGltf.count = 1;    cachedGltf.modelsToLoad.push(model);    setCachedGltf(model, cachedGltf);    gltfCache[cacheKey] = cachedGltf;    // Add Accept header if we need it    if (!defined(modelResource.headers.Accept)) {      modelResource.headers.Accept = defaultModelAccept;    }    modelResource      .fetchArrayBuffer()      .then(function (arrayBuffer) {        const array = new Uint8Array(arrayBuffer);        if (containsGltfMagic(array)) {          // Load binary glTF          const parsedGltf = parseGlb(array);          cachedGltf.makeReady(parsedGltf);        } else {          // Load text (JSON) glTF          const json = getJsonFromTypedArray(array);          cachedGltf.makeReady(json);        }        const resourceCredits = model._resourceCredits;        const credits = modelResource.credits;        if (defined(credits)) {          const length = credits.length;          for (let i = 0; i < length; i++) {            resourceCredits.push(credits[i]);          }        }      })      .catch(        ModelUtility.getFailedLoadFunction(model, "model", modelResource.url)      );  } else if (!cachedGltf.ready) {    // Cache hit but the fetchArrayBuffer() or fetchText() request is still pending    ++cachedGltf.count;    cachedGltf.modelsToLoad.push(model);  }  // else if the cached glTF is defined and ready, the  // model constructor will pick it up using the cache key.  return model;};/** * For the unit tests to verify model caching. * * @private */Model._gltfCache = gltfCache;function getRuntime(model, runtimeName, name) {  //>>includeStart('debug', pragmas.debug);  if (model._state !== ModelState.LOADED) {    throw new DeveloperError(      "The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true."    );  }  if (!defined(name)) {    throw new DeveloperError("name is required.");  }  //>>includeEnd('debug');  return model._runtime[runtimeName][name];}/** * Returns the glTF node with the given <code>name</code> property.  This is used to * modify a node's transform for animation outside of glTF animations. * * @param {String} name The glTF name of the node. * @returns {ModelNode} The node or <code>undefined</code> if no node with <code>name</code> exists. * * @exception {DeveloperError} The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true. * * @example * // Apply non-uniform scale to node LOD3sp * const node = model.getNode('LOD3sp'); * node.matrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix); */Model.prototype.getNode = function (name) {  const node = getRuntime(this, "nodesByName", name);  return defined(node) ? node.publicNode : undefined;};/** * Returns the glTF mesh with the given <code>name</code> property. * * @param {String} name The glTF name of the mesh. * * @returns {ModelMesh} The mesh or <code>undefined</code> if no mesh with <code>name</code> exists. * * @exception {DeveloperError} The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true. */Model.prototype.getMesh = function (name) {  return getRuntime(this, "meshesByName", name);};/** * Returns the glTF material with the given <code>name</code> property. * * @param {String} name The glTF name of the material. * @returns {ModelMaterial} The material or <code>undefined</code> if no material with <code>name</code> exists. * * @exception {DeveloperError} The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true. */Model.prototype.getMaterial = function (name) {  return getRuntime(this, "materialsByName", name);};/** * Sets the current value of an articulation stage.  After setting one or multiple stage values, call * Model.applyArticulations() to cause the node matrices to be recalculated. * * @param {String} articulationStageKey The name of the articulation, a space, and the name of the stage. * @param {Number} value The numeric value of this stage of the articulation. * * @exception {DeveloperError} The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true. * * @see Model#applyArticulations */Model.prototype.setArticulationStage = function (articulationStageKey, value) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.number("value", value);  //>>includeEnd('debug');  const stage = getRuntime(this, "stagesByKey", articulationStageKey);  const articulation = getRuntime(    this,    "articulationsByStageKey",    articulationStageKey  );  if (defined(stage) && defined(articulation)) {    value = CesiumMath.clamp(value, stage.minimumValue, stage.maximumValue);    if (      !CesiumMath.equalsEpsilon(stage.currentValue, value, articulationEpsilon)    ) {      stage.currentValue = value;      articulation.isDirty = true;    }  }};const scratchArticulationCartesian = new Cartesian3();const scratchArticulationRotation = new Matrix3();/** * Modifies a Matrix4 by applying a transformation for a given value of a stage.  Note this is different usage * from the typical <code>result</code> parameter, in that the incoming value of <code>result</code> is * meaningful.  Various stages of an articulation can be multiplied together, so their * transformations are all merged into a composite Matrix4 representing them all. * * @param {object} stage The stage of an articulation that is being evaluated. * @param {Matrix4} result The matrix to be modified. * @returns {Matrix4} A matrix transformed as requested by the articulation stage. * * @private */function applyArticulationStageMatrix(stage, result) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("stage", stage);  Check.typeOf.object("result", result);  //>>includeEnd('debug');  const value = stage.currentValue;  const cartesian = scratchArticulationCartesian;  let rotation;  switch (stage.type) {    case "xRotate":      rotation = Matrix3.fromRotationX(        CesiumMath.toRadians(value),        scratchArticulationRotation      );      Matrix4.multiplyByMatrix3(result, rotation, result);      break;    case "yRotate":      rotation = Matrix3.fromRotationY(        CesiumMath.toRadians(value),        scratchArticulationRotation      );      Matrix4.multiplyByMatrix3(result, rotation, result);      break;    case "zRotate":      rotation = Matrix3.fromRotationZ(        CesiumMath.toRadians(value),        scratchArticulationRotation      );      Matrix4.multiplyByMatrix3(result, rotation, result);      break;    case "xTranslate":      cartesian.x = value;      cartesian.y = 0.0;      cartesian.z = 0.0;      Matrix4.multiplyByTranslation(result, cartesian, result);      break;    case "yTranslate":      cartesian.x = 0.0;      cartesian.y = value;      cartesian.z = 0.0;      Matrix4.multiplyByTranslation(result, cartesian, result);      break;    case "zTranslate":      cartesian.x = 0.0;      cartesian.y = 0.0;      cartesian.z = value;      Matrix4.multiplyByTranslation(result, cartesian, result);      break;    case "xScale":      cartesian.x = value;      cartesian.y = 1.0;      cartesian.z = 1.0;      Matrix4.multiplyByScale(result, cartesian, result);      break;    case "yScale":      cartesian.x = 1.0;      cartesian.y = value;      cartesian.z = 1.0;      Matrix4.multiplyByScale(result, cartesian, result);      break;    case "zScale":      cartesian.x = 1.0;      cartesian.y = 1.0;      cartesian.z = value;      Matrix4.multiplyByScale(result, cartesian, result);      break;    case "uniformScale":      Matrix4.multiplyByUniformScale(result, value, result);      break;    default:      break;  }  return result;}const scratchApplyArticulationTransform = new Matrix4();/** * Applies any modified articulation stages to the matrix of each node that participates * in any articulation.  Note that this will overwrite any nodeTransformations on participating nodes. * * @exception {DeveloperError} The model is not loaded.  Use Model.readyPromise or wait for Model.ready to be true. */Model.prototype.applyArticulations = function () {  const articulationsByName = this._runtime.articulationsByName;  for (const articulationName in articulationsByName) {    if (articulationsByName.hasOwnProperty(articulationName)) {      const articulation = articulationsByName[articulationName];      if (articulation.isDirty) {        articulation.isDirty = false;        const numNodes = articulation.nodes.length;        for (let n = 0; n < numNodes; ++n) {          const node = articulation.nodes[n];          let transform = Matrix4.clone(            node.originalMatrix,            scratchApplyArticulationTransform          );          const numStages = articulation.stages.length;          for (let s = 0; s < numStages; ++s) {            const stage = articulation.stages[s];            transform = applyArticulationStageMatrix(stage, transform);          }          node.matrix = transform;        }      }    }  }};///////////////////////////////////////////////////////////////////////////function addBuffersToLoadResources(model) {  const gltf = model.gltf;  const loadResources = model._loadResources;  ForEach.buffer(gltf, function (buffer, id) {    loadResources.buffers[id] = buffer.extras._pipeline.source;  });}function bufferLoad(model, id) {  return function (arrayBuffer) {    const loadResources = model._loadResources;    const buffer = new Uint8Array(arrayBuffer);    --loadResources.pendingBufferLoads;    model.gltf.buffers[id].extras._pipeline.source = buffer;  };}function parseBufferViews(model) {  const bufferViews = model.gltf.bufferViews;  const vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate;  // Only ARRAY_BUFFER here.  ELEMENT_ARRAY_BUFFER created below.  ForEach.bufferView(model.gltf, function (bufferView, id) {    if (bufferView.target === WebGLConstants.ARRAY_BUFFER) {      vertexBuffersToCreate.enqueue(id);    }  });  const indexBuffersToCreate = model._loadResources.indexBuffersToCreate;  const indexBufferIds = {};  // The Cesium Renderer requires knowing the datatype for an index buffer  // at creation type, which is not part of the glTF bufferview so loop  // through glTF accessors to create the bufferview's index buffer.  ForEach.accessor(model.gltf, function (accessor) {    const bufferViewId = accessor.bufferView;    if (!defined(bufferViewId)) {      return;    }    const bufferView = bufferViews[bufferViewId];    if (      bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER &&      !defined(indexBufferIds[bufferViewId])    ) {      indexBufferIds[bufferViewId] = true;      indexBuffersToCreate.enqueue({        id: bufferViewId,        componentType: accessor.componentType,      });    }  });}function parseTechniques(model) {  // retain references to gltf techniques  const gltf = model.gltf;  if (!usesExtension(gltf, "KHR_techniques_webgl")) {    return;  }  const sourcePrograms = model._sourcePrograms;  const sourceTechniques = model._sourceTechniques;  const programs = gltf.extensions.KHR_techniques_webgl.programs;  ForEach.technique(gltf, function (technique, techniqueId) {    sourceTechniques[techniqueId] = clone(technique);    const programId = technique.program;    if (!defined(sourcePrograms[programId])) {      sourcePrograms[programId] = clone(programs[programId]);    }  });}function shaderLoad(model, type, id) {  return function (source) {    const loadResources = model._loadResources;    loadResources.shaders[id] = {      source: source,      type: type,      bufferView: undefined,    };    --loadResources.pendingShaderLoads;    model._rendererResources.sourceShaders[id] = source;  };}function parseShaders(model) {  const gltf = model.gltf;  const buffers = gltf.buffers;  const bufferViews = gltf.bufferViews;  const sourceShaders = model._rendererResources.sourceShaders;  ForEach.shader(gltf, function (shader, id) {    // Shader references either uri (external or base64-encoded) or bufferView    if (defined(shader.bufferView)) {      const bufferViewId = shader.bufferView;      const bufferView = bufferViews[bufferViewId];      const bufferId = bufferView.buffer;      const buffer = buffers[bufferId];      const source = getStringFromTypedArray(        buffer.extras._pipeline.source,        bufferView.byteOffset,        bufferView.byteLength      );      sourceShaders[id] = source;    } else if (defined(shader.extras._pipeline.source)) {      sourceShaders[id] = shader.extras._pipeline.source;    } else {      ++model._loadResources.pendingShaderLoads;      const shaderResource = model._resource.getDerivedResource({        url: shader.uri,      });      shaderResource        .fetchText()        .then(shaderLoad(model, shader.type, id))        .catch(          ModelUtility.getFailedLoadFunction(            model,            "shader",            shaderResource.url          )        );    }  });}function parsePrograms(model) {  const sourceTechniques = model._sourceTechniques;  for (const techniqueId in sourceTechniques) {    if (sourceTechniques.hasOwnProperty(techniqueId)) {      const technique = sourceTechniques[techniqueId];      model._loadResources.programsToCreate.enqueue({        programId: technique.program,        techniqueId: techniqueId,      });    }  }}function parseArticulations(model) {  const articulationsByName = {};  const articulationsByStageKey = {};  const runtimeStagesByKey = {};  model._runtime.articulationsByName = articulationsByName;  model._runtime.articulationsByStageKey = articulationsByStageKey;  model._runtime.stagesByKey = runtimeStagesByKey;  const gltf = model.gltf;  if (    !usesExtension(gltf, "AGI_articulations") ||    !defined(gltf.extensions) ||    !defined(gltf.extensions.AGI_articulations)  ) {    return;  }  const gltfArticulations = gltf.extensions.AGI_articulations.articulations;  if (!defined(gltfArticulations)) {    return;  }  const numArticulations = gltfArticulations.length;  for (let i = 0; i < numArticulations; ++i) {    const articulation = clone(gltfArticulations[i]);    articulation.nodes = [];    articulation.isDirty = true;    articulationsByName[articulation.name] = articulation;    const numStages = articulation.stages.length;    for (let s = 0; s < numStages; ++s) {      const stage = articulation.stages[s];      stage.currentValue = stage.initialValue;      const stageKey = `${articulation.name} ${stage.name}`;      articulationsByStageKey[stageKey] = articulation;      runtimeStagesByKey[stageKey] = stage;    }  }}function imageLoad(model, textureId) {  return function (image) {    const loadResources = model._loadResources;    --loadResources.pendingTextureLoads;    // Images transcoded from KTX2 can contain multiple mip levels:    // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu    let mipLevels;    if (Array.isArray(image)) {      // highest detail mip should be level 0      mipLevels = image.slice(1, image.length).map(function (mipLevel) {        return mipLevel.bufferView;      });      image = image[0];    }    loadResources.texturesToCreate.enqueue({      id: textureId,      image: image,      bufferView: image.bufferView,      width: image.width,      height: image.height,      internalFormat: image.internalFormat,      mipLevels: mipLevels,    });  };}const ktx2Regex = /(^data:image\/ktx2)|(\.ktx2$)/i;function parseTextures(model, context, supportsWebP) {  const gltf = model.gltf;  const images = gltf.images;  let uri;  ForEach.texture(gltf, function (texture, id) {    let imageId = texture.source;    if (      defined(texture.extensions) &&      defined(texture.extensions.EXT_texture_webp) &&      supportsWebP    ) {      imageId = texture.extensions.EXT_texture_webp.source;    } else if (      defined(texture.extensions) &&      defined(texture.extensions.KHR_texture_basisu) &&      context.supportsBasis    ) {      imageId = texture.extensions.KHR_texture_basisu.source;    }    const gltfImage = images[imageId];    const bufferViewId = gltfImage.bufferView;    const mimeType = gltfImage.mimeType;    uri = gltfImage.uri;    // Image references either uri (external or base64-encoded) or bufferView    if (defined(bufferViewId)) {      model._loadResources.texturesToCreateFromBufferView.enqueue({        id: id,        image: undefined,        bufferView: bufferViewId,        mimeType: mimeType,      });    } else {      ++model._loadResources.pendingTextureLoads;      const imageResource = model._resource.getDerivedResource({        url: uri,      });      let promise;      if (ktx2Regex.test(uri)) {        promise = loadKTX2(imageResource);      } else {        promise = imageResource.fetchImage({          skipColorSpaceConversion: true,          preferImageBitmap: true,        });      }      promise        .then(imageLoad(model, id, imageId))        .catch(          ModelUtility.getFailedLoadFunction(model, "image", imageResource.url)        );    }  });}const scratchArticulationStageInitialTransform = new Matrix4();function parseNodes(model) {  const runtimeNodes = {};  const runtimeNodesByName = {};  const skinnedNodes = [];  const skinnedNodesIds = model._loadResources.skinnedNodesIds;  const articulationsByName = model._runtime.articulationsByName;  ForEach.node(model.gltf, function (node, id) {    const runtimeNode = {      // Animation targets      matrix: undefined,      translation: undefined,      rotation: undefined,      scale: undefined,      // Per-node show inherited from parent      computedShow: true,      // Computed transforms      transformToRoot: new Matrix4(),      computedMatrix: new Matrix4(),      dirtyNumber: 0, // The frame this node was made dirty by an animation; for graph traversal      // Rendering      commands: [], // empty for transform, light, and camera nodes      // Skinned node      inverseBindMatrices: undefined, // undefined when node is not skinned      bindShapeMatrix: undefined, // undefined when node is not skinned or identity      joints: [], // empty when node is not skinned      computedJointMatrices: [], // empty when node is not skinned      // Joint node      jointName: node.jointName, // undefined when node is not a joint      weights: [],      // Graph pointers      children: [], // empty for leaf nodes      parents: [], // empty for root nodes      // Publicly-accessible ModelNode instance to modify animation targets      publicNode: undefined,    };    runtimeNode.publicNode = new ModelNode(      model,      node,      runtimeNode,      id,      ModelUtility.getTransform(node)    );    runtimeNodes[id] = runtimeNode;    runtimeNodesByName[node.name] = runtimeNode;    if (defined(node.skin)) {      skinnedNodesIds.push(id);      skinnedNodes.push(runtimeNode);    }    if (      defined(node.extensions) &&      defined(node.extensions.AGI_articulations)    ) {      const articulationName =        node.extensions.AGI_articulations.articulationName;      if (defined(articulationName)) {        let transform = Matrix4.clone(          runtimeNode.publicNode.originalMatrix,          scratchArticulationStageInitialTransform        );        const articulation = articulationsByName[articulationName];        articulation.nodes.push(runtimeNode.publicNode);        const numStages = articulation.stages.length;        for (let s = 0; s < numStages; ++s) {          const stage = articulation.stages[s];          transform = applyArticulationStageMatrix(stage, transform);        }        runtimeNode.publicNode.matrix = transform;      }    }  });  model._runtime.nodes = runtimeNodes;  model._runtime.nodesByName = runtimeNodesByName;  model._runtime.skinnedNodes = skinnedNodes;}function parseMaterials(model) {  const gltf = model.gltf;  const techniques = model._sourceTechniques;  const runtimeMaterialsByName = {};  const runtimeMaterialsById = {};  const uniformMaps = model._uniformMaps;  ForEach.material(gltf, function (material, materialId) {    // Allocated now so ModelMaterial can keep a reference to it.    uniformMaps[materialId] = {      uniformMap: undefined,      values: undefined,      jointMatrixUniformName: undefined,      morphWeightsUniformName: undefined,    };    const modelMaterial = new ModelMaterial(model, material, materialId);    if (      defined(material.extensions) &&      defined(material.extensions.KHR_techniques_webgl)    ) {      const techniqueId = material.extensions.KHR_techniques_webgl.technique;      modelMaterial._technique = techniqueId;      modelMaterial._program = techniques[techniqueId].program;      ForEach.materialValue(material, function (value, uniformName) {        if (!defined(modelMaterial._values)) {          modelMaterial._values = {};        }        modelMaterial._values[uniformName] = clone(value);      });    }    runtimeMaterialsByName[material.name] = modelMaterial;    runtimeMaterialsById[materialId] = modelMaterial;  });  model._runtime.materialsByName = runtimeMaterialsByName;  model._runtime.materialsById = runtimeMaterialsById;}function parseMeshes(model) {  const runtimeMeshesByName = {};  const runtimeMaterialsById = model._runtime.materialsById;  ForEach.mesh(model.gltf, function (mesh, meshId) {    runtimeMeshesByName[mesh.name] = new ModelMesh(      mesh,      runtimeMaterialsById,      meshId    );    if (      defined(model.extensionsUsed.WEB3D_quantized_attributes) ||      model._dequantizeInShader    ) {      // Cache primitives according to their program      ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {        const programId = getProgramForPrimitive(model, primitive);        let programPrimitives = model._programPrimitives[programId];        if (!defined(programPrimitives)) {          programPrimitives = {};          model._programPrimitives[programId] = programPrimitives;        }        programPrimitives[`${meshId}.primitive.${primitiveId}`] = primitive;      });    }  });  model._runtime.meshesByName = runtimeMeshesByName;}function parseCredits(model) {  const asset = model.gltf.asset;  const copyright = asset.copyright;  if (!defined(copyright)) {    return;  }  const showOnScreen = model._showCreditsOnScreen;  const credits = copyright.split(";").map(function (string) {    return new Credit(string.trim(), showOnScreen);  });  model._gltfCredits = credits;}///////////////////////////////////////////////////////////////////////////const CreateVertexBufferJob = function () {  this.id = undefined;  this.model = undefined;  this.context = undefined;};CreateVertexBufferJob.prototype.set = function (id, model, context) {  this.id = id;  this.model = model;  this.context = context;};CreateVertexBufferJob.prototype.execute = function () {  createVertexBuffer(this.id, this.model, this.context);};///////////////////////////////////////////////////////////////////////////function createVertexBuffer(bufferViewId, model, context) {  const loadResources = model._loadResources;  const bufferViews = model.gltf.bufferViews;  let bufferView = bufferViews[bufferViewId];  // Use bufferView created at runtime  if (!defined(bufferView)) {    bufferView = loadResources.createdBufferViews[bufferViewId];  }  const vertexBuffer = Buffer.createVertexBuffer({    context: context,    typedArray: loadResources.getBuffer(bufferView),    usage: BufferUsage.STATIC_DRAW,  });  vertexBuffer.vertexArrayDestroyable = false;  model._rendererResources.buffers[bufferViewId] = vertexBuffer;  model._geometryByteLength += vertexBuffer.sizeInBytes;}///////////////////////////////////////////////////////////////////////////const CreateIndexBufferJob = function () {  this.id = undefined;  this.componentType = undefined;  this.model = undefined;  this.context = undefined;};CreateIndexBufferJob.prototype.set = function (  id,  componentType,  model,  context) {  this.id = id;  this.componentType = componentType;  this.model = model;  this.context = context;};CreateIndexBufferJob.prototype.execute = function () {  createIndexBuffer(this.id, this.componentType, this.model, this.context);};///////////////////////////////////////////////////////////////////////////function createIndexBuffer(bufferViewId, componentType, model, context) {  const loadResources = model._loadResources;  const bufferViews = model.gltf.bufferViews;  let bufferView = bufferViews[bufferViewId];  // Use bufferView created at runtime  if (!defined(bufferView)) {    bufferView = loadResources.createdBufferViews[bufferViewId];  }  const indexBuffer = Buffer.createIndexBuffer({    context: context,    typedArray: loadResources.getBuffer(bufferView),    usage: BufferUsage.STATIC_DRAW,    indexDatatype: componentType,  });  indexBuffer.vertexArrayDestroyable = false;  model._rendererResources.buffers[bufferViewId] = indexBuffer;  model._geometryByteLength += indexBuffer.sizeInBytes;}const scratchVertexBufferJob = new CreateVertexBufferJob();const scratchIndexBufferJob = new CreateIndexBufferJob();function createBuffers(model, frameState) {  const loadResources = model._loadResources;  if (loadResources.pendingBufferLoads !== 0) {    return;  }  const context = frameState.context;  const vertexBuffersToCreate = loadResources.vertexBuffersToCreate;  const indexBuffersToCreate = loadResources.indexBuffersToCreate;  let i;  if (model.asynchronous) {    while (vertexBuffersToCreate.length > 0) {      scratchVertexBufferJob.set(vertexBuffersToCreate.peek(), model, context);      if (        !frameState.jobScheduler.execute(scratchVertexBufferJob, JobType.BUFFER)      ) {        break;      }      vertexBuffersToCreate.dequeue();    }    while (indexBuffersToCreate.length > 0) {      i = indexBuffersToCreate.peek();      scratchIndexBufferJob.set(i.id, i.componentType, model, context);      if (        !frameState.jobScheduler.execute(scratchIndexBufferJob, JobType.BUFFER)      ) {        break;      }      indexBuffersToCreate.dequeue();    }  } else {    while (vertexBuffersToCreate.length > 0) {      createVertexBuffer(vertexBuffersToCreate.dequeue(), model, context);    }    while (indexBuffersToCreate.length > 0) {      i = indexBuffersToCreate.dequeue();      createIndexBuffer(i.id, i.componentType, model, context);    }  }}function getProgramForPrimitive(model, primitive) {  const material = model._runtime.materialsById[primitive.material];  if (!defined(material)) {    return;  }  return material._program;}function modifyShaderForQuantizedAttributes(shader, programName, model) {  let primitive;  const primitives = model._programPrimitives[programName];  // If no primitives were cached for this program, there's no need to modify the shader  if (!defined(primitives)) {    return shader;  }  let primitiveId;  for (primitiveId in primitives) {    if (primitives.hasOwnProperty(primitiveId)) {      primitive = primitives[primitiveId];      if (getProgramForPrimitive(model, primitive) === programName) {        break;      }    }  }  // This is not needed after the program is processed, free the memory  model._programPrimitives[programName] = undefined;  let result;  if (model.extensionsUsed.WEB3D_quantized_attributes) {    result = ModelUtility.modifyShaderForQuantizedAttributes(      model.gltf,      primitive,      shader    );    model._quantizedUniforms[programName] = result.uniforms;  } else {    const decodedData = model._decodedData[primitiveId];    if (defined(decodedData)) {      result = ModelUtility.modifyShaderForDracoQuantizedAttributes(        model.gltf,        primitive,        shader,        decodedData.attributes      );    } else {      return shader;    }  }  return result.shader;}function modifyShaderForColor(shader) {  shader = ShaderSource.replaceMain(shader, "gltf_blend_main");  shader +=    "uniform vec4 gltf_color; \n" +    "uniform float gltf_colorBlend; \n" +    "void main() \n" +    "{ \n" +    "    gltf_blend_main(); \n" +    "    gl_FragColor.rgb = mix(gl_FragColor.rgb, gltf_color.rgb, gltf_colorBlend); \n" +    "    float highlight = ceil(gltf_colorBlend); \n" +    "    gl_FragColor.rgb *= mix(gltf_color.rgb, vec3(1.0), highlight); \n" +    "    gl_FragColor.a *= gltf_color.a; \n" +    "} \n";  return shader;}function modifyShader(shader, programName, callback) {  if (defined(callback)) {    shader = callback(shader, programName);  }  return shader;}const CreateProgramJob = function () {  this.programToCreate = undefined;  this.model = undefined;  this.context = undefined;};CreateProgramJob.prototype.set = function (programToCreate, model, context) {  this.programToCreate = programToCreate;  this.model = model;  this.context = context;};CreateProgramJob.prototype.execute = function () {  createProgram(this.programToCreate, this.model, this.context);};///////////////////////////////////////////////////////////////////////////// When building programs for the first time, do not include modifiers for clipping planes and color// since this is the version of the program that will be cached for use with other Models.function createProgram(programToCreate, model, context) {  const programId = programToCreate.programId;  const techniqueId = programToCreate.techniqueId;  const program = model._sourcePrograms[programId];  const shaders = model._rendererResources.sourceShaders;  let vs = shaders[program.vertexShader];  const fs = shaders[program.fragmentShader];  const quantizedVertexShaders = model._quantizedVertexShaders;  if (    model.extensionsUsed.WEB3D_quantized_attributes ||    model._dequantizeInShader  ) {    let quantizedVS = quantizedVertexShaders[programId];    if (!defined(quantizedVS)) {      quantizedVS = modifyShaderForQuantizedAttributes(vs, programId, model);      quantizedVertexShaders[programId] = quantizedVS;    }    vs = quantizedVS;  }  const drawVS = modifyShader(vs, programId, model._vertexShaderLoaded);  let drawFS = modifyShader(fs, programId, model._fragmentShaderLoaded);  if (!defined(model._uniformMapLoaded)) {    drawFS = `uniform vec4 czm_pickColor;\n${drawFS}`;  }  const imageBasedLighting = model._imageBasedLighting;  const useIBL = imageBasedLighting.enabled;  if (useIBL) {    drawFS = `#define USE_IBL_LIGHTING \n\n${drawFS}`;  }  if (defined(model._lightColor)) {    drawFS = `#define USE_CUSTOM_LIGHT_COLOR \n\n${drawFS}`;  }  if (model._sourceVersion !== "2.0" || model._sourceKHRTechniquesWebGL) {    drawFS = ShaderSource.replaceMain(drawFS, "non_gamma_corrected_main");    drawFS =      `${drawFS}\n` +      `void main() { \n` +      `    non_gamma_corrected_main(); \n` +      `    gl_FragColor = czm_gammaCorrect(gl_FragColor); \n` +      `} \n`;  }  if (OctahedralProjectedCubeMap.isSupported(context)) {    const useSHC = imageBasedLighting.useSphericalHarmonicCoefficients;    const useSEM = imageBasedLighting.useSpecularEnvironmentMaps;    const addMatrix = useSHC || useSEM || useIBL;    if (addMatrix) {      drawFS = `uniform mat3 gltf_iblReferenceFrameMatrix; \n${drawFS}`;    }    if (defined(imageBasedLighting.sphericalHarmonicCoefficients)) {      drawFS = `${        "#define DIFFUSE_IBL \n" +        "#define CUSTOM_SPHERICAL_HARMONICS \n" +        "uniform vec3 gltf_sphericalHarmonicCoefficients[9]; \n"      }${drawFS}`;    } else if (imageBasedLighting.useDefaultSphericalHarmonics) {      drawFS = `#define DIFFUSE_IBL \n${drawFS}`;    }    if (      defined(imageBasedLighting.specularEnvironmentMapAtlas) &&      imageBasedLighting.specularEnvironmentMapAtlas.ready    ) {      drawFS = `${        "#define SPECULAR_IBL \n" +        "#define CUSTOM_SPECULAR_IBL \n" +        "uniform sampler2D gltf_specularMap; \n" +        "uniform vec2 gltf_specularMapSize; \n" +        "uniform float gltf_maxSpecularLOD; \n"      }${drawFS}`;    } else if (imageBasedLighting.useDefaultSpecularMaps) {      drawFS = `#define SPECULAR_IBL \n${drawFS}`;    }  }  if (defined(imageBasedLighting.luminanceAtZenith)) {    drawFS = `${      "#define USE_SUN_LUMINANCE \n" + "uniform float gltf_luminanceAtZenith;\n"    }${drawFS}`;  }  createAttributesAndProgram(    programId,    techniqueId,    drawFS,    drawVS,    model,    context  );}function recreateProgram(programToCreate, model, context) {  const programId = programToCreate.programId;  const techniqueId = programToCreate.techniqueId;  const program = model._sourcePrograms[programId];  const shaders = model._rendererResources.sourceShaders;  const quantizedVertexShaders = model._quantizedVertexShaders;  const clippingPlaneCollection = model.clippingPlanes;  const addClippingPlaneCode = isClippingEnabled(model);  let vs = shaders[program.vertexShader];  const fs = shaders[program.fragmentShader];  if (    model.extensionsUsed.WEB3D_quantized_attributes ||    model._dequantizeInShader  ) {    vs = quantizedVertexShaders[programId];  }  let finalFS = fs;  if (isColorShadingEnabled(model)) {    finalFS = Model._modifyShaderForColor(finalFS);  }  if (addClippingPlaneCode) {    finalFS = modifyShaderForClippingPlanes(      finalFS,      clippingPlaneCollection,      context    );  }  if (model.splitDirection !== SplitDirection.NONE) {    finalFS = Splitter.modifyFragmentShader(finalFS);  }  const drawVS = modifyShader(vs, programId, model._vertexShaderLoaded);  let drawFS = modifyShader(finalFS, programId, model._fragmentShaderLoaded);  if (!defined(model._uniformMapLoaded)) {    drawFS = `uniform vec4 czm_pickColor;\n${drawFS}`;  }  const imageBasedLighting = model._imageBasedLighting;  const useIBL = imageBasedLighting.enabled;  if (useIBL) {    drawFS = `#define USE_IBL_LIGHTING \n\n${drawFS}`;  }  if (defined(model._lightColor)) {    drawFS = `#define USE_CUSTOM_LIGHT_COLOR \n\n${drawFS}`;  }  if (model._sourceVersion !== "2.0" || model._sourceKHRTechniquesWebGL) {    drawFS = ShaderSource.replaceMain(drawFS, "non_gamma_corrected_main");    drawFS =      `${drawFS}\n` +      `void main() { \n` +      `    non_gamma_corrected_main(); \n` +      `    gl_FragColor = czm_gammaCorrect(gl_FragColor); \n` +      `} \n`;  }  if (OctahedralProjectedCubeMap.isSupported(context)) {    const useSHC = imageBasedLighting.useSphericalHarmonicCoefficients;    const useSEM = imageBasedLighting.useSpecularEnvironmentMaps;    const addMatrix = useSHC || useSEM || useIBL;    if (addMatrix) {      drawFS = `uniform mat3 gltf_iblReferenceFrameMatrix; \n${drawFS}`;    }    if (defined(imageBasedLighting.sphericalHarmonicCoefficients)) {      drawFS = `${        "#define DIFFUSE_IBL \n" +        "#define CUSTOM_SPHERICAL_HARMONICS \n" +        "uniform vec3 gltf_sphericalHarmonicCoefficients[9]; \n"      }${drawFS}`;    } else if (imageBasedLighting.useDefaultSphericalHarmonics) {      drawFS = `#define DIFFUSE_IBL \n${drawFS}`;    }    if (      defined(imageBasedLighting.specularEnvironmentMapAtlas) &&      imageBasedLighting.specularEnvironmentMapAtlas.ready    ) {      drawFS = `${        "#define SPECULAR_IBL \n" +        "#define CUSTOM_SPECULAR_IBL \n" +        "uniform sampler2D gltf_specularMap; \n" +        "uniform vec2 gltf_specularMapSize; \n" +        "uniform float gltf_maxSpecularLOD; \n"      }${drawFS}`;    } else if (imageBasedLighting.useDefaultSpecularMaps) {      drawFS = `#define SPECULAR_IBL \n${drawFS}`;    }  }  if (defined(imageBasedLighting.luminanceAtZenith)) {    drawFS = `${      "#define USE_SUN_LUMINANCE \n" + "uniform float gltf_luminanceAtZenith;\n"    }${drawFS}`;  }  createAttributesAndProgram(    programId,    techniqueId,    drawFS,    drawVS,    model,    context  );}function createAttributesAndProgram(  programId,  techniqueId,  drawFS,  drawVS,  model,  context) {  const technique = model._sourceTechniques[techniqueId];  const attributeLocations = ModelUtility.createAttributeLocations(    technique,    model._precreatedAttributes  );  model._rendererResources.programs[programId] = ShaderProgram.fromCache({    context: context,    vertexShaderSource: drawVS,    fragmentShaderSource: drawFS,    attributeLocations: attributeLocations,  });}const scratchCreateProgramJob = new CreateProgramJob();function createPrograms(model, frameState) {  const loadResources = model._loadResources;  const programsToCreate = loadResources.programsToCreate;  if (loadResources.pendingShaderLoads !== 0) {    return;  }  // PERFORMANCE_IDEA: this could be more fine-grained by looking  // at the shader's bufferView's to determine the buffer dependencies.  if (loadResources.pendingBufferLoads !== 0) {    return;  }  const context = frameState.context;  if (model.asynchronous) {    while (programsToCreate.length > 0) {      scratchCreateProgramJob.set(programsToCreate.peek(), model, context);      if (        !frameState.jobScheduler.execute(          scratchCreateProgramJob,          JobType.PROGRAM        )      ) {        break;      }      programsToCreate.dequeue();    }  } else {    // Create all loaded programs this frame    while (programsToCreate.length > 0) {      createProgram(programsToCreate.dequeue(), model, context);    }  }}function getOnImageCreatedFromTypedArray(loadResources, gltfTexture) {  return function (image) {    loadResources.texturesToCreate.enqueue({      id: gltfTexture.id,      image: image,      bufferView: undefined,    });    --loadResources.pendingBufferViewToImage;  };}function loadTexturesFromBufferViews(model) {  const loadResources = model._loadResources;  if (loadResources.pendingBufferLoads !== 0) {    return;  }  while (loadResources.texturesToCreateFromBufferView.length > 0) {    const gltfTexture = loadResources.texturesToCreateFromBufferView.dequeue();    const gltf = model.gltf;    const bufferView = gltf.bufferViews[gltfTexture.bufferView];    const imageId = gltf.textures[gltfTexture.id].source;    const onerror = ModelUtility.getFailedLoadFunction(      model,      "image",      `id: ${gltfTexture.id}, bufferView: ${gltfTexture.bufferView}`    );    if (gltfTexture.mimeType === "image/ktx2") {      // Need to make a copy of the embedded KTX2 buffer otherwise the underlying      // ArrayBuffer may be accessed on both the worker and the main thread and      // throw an error like "Cannot perform Construct on a detached ArrayBuffer".      // Look into SharedArrayBuffer at some point to get around this.      const ktxBuffer = new Uint8Array(loadResources.getBuffer(bufferView));      loadKTX2(ktxBuffer)        .then(imageLoad(model, gltfTexture.id, imageId))        .catch(onerror);      ++model._loadResources.pendingTextureLoads;    } else {      const onload = getOnImageCreatedFromTypedArray(        loadResources,        gltfTexture      );      loadImageFromTypedArray({        uint8Array: loadResources.getBuffer(bufferView),        format: gltfTexture.mimeType,        flipY: false,        skipColorSpaceConversion: true,      })        .then(onload)        .catch(onerror);      ++loadResources.pendingBufferViewToImage;    }  }}function createSamplers(model) {  const loadResources = model._loadResources;  if (loadResources.createSamplers) {    loadResources.createSamplers = false;    const rendererSamplers = model._rendererResources.samplers;    ForEach.sampler(model.gltf, function (sampler, samplerId) {      rendererSamplers[samplerId] = new Sampler({        wrapS: sampler.wrapS,        wrapT: sampler.wrapT,        minificationFilter: sampler.minFilter,        magnificationFilter: sampler.magFilter,      });    });  }}///////////////////////////////////////////////////////////////////////////const CreateTextureJob = function () {  this.gltfTexture = undefined;  this.model = undefined;  this.context = undefined;};CreateTextureJob.prototype.set = function (gltfTexture, model, context) {  this.gltfTexture = gltfTexture;  this.model = model;  this.context = context;};CreateTextureJob.prototype.execute = function () {  createTexture(this.gltfTexture, this.model, this.context);};///////////////////////////////////////////////////////////////////////////function createTexture(gltfTexture, model, context) {  const textures = model.gltf.textures;  const texture = textures[gltfTexture.id];  const rendererSamplers = model._rendererResources.samplers;  let sampler = rendererSamplers[texture.sampler];  if (!defined(sampler)) {    sampler = new Sampler({      wrapS: TextureWrap.REPEAT,      wrapT: TextureWrap.REPEAT,    });  }  let usesTextureTransform = false;  const materials = model.gltf.materials;  const materialsLength = materials.length;  for (let i = 0; i < materialsLength; ++i) {    const material = materials[i];    if (      defined(material.extensions) &&      defined(material.extensions.KHR_techniques_webgl)    ) {      const values = material.extensions.KHR_techniques_webgl.values;      for (const valueName in values) {        if (          values.hasOwnProperty(valueName) &&          valueName.indexOf("Texture") !== -1        ) {          const value = values[valueName];          if (            value.index === gltfTexture.id &&            defined(value.extensions) &&            defined(value.extensions.KHR_texture_transform)          ) {            usesTextureTransform = true;            break;          }        }      }    }    if (usesTextureTransform) {      break;    }  }  const wrapS = sampler.wrapS;  const wrapT = sampler.wrapT;  let minFilter = sampler.minificationFilter;  if (    usesTextureTransform &&    minFilter !== TextureMinificationFilter.LINEAR &&    minFilter !== TextureMinificationFilter.NEAREST  ) {    if (      minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||      minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR    ) {      minFilter = TextureMinificationFilter.NEAREST;    } else {      minFilter = TextureMinificationFilter.LINEAR;    }    sampler = new Sampler({      wrapS: sampler.wrapS,      wrapT: sampler.wrapT,      minificationFilter: minFilter,      magnificationFilter: sampler.magnificationFilter,    });  }  const internalFormat = gltfTexture.internalFormat;  const mipmap =    !(      defined(internalFormat) && PixelFormat.isCompressedFormat(internalFormat)    ) &&    (minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||      minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR ||      minFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST ||      minFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR);  const requiresNpot =    mipmap ||    wrapS === TextureWrap.REPEAT ||    wrapS === TextureWrap.MIRRORED_REPEAT ||    wrapT === TextureWrap.REPEAT ||    wrapT === TextureWrap.MIRRORED_REPEAT;  let npot;  let tx;  let source = gltfTexture.image;  if (defined(internalFormat)) {    npot =      !CesiumMath.isPowerOfTwo(gltfTexture.width) ||      !CesiumMath.isPowerOfTwo(gltfTexture.height);    // Warning to encourage power of 2 texture dimensions with KHR_texture_basisu    if (      !context.webgl2 &&      PixelFormat.isCompressedFormat(internalFormat) &&      npot &&      requiresNpot    ) {      console.warn(        "Compressed texture uses REPEAT or MIRRORED_REPEAT texture wrap mode and dimensions are not powers of two. The texture may be rendered incorrectly. See the Model.js constructor documentation for more information."      );    }    let minificationFilter = sampler.minificationFilter;    if (      !defined(gltfTexture.mipLevels) &&      (minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||        minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR)    ) {      minificationFilter = TextureMinificationFilter.NEAREST;    } else if (      !defined(gltfTexture.mipLevels) &&      (minFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST ||        minFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR)    ) {      minificationFilter = TextureMinificationFilter.LINEAR;    }    sampler = new Sampler({      wrapS: sampler.wrapS,      wrapT: sampler.wrapT,      minificationFilter: minificationFilter,      magnificationFilter: sampler.magnificationFilter,    });    tx = new Texture({      context: context,      source: {        arrayBufferView: gltfTexture.bufferView,        mipLevels: gltfTexture.mipLevels,      },      width: gltfTexture.width,      height: gltfTexture.height,      pixelFormat: internalFormat,      sampler: sampler,    });  } else if (defined(source)) {    npot =      !CesiumMath.isPowerOfTwo(source.width) ||      !CesiumMath.isPowerOfTwo(source.height);    if (requiresNpot && npot) {      // WebGL requires power-of-two texture dimensions for mipmapping and REPEAT/MIRRORED_REPEAT wrap modes.      const canvas = document.createElement("canvas");      canvas.width = CesiumMath.nextPowerOfTwo(source.width);      canvas.height = CesiumMath.nextPowerOfTwo(source.height);      const canvasContext = canvas.getContext("2d");      canvasContext.drawImage(        source,        0,        0,        source.width,        source.height,        0,        0,        canvas.width,        canvas.height      );      source = canvas;    }    tx = new Texture({      context: context,      source: source,      pixelFormat: texture.internalFormat,      pixelDatatype: texture.type,      sampler: sampler,      flipY: false,      skipColorSpaceConversion: true,    });    // GLTF_SPEC: Support TEXTURE_CUBE_MAP.  https://github.com/KhronosGroup/glTF/issues/40    if (mipmap) {      tx.generateMipmap();    }  }  if (defined(tx)) {    model._rendererResources.textures[gltfTexture.id] = tx;    model._texturesByteLength += tx.sizeInBytes;  }}const scratchCreateTextureJob = new CreateTextureJob();function createTextures(model, frameState) {  const context = frameState.context;  const texturesToCreate = model._loadResources.texturesToCreate;  if (model.asynchronous) {    while (texturesToCreate.length > 0) {      scratchCreateTextureJob.set(texturesToCreate.peek(), model, context);      if (        !frameState.jobScheduler.execute(          scratchCreateTextureJob,          JobType.TEXTURE        )      ) {        break;      }      texturesToCreate.dequeue();    }  } else {    // Create all loaded textures this frame    while (texturesToCreate.length > 0) {      createTexture(texturesToCreate.dequeue(), model, context);    }  }}function getAttributeLocations(model, primitive) {  const techniques = model._sourceTechniques;  // Retrieve the compiled shader program to assign index values to attributes  const attributeLocations = {};  let location;  let index;  const material = model._runtime.materialsById[primitive.material];  if (!defined(material)) {    return attributeLocations;  }  const technique = techniques[material._technique];  if (!defined(technique)) {    return attributeLocations;  }  const attributes = technique.attributes;  const program = model._rendererResources.programs[technique.program];  const programAttributeLocations = program._attributeLocations;  for (location in programAttributeLocations) {    if (programAttributeLocations.hasOwnProperty(location)) {      const attribute = attributes[location];      if (defined(attribute)) {        index = programAttributeLocations[location];        attributeLocations[attribute.semantic] = index;      }    }  }  // Add pre-created attributes.  const precreatedAttributes = model._precreatedAttributes;  if (defined(precreatedAttributes)) {    for (location in precreatedAttributes) {      if (precreatedAttributes.hasOwnProperty(location)) {        index = programAttributeLocations[location];        attributeLocations[location] = index;      }    }  }  return attributeLocations;}function createJoints(model, runtimeSkins) {  const gltf = model.gltf;  const skins = gltf.skins;  const nodes = gltf.nodes;  const runtimeNodes = model._runtime.nodes;  const skinnedNodesIds = model._loadResources.skinnedNodesIds;  const length = skinnedNodesIds.length;  for (let j = 0; j < length; ++j) {    const id = skinnedNodesIds[j];    const skinnedNode = runtimeNodes[id];    const node = nodes[id];    const runtimeSkin = runtimeSkins[node.skin];    skinnedNode.inverseBindMatrices = runtimeSkin.inverseBindMatrices;    skinnedNode.bindShapeMatrix = runtimeSkin.bindShapeMatrix;    const gltfJoints = skins[node.skin].joints;    const jointsLength = gltfJoints.length;    for (let i = 0; i < jointsLength; ++i) {      const nodeId = gltfJoints[i];      const jointNode = runtimeNodes[nodeId];      skinnedNode.joints.push(jointNode);    }  }}function createSkins(model) {  const loadResources = model._loadResources;  if (loadResources.pendingBufferLoads !== 0) {    return;  }  if (!loadResources.createSkins) {    return;  }  loadResources.createSkins = false;  const gltf = model.gltf;  const accessors = gltf.accessors;  const runtimeSkins = {};  ForEach.skin(gltf, function (skin, id) {    const accessor = accessors[skin.inverseBindMatrices];    let bindShapeMatrix;    if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) {      bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix);    }    runtimeSkins[id] = {      inverseBindMatrices: ModelAnimationCache.getSkinInverseBindMatrices(        model,        accessor      ),      bindShapeMatrix: bindShapeMatrix, // not used when undefined    };  });  createJoints(model, runtimeSkins);}function getChannelEvaluator(model, runtimeNode, targetPath, spline) {  return function (localAnimationTime) {    if (defined(spline)) {      localAnimationTime = model.clampAnimations        ? spline.clampTime(localAnimationTime)        : spline.wrapTime(localAnimationTime);      runtimeNode[targetPath] = spline.evaluate(        localAnimationTime,        runtimeNode[targetPath]      );      runtimeNode.dirtyNumber = model._maxDirtyNumber;    }  };}function createRuntimeAnimations(model) {  const loadResources = model._loadResources;  if (!loadResources.finishedPendingBufferLoads()) {    return;  }  if (!loadResources.createRuntimeAnimations) {    return;  }  loadResources.createRuntimeAnimations = false;  model._runtime.animations = [];  const runtimeNodes = model._runtime.nodes;  const accessors = model.gltf.accessors;  ForEach.animation(model.gltf, function (animation, i) {    const channels = animation.channels;    const samplers = animation.samplers;    // Find start and stop time for the entire animation    let startTime = Number.MAX_VALUE;    let stopTime = -Number.MAX_VALUE;    const channelsLength = channels.length;    const channelEvaluators = new Array(channelsLength);    for (let j = 0; j < channelsLength; ++j) {      const channel = channels[j];      const target = channel.target;      const path = target.path;      const sampler = samplers[channel.sampler];      const input = ModelAnimationCache.getAnimationParameterValues(        model,        accessors[sampler.input]      );      const output = ModelAnimationCache.getAnimationParameterValues(        model,        accessors[sampler.output]      );      startTime = Math.min(startTime, input[0]);      stopTime = Math.max(stopTime, input[input.length - 1]);      const spline = ModelAnimationCache.getAnimationSpline(        model,        i,        animation,        channel.sampler,        sampler,        input,        path,        output      );      channelEvaluators[j] = getChannelEvaluator(        model,        runtimeNodes[target.node],        target.path,        spline      );    }    model._runtime.animations[i] = {      name: animation.name,      startTime: startTime,      stopTime: stopTime,      channelEvaluators: channelEvaluators,    };  });}function createVertexArrays(model, context) {  const loadResources = model._loadResources;  if (    !loadResources.finishedBuffersCreation() ||    !loadResources.finishedProgramCreation() ||    !loadResources.createVertexArrays  ) {    return;  }  loadResources.createVertexArrays = false;  const rendererBuffers = model._rendererResources.buffers;  const rendererVertexArrays = model._rendererResources.vertexArrays;  const gltf = model.gltf;  const accessors = gltf.accessors;  ForEach.mesh(gltf, function (mesh, meshId) {    ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {      const attributes = [];      let attributeLocation;      const attributeLocations = getAttributeLocations(model, primitive);      const decodedData =        model._decodedData[`${meshId}.primitive.${primitiveId}`];      ForEach.meshPrimitiveAttribute(primitive, function (        accessorId,        attributeName      ) {        // Skip if the attribute is not used by the material, e.g., because the asset        // was exported with an attribute that wasn't used and the asset wasn't optimized.        attributeLocation = attributeLocations[attributeName];        if (defined(attributeLocation)) {          // Use attributes of previously decoded draco geometry          if (defined(decodedData)) {            const decodedAttributes = decodedData.attributes;            if (decodedAttributes.hasOwnProperty(attributeName)) {              const decodedAttribute = decodedAttributes[attributeName];              attributes.push({                index: attributeLocation,                vertexBuffer: rendererBuffers[decodedAttribute.bufferView],                componentsPerAttribute: decodedAttribute.componentsPerAttribute,                componentDatatype: decodedAttribute.componentDatatype,                normalize: decodedAttribute.normalized,                offsetInBytes: decodedAttribute.byteOffset,                strideInBytes: decodedAttribute.byteStride,              });              return;            }          }          const a = accessors[accessorId];          const normalize = defined(a.normalized) && a.normalized;          attributes.push({            index: attributeLocation,            vertexBuffer: rendererBuffers[a.bufferView],            componentsPerAttribute: numberOfComponentsForType(a.type),            componentDatatype: a.componentType,            normalize: normalize,            offsetInBytes: a.byteOffset,            strideInBytes: getAccessorByteStride(gltf, a),          });        }      });      // Add pre-created attributes      let attribute;      let attributeName;      const precreatedAttributes = model._precreatedAttributes;      if (defined(precreatedAttributes)) {        for (attributeName in precreatedAttributes) {          if (precreatedAttributes.hasOwnProperty(attributeName)) {            attributeLocation = attributeLocations[attributeName];            if (defined(attributeLocation)) {              attribute = precreatedAttributes[attributeName];              attribute.index = attributeLocation;              attributes.push(attribute);            }          }        }      }      let indexBuffer;      if (defined(primitive.indices)) {        const accessor = accessors[primitive.indices];        let bufferView = accessor.bufferView;        // Use buffer of previously decoded draco geometry        if (defined(decodedData)) {          bufferView = decodedData.bufferView;        }        indexBuffer = rendererBuffers[bufferView];      }      rendererVertexArrays[        `${meshId}.primitive.${primitiveId}`      ] = new VertexArray({        context: context,        attributes: attributes,        indexBuffer: indexBuffer,      });    });  });}function createRenderStates(model) {  const loadResources = model._loadResources;  if (loadResources.createRenderStates) {    loadResources.createRenderStates = false;    ForEach.material(model.gltf, function (material, materialId) {      createRenderStateForMaterial(model, material, materialId);    });  }}function createRenderStateForMaterial(model, material, materialId) {  const rendererRenderStates = model._rendererResources.renderStates;  let blendEquationSeparate = [    WebGLConstants.FUNC_ADD,    WebGLConstants.FUNC_ADD,  ];  let blendFuncSeparate = [    WebGLConstants.ONE,    WebGLConstants.ONE_MINUS_SRC_ALPHA,    WebGLConstants.ONE,    WebGLConstants.ONE_MINUS_SRC_ALPHA,  ];  if (defined(material.extensions) && defined(material.extensions.KHR_blend)) {    blendEquationSeparate = material.extensions.KHR_blend.blendEquation;    blendFuncSeparate = material.extensions.KHR_blend.blendFactors;  }  const enableCulling = !material.doubleSided;  const blendingEnabled = material.alphaMode === "BLEND";  rendererRenderStates[materialId] = RenderState.fromCache({    cull: {      enabled: enableCulling,    },    depthTest: {      enabled: true,      func: DepthFunction.LESS_OR_EQUAL,    },    depthMask: !blendingEnabled,    blending: {      enabled: blendingEnabled,      equationRgb: blendEquationSeparate[0],      equationAlpha: blendEquationSeparate[1],      functionSourceRgb: blendFuncSeparate[0],      functionDestinationRgb: blendFuncSeparate[1],      functionSourceAlpha: blendFuncSeparate[2],      functionDestinationAlpha: blendFuncSeparate[3],    },  });}///////////////////////////////////////////////////////////////////////////const gltfUniformsFromNode = {  MODEL: function (uniformState, model, runtimeNode) {    return function () {      return runtimeNode.computedMatrix;    };  },  VIEW: function (uniformState, model, runtimeNode) {    return function () {      return uniformState.view;    };  },  PROJECTION: function (uniformState, model, runtimeNode) {    return function () {      return uniformState.projection;    };  },  MODELVIEW: function (uniformState, model, runtimeNode) {    const mv = new Matrix4();    return function () {      return Matrix4.multiplyTransformation(        uniformState.view,        runtimeNode.computedMatrix,        mv      );    };  },  CESIUM_RTC_MODELVIEW: function (uniformState, model, runtimeNode) {    // CESIUM_RTC extension    const mvRtc = new Matrix4();    return function () {      Matrix4.multiplyTransformation(        uniformState.view,        runtimeNode.computedMatrix,        mvRtc      );      return Matrix4.setTranslation(mvRtc, model._rtcCenterEye, mvRtc);    };  },  MODELVIEWPROJECTION: function (uniformState, model, runtimeNode) {    const mvp = new Matrix4();    return function () {      Matrix4.multiplyTransformation(        uniformState.view,        runtimeNode.computedMatrix,        mvp      );      return Matrix4.multiply(uniformState._projection, mvp, mvp);    };  },  MODELINVERSE: function (uniformState, model, runtimeNode) {    const mInverse = new Matrix4();    return function () {      return Matrix4.inverse(runtimeNode.computedMatrix, mInverse);    };  },  VIEWINVERSE: function (uniformState, model) {    return function () {      return uniformState.inverseView;    };  },  PROJECTIONINVERSE: function (uniformState, model, runtimeNode) {    return function () {      return uniformState.inverseProjection;    };  },  MODELVIEWINVERSE: function (uniformState, model, runtimeNode) {    const mv = new Matrix4();    const mvInverse = new Matrix4();    return function () {      Matrix4.multiplyTransformation(        uniformState.view,        runtimeNode.computedMatrix,        mv      );      return Matrix4.inverse(mv, mvInverse);    };  },  MODELVIEWPROJECTIONINVERSE: function (uniformState, model, runtimeNode) {    const mvp = new Matrix4();    const mvpInverse = new Matrix4();    return function () {      Matrix4.multiplyTransformation(        uniformState.view,        runtimeNode.computedMatrix,        mvp      );      Matrix4.multiply(uniformState._projection, mvp, mvp);      return Matrix4.inverse(mvp, mvpInverse);    };  },  MODELINVERSETRANSPOSE: function (uniformState, model, runtimeNode) {    const mInverse = new Matrix4();    const mInverseTranspose = new Matrix3();    return function () {      Matrix4.inverse(runtimeNode.computedMatrix, mInverse);      Matrix4.getMatrix3(mInverse, mInverseTranspose);      return Matrix3.transpose(mInverseTranspose, mInverseTranspose);    };  },  MODELVIEWINVERSETRANSPOSE: function (uniformState, model, runtimeNode) {    const mv = new Matrix4();    const mvInverse = new Matrix4();    const mvInverseTranspose = new Matrix3();    return function () {      Matrix4.multiplyTransformation(        uniformState.view,        runtimeNode.computedMatrix,        mv      );      Matrix4.inverse(mv, mvInverse);      Matrix4.getMatrix3(mvInverse, mvInverseTranspose);      return Matrix3.transpose(mvInverseTranspose, mvInverseTranspose);    };  },  VIEWPORT: function (uniformState, model, runtimeNode) {    return function () {      return uniformState.viewportCartesian4;    };  },};function getUniformFunctionFromSource(source, model, semantic, uniformState) {  const runtimeNode = model._runtime.nodes[source];  return gltfUniformsFromNode[semantic](uniformState, model, runtimeNode);}function createUniformsForMaterial(  model,  material,  technique,  instanceValues,  context,  textures,  defaultTexture) {  const uniformMap = {};  const uniformValues = {};  let jointMatrixUniformName;  let morphWeightsUniformName;  ForEach.techniqueUniform(technique, function (uniform, uniformName) {    // GLTF_SPEC: This does not take into account uniform arrays,    // indicated by uniforms with a count property.    //    // https://github.com/KhronosGroup/glTF/issues/258    // GLTF_SPEC: In this implementation, material parameters with a    // semantic or targeted via a source (for animation) are not    // targetable for material animations.  Is this too strict?    //    // https://github.com/KhronosGroup/glTF/issues/142    let uv;    if (defined(instanceValues) && defined(instanceValues[uniformName])) {      // Parameter overrides by the instance technique      uv = ModelUtility.createUniformFunction(        uniform.type,        instanceValues[uniformName],        textures,        defaultTexture      );      uniformMap[uniformName] = uv.func;      uniformValues[uniformName] = uv;    } else if (defined(uniform.node)) {      uniformMap[uniformName] = getUniformFunctionFromSource(        uniform.node,        model,        uniform.semantic,        context.uniformState      );    } else if (defined(uniform.semantic)) {      if (uniform.semantic === "JOINTMATRIX") {        jointMatrixUniformName = uniformName;      } else if (uniform.semantic === "MORPHWEIGHTS") {        morphWeightsUniformName = uniformName;      } else if (uniform.semantic === "ALPHACUTOFF") {        // The material's alphaCutoff value uses a uniform with semantic ALPHACUTOFF.        // A uniform with this semantic will ignore the instance or default values.        const alphaMode = material.alphaMode;        if (defined(alphaMode) && alphaMode === "MASK") {          const alphaCutoffValue = defaultValue(material.alphaCutoff, 0.5);          uv = ModelUtility.createUniformFunction(            uniform.type,            alphaCutoffValue,            textures,            defaultTexture          );          uniformMap[uniformName] = uv.func;          uniformValues[uniformName] = uv;        }      } else {        // Map glTF semantic to Cesium automatic uniform        uniformMap[uniformName] = ModelUtility.getGltfSemanticUniforms()[          uniform.semantic        ](context.uniformState, model);      }    } else if (defined(uniform.value)) {      // Technique value that isn't overridden by a material      const uv2 = ModelUtility.createUniformFunction(        uniform.type,        uniform.value,        textures,        defaultTexture      );      uniformMap[uniformName] = uv2.func;      uniformValues[uniformName] = uv2;    }  });  return {    map: uniformMap,    values: uniformValues,    jointMatrixUniformName: jointMatrixUniformName,    morphWeightsUniformName: morphWeightsUniformName,  };}function createUniformMaps(model, context) {  const loadResources = model._loadResources;  if (!loadResources.finishedProgramCreation()) {    return;  }  if (!loadResources.createUniformMaps) {    return;  }  loadResources.createUniformMaps = false;  const gltf = model.gltf;  const techniques = model._sourceTechniques;  const uniformMaps = model._uniformMaps;  const textures = model._rendererResources.textures;  const defaultTexture = model._defaultTexture;  ForEach.material(gltf, function (material, materialId) {    const modelMaterial = model._runtime.materialsById[materialId];    const technique = techniques[modelMaterial._technique];    const instanceValues = modelMaterial._values;    const uniforms = createUniformsForMaterial(      model,      material,      technique,      instanceValues,      context,      textures,      defaultTexture    );    const u = uniformMaps[materialId];    u.uniformMap = uniforms.map; // uniform name -> function for the renderer    u.values = uniforms.values; // material parameter name -> ModelMaterial for modifying the parameter at runtime    u.jointMatrixUniformName = uniforms.jointMatrixUniformName;    u.morphWeightsUniformName = uniforms.morphWeightsUniformName;    if (defined(technique.attributes.a_outlineCoordinates)) {      const outlineTexture = ModelOutlineLoader.createTexture(model, context);      u.uniformMap.u_outlineTexture = function () {        return outlineTexture;      };    }  });}function createUniformsForDracoQuantizedAttributes(decodedData) {  return ModelUtility.createUniformsForDracoQuantizedAttributes(    decodedData.attributes  );}function createUniformsForQuantizedAttributes(model, primitive) {  const programId = getProgramForPrimitive(model, primitive);  const quantizedUniforms = model._quantizedUniforms[programId];  return ModelUtility.createUniformsForQuantizedAttributes(    model.gltf,    primitive,    quantizedUniforms  );}function createPickColorFunction(color) {  return function () {    return color;  };}function createJointMatricesFunction(runtimeNode) {  return function () {    return runtimeNode.computedJointMatrices;  };}function createMorphWeightsFunction(runtimeNode) {  return function () {    return runtimeNode.weights;  };}function createSilhouetteColorFunction(model) {  return function () {    return model.silhouetteColor;  };}function createSilhouetteSizeFunction(model) {  return function () {    return model.silhouetteSize;  };}function createColorFunction(model) {  return function () {    return model.color;  };}function createClippingPlanesMatrixFunction(model) {  return function () {    return model._clippingPlanesMatrix;  };}function createIBLReferenceFrameMatrixFunction(model) {  return function () {    return model._iblReferenceFrameMatrix;  };}function createClippingPlanesFunction(model) {  return function () {    const clippingPlanes = model.clippingPlanes;    return !defined(clippingPlanes) || !clippingPlanes.enabled      ? model._defaultTexture      : clippingPlanes.texture;  };}function createClippingPlanesEdgeStyleFunction(model) {  return function () {    const clippingPlanes = model.clippingPlanes;    if (!defined(clippingPlanes)) {      return Color.WHITE.withAlpha(0.0);    }    const style = Color.clone(clippingPlanes.edgeColor);    style.alpha = clippingPlanes.edgeWidth;    return style;  };}function createColorBlendFunction(model) {  return function () {    return ColorBlendMode.getColorBlend(      model.colorBlendMode,      model.colorBlendAmount    );  };}function createIBLFactorFunction(model) {  return function () {    return model._imageBasedLighting.imageBasedLightingFactor;  };}function createLightColorFunction(model) {  return function () {    return model._lightColor;  };}function createLuminanceAtZenithFunction(model) {  return function () {    return model._imageBasedLighting.luminanceAtZenith;  };}function createSphericalHarmonicCoefficientsFunction(model) {  return function () {    return model._imageBasedLighting.sphericalHarmonicCoefficients;  };}function createSpecularEnvironmentMapFunction(model) {  return function () {    return model._imageBasedLighting.specularEnvironmentMapAtlas.texture;  };}function createSpecularEnvironmentMapSizeFunction(model) {  return function () {    return model._imageBasedLighting.specularEnvironmentMapAtlas.texture      .dimensions;  };}function createSpecularEnvironmentMapLOD(model) {  return function () {    return model._imageBasedLighting.specularEnvironmentMapAtlas      .maximumMipmapLevel;  };}function triangleCountFromPrimitiveIndices(primitive, indicesCount) {  switch (primitive.mode) {    case PrimitiveType.TRIANGLES:      return indicesCount / 3;    case PrimitiveType.TRIANGLE_STRIP:    case PrimitiveType.TRIANGLE_FAN:      return Math.max(indicesCount - 2, 0);    default:      return 0;  }}function createCommand(model, gltfNode, runtimeNode, context, scene3DOnly) {  const nodeCommands = model._nodeCommands;  const pickIds = model._pickIds;  const allowPicking = model.allowPicking;  const runtimeMeshesByName = model._runtime.meshesByName;  const resources = model._rendererResources;  const rendererVertexArrays = resources.vertexArrays;  const rendererPrograms = resources.programs;  const rendererRenderStates = resources.renderStates;  const uniformMaps = model._uniformMaps;  const gltf = model.gltf;  const accessors = gltf.accessors;  const gltfMeshes = gltf.meshes;  const id = gltfNode.mesh;  const mesh = gltfMeshes[id];  const primitives = mesh.primitives;  const length = primitives.length;  // The glTF node hierarchy is a DAG so a node can have more than one  // parent, so a node may already have commands.  If so, append more  // since they will have a different model matrix.  for (let i = 0; i < length; ++i) {    const primitive = primitives[i];    const ix = accessors[primitive.indices];    const material = model._runtime.materialsById[primitive.material];    const programId = material._program;    const decodedData = model._decodedData[`${id}.primitive.${i}`];    let boundingSphere;    const positionAccessor = primitive.attributes.POSITION;    if (defined(positionAccessor)) {      const minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor);      boundingSphere = BoundingSphere.fromCornerPoints(        Cartesian3.fromArray(minMax.min),        Cartesian3.fromArray(minMax.max)      );    }    const vertexArray = rendererVertexArrays[`${id}.primitive.${i}`];    let offset;    let count;    // Use indices of the previously decoded Draco geometry.    if (defined(decodedData)) {      count = decodedData.numberOfIndices;      offset = 0;    } else if (defined(ix)) {      count = ix.count;      offset = ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType); // glTF has offset in bytes.  Cesium has offsets in indices    } else {      const positions = accessors[primitive.attributes.POSITION];      count = positions.count;      offset = 0;    }    // Update model triangle count using number of indices    model._trianglesLength += triangleCountFromPrimitiveIndices(      primitive,      count    );    if (primitive.mode === PrimitiveType.POINTS) {      model._pointsLength += count;    }    const um = uniformMaps[primitive.material];    let uniformMap = um.uniformMap;    if (defined(um.jointMatrixUniformName)) {      const jointUniformMap = {};      jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction(        runtimeNode      );      uniformMap = combine(uniformMap, jointUniformMap);    }    if (defined(um.morphWeightsUniformName)) {      const morphWeightsUniformMap = {};      morphWeightsUniformMap[        um.morphWeightsUniformName      ] = createMorphWeightsFunction(runtimeNode);      uniformMap = combine(uniformMap, morphWeightsUniformMap);    }    uniformMap = combine(uniformMap, {      gltf_color: createColorFunction(model),      gltf_colorBlend: createColorBlendFunction(model),      gltf_clippingPlanes: createClippingPlanesFunction(model),      gltf_clippingPlanesEdgeStyle: createClippingPlanesEdgeStyleFunction(        model      ),      gltf_clippingPlanesMatrix: createClippingPlanesMatrixFunction(model),      gltf_iblReferenceFrameMatrix: createIBLReferenceFrameMatrixFunction(        model      ),      gltf_iblFactor: createIBLFactorFunction(model),      gltf_lightColor: createLightColorFunction(model),      gltf_sphericalHarmonicCoefficients: createSphericalHarmonicCoefficientsFunction(        model      ),      gltf_specularMap: createSpecularEnvironmentMapFunction(model),      gltf_specularMapSize: createSpecularEnvironmentMapSizeFunction(model),      gltf_maxSpecularLOD: createSpecularEnvironmentMapLOD(model),      gltf_luminanceAtZenith: createLuminanceAtZenithFunction(model),    });    Splitter.addUniforms(model, uniformMap);    // Allow callback to modify the uniformMap    if (defined(model._uniformMapLoaded)) {      uniformMap = model._uniformMapLoaded(uniformMap, programId, runtimeNode);    }    // Add uniforms for decoding quantized attributes if used    let quantizedUniformMap = {};    if (model.extensionsUsed.WEB3D_quantized_attributes) {      quantizedUniformMap = createUniformsForQuantizedAttributes(        model,        primitive      );    } else if (model._dequantizeInShader && defined(decodedData)) {      quantizedUniformMap = createUniformsForDracoQuantizedAttributes(        decodedData      );    }    uniformMap = combine(uniformMap, quantizedUniformMap);    const rs = rendererRenderStates[primitive.material];    const isTranslucent = rs.blending.enabled;    let owner = model._pickObject;    if (!defined(owner)) {      owner = {        primitive: model,        id: model.id,        node: runtimeNode.publicNode,        mesh: runtimeMeshesByName[mesh.name],      };    }    const castShadows = ShadowMode.castShadows(model._shadows);    const receiveShadows = ShadowMode.receiveShadows(model._shadows);    let pickId;    if (allowPicking && !defined(model._uniformMapLoaded)) {      pickId = context.createPickId(owner);      pickIds.push(pickId);      const pickUniforms = {        czm_pickColor: createPickColorFunction(pickId.color),      };      uniformMap = combine(uniformMap, pickUniforms);    }    if (allowPicking) {      if (defined(model._pickIdLoaded) && defined(model._uniformMapLoaded)) {        pickId = model._pickIdLoaded();      } else {        pickId = "czm_pickColor";      }    }    const command = new DrawCommand({      boundingVolume: new BoundingSphere(), // updated in update()      cull: model.cull,      modelMatrix: new Matrix4(), // computed in update()      primitiveType: primitive.mode,      vertexArray: vertexArray,      count: count,      offset: offset,      shaderProgram: rendererPrograms[programId],      castShadows: castShadows,      receiveShadows: receiveShadows,      uniformMap: uniformMap,      renderState: rs,      owner: owner,      pass: isTranslucent ? Pass.TRANSLUCENT : model.opaquePass,      pickId: pickId,    });    let command2D;    if (!scene3DOnly) {      command2D = DrawCommand.shallowClone(command);      command2D.boundingVolume = new BoundingSphere(); // updated in update()      command2D.modelMatrix = new Matrix4(); // updated in update()    }    const nodeCommand = {      show: true,      boundingSphere: boundingSphere,      command: command,      command2D: command2D,      // Generated on demand when silhouette size is greater than 0.0 and silhouette alpha is greater than 0.0      silhouetteModelCommand: undefined,      silhouetteModelCommand2D: undefined,      silhouetteColorCommand: undefined,      silhouetteColorCommand2D: undefined,      // Generated on demand when color alpha is less than 1.0      translucentCommand: undefined,      translucentCommand2D: undefined,      // Generated on demand when back face culling is false      disableCullingCommand: undefined,      disableCullingCommand2D: undefined,      // For updating node commands on shader reconstruction      programId: programId,    };    runtimeNode.commands.push(nodeCommand);    nodeCommands.push(nodeCommand);  }}function createRuntimeNodes(model, context, scene3DOnly) {  const loadResources = model._loadResources;  if (!loadResources.finishedEverythingButTextureCreation()) {    return;  }  if (!loadResources.createRuntimeNodes) {    return;  }  loadResources.createRuntimeNodes = false;  const rootNodes = [];  const runtimeNodes = model._runtime.nodes;  const gltf = model.gltf;  const nodes = gltf.nodes;  const scene = gltf.scenes[gltf.scene];  const sceneNodes = scene.nodes;  const length = sceneNodes.length;  const stack = [];  const seen = {};  for (let i = 0; i < length; ++i) {    stack.push({      parentRuntimeNode: undefined,      gltfNode: nodes[sceneNodes[i]],      id: sceneNodes[i],    });    while (stack.length > 0) {      const n = stack.pop();      seen[n.id] = true;      const parentRuntimeNode = n.parentRuntimeNode;      const gltfNode = n.gltfNode;      // Node hierarchy is a DAG so a node can have more than one parent so it may already exist      const runtimeNode = runtimeNodes[n.id];      if (runtimeNode.parents.length === 0) {        if (defined(gltfNode.matrix)) {          runtimeNode.matrix = Matrix4.fromColumnMajorArray(gltfNode.matrix);        } else {          // TRS converted to Cesium types          const rotation = gltfNode.rotation;          runtimeNode.translation = Cartesian3.fromArray(gltfNode.translation);          runtimeNode.rotation = Quaternion.unpack(rotation);          runtimeNode.scale = Cartesian3.fromArray(gltfNode.scale);        }      }      if (defined(parentRuntimeNode)) {        parentRuntimeNode.children.push(runtimeNode);        runtimeNode.parents.push(parentRuntimeNode);      } else {        rootNodes.push(runtimeNode);      }      if (defined(gltfNode.mesh)) {        createCommand(model, gltfNode, runtimeNode, context, scene3DOnly);      }      const children = gltfNode.children;      if (defined(children)) {        const childrenLength = children.length;        for (let j = 0; j < childrenLength; j++) {          const childId = children[j];          if (!seen[childId]) {            stack.push({              parentRuntimeNode: runtimeNode,              gltfNode: nodes[childId],              id: children[j],            });          }        }      }    }  }  model._runtime.rootNodes = rootNodes;  model._runtime.nodes = runtimeNodes;}function getGeometryByteLength(buffers) {  let memory = 0;  for (const id in buffers) {    if (buffers.hasOwnProperty(id)) {      memory += buffers[id].sizeInBytes;    }  }  return memory;}function getTexturesByteLength(textures) {  let memory = 0;  for (const id in textures) {    if (textures.hasOwnProperty(id)) {      memory += textures[id].sizeInBytes;    }  }  return memory;}function createResources(model, frameState) {  const context = frameState.context;  const scene3DOnly = frameState.scene3DOnly;  const quantizedVertexShaders = model._quantizedVertexShaders;  const techniques = model._sourceTechniques;  const programs = model._sourcePrograms;  const resources = model._rendererResources;  let shaders = resources.sourceShaders;  if (model._loadRendererResourcesFromCache) {    shaders = resources.sourceShaders =      model._cachedRendererResources.sourceShaders;  }  for (const techniqueId in techniques) {    if (techniques.hasOwnProperty(techniqueId)) {      const programId = techniques[techniqueId].program;      const program = programs[programId];      let shader = shaders[program.vertexShader];      ModelUtility.checkSupportedGlExtensions(program.glExtensions, context);      if (        model.extensionsUsed.WEB3D_quantized_attributes ||        model._dequantizeInShader      ) {        let quantizedVS = quantizedVertexShaders[programId];        if (!defined(quantizedVS)) {          quantizedVS = modifyShaderForQuantizedAttributes(            shader,            programId,            model          );          quantizedVertexShaders[programId] = quantizedVS;        }        shader = quantizedVS;      }      shader = modifyShader(shader, programId, model._vertexShaderLoaded);    }  }  if (model._loadRendererResourcesFromCache) {    const cachedResources = model._cachedRendererResources;    resources.buffers = cachedResources.buffers;    resources.vertexArrays = cachedResources.vertexArrays;    resources.programs = cachedResources.programs;    resources.silhouettePrograms = cachedResources.silhouettePrograms;    resources.textures = cachedResources.textures;    resources.samplers = cachedResources.samplers;    resources.renderStates = cachedResources.renderStates;    // Vertex arrays are unique to this model, create instead of using the cache.    if (defined(model._precreatedAttributes)) {      createVertexArrays(model, context);    }    model._cachedGeometryByteLength += getGeometryByteLength(      cachedResources.buffers    );    model._cachedTexturesByteLength += getTexturesByteLength(      cachedResources.textures    );  } else {    createBuffers(model, frameState); // using glTF bufferViews    createPrograms(model, frameState);    createSamplers(model, context);    loadTexturesFromBufferViews(model);    createTextures(model, frameState);  }  createSkins(model);  createRuntimeAnimations(model);  if (!model._loadRendererResourcesFromCache) {    createVertexArrays(model, context); // using glTF meshes    createRenderStates(model); // using glTF materials/techniques/states    // Long-term, we might not cache render states if they could change    // due to an animation, e.g., a uniform going from opaque to transparent.    // Could use copy-on-write if it is worth it.  Probably overkill.  }  createUniformMaps(model, context); // using glTF materials/techniques  createRuntimeNodes(model, context, scene3DOnly); // using glTF scene}///////////////////////////////////////////////////////////////////////////function getNodeMatrix(node, result) {  const publicNode = node.publicNode;  const publicMatrix = publicNode.matrix;  if (publicNode.useMatrix && defined(publicMatrix)) {    // Public matrix overrides original glTF matrix and glTF animations    Matrix4.clone(publicMatrix, result);  } else if (defined(node.matrix)) {    Matrix4.clone(node.matrix, result);  } else {    Matrix4.fromTranslationQuaternionRotationScale(      node.translation,      node.rotation,      node.scale,      result    );    // Keep matrix returned by the node in-sync if the node is targeted by an animation.  Only TRS nodes can be targeted.    publicNode.setMatrix(result);  }}const scratchNodeStack = [];const scratchComputedTranslation = new Cartesian4();const scratchComputedMatrixIn2D = new Matrix4();function updateNodeHierarchyModelMatrix(  model,  modelTransformChanged,  justLoaded,  projection) {  const maxDirtyNumber = model._maxDirtyNumber;  const rootNodes = model._runtime.rootNodes;  const length = rootNodes.length;  const nodeStack = scratchNodeStack;  let computedModelMatrix = model._computedModelMatrix;  if (model._mode !== SceneMode.SCENE3D && !model._ignoreCommands) {    const translation = Matrix4.getColumn(      computedModelMatrix,      3,      scratchComputedTranslation    );    if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {      computedModelMatrix = Transforms.basisTo2D(        projection,        computedModelMatrix,        scratchComputedMatrixIn2D      );      model._rtcCenter = model._rtcCenter3D;    } else {      const center = model.boundingSphere.center;      const to2D = Transforms.wgs84To2DModelMatrix(        projection,        center,        scratchComputedMatrixIn2D      );      computedModelMatrix = Matrix4.multiply(        to2D,        computedModelMatrix,        scratchComputedMatrixIn2D      );      if (defined(model._rtcCenter)) {        Matrix4.setTranslation(          computedModelMatrix,          Cartesian4.UNIT_W,          computedModelMatrix        );        model._rtcCenter = model._rtcCenter2D;      }    }  }  for (let i = 0; i < length; ++i) {    let n = rootNodes[i];    getNodeMatrix(n, n.transformToRoot);    nodeStack.push(n);    while (nodeStack.length > 0) {      n = nodeStack.pop();      const transformToRoot = n.transformToRoot;      const commands = n.commands;      if (        n.dirtyNumber === maxDirtyNumber ||        modelTransformChanged ||        justLoaded      ) {        const nodeMatrix = Matrix4.multiplyTransformation(          computedModelMatrix,          transformToRoot,          n.computedMatrix        );        const commandsLength = commands.length;        if (commandsLength > 0) {          // Node has meshes, which has primitives.  Update their commands.          for (let j = 0; j < commandsLength; ++j) {            const primitiveCommand = commands[j];            let command = primitiveCommand.command;            Matrix4.clone(nodeMatrix, command.modelMatrix);            // PERFORMANCE_IDEA: Can use transformWithoutScale if no node up to the root has scale (including animation)            BoundingSphere.transform(              primitiveCommand.boundingSphere,              command.modelMatrix,              command.boundingVolume            );            if (defined(model._rtcCenter)) {              Cartesian3.add(                model._rtcCenter,                command.boundingVolume.center,                command.boundingVolume.center              );            }            // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it            // will be clipped by the viewport. We create a second command that translates the model            // model matrix to the opposite side of the map so the part that was clipped in one viewport            // is drawn in the other.            command = primitiveCommand.command2D;            if (defined(command) && model._mode === SceneMode.SCENE2D) {              Matrix4.clone(nodeMatrix, command.modelMatrix);              command.modelMatrix[13] -=                CesiumMath.sign(command.modelMatrix[13]) *                2.0 *                CesiumMath.PI *                projection.ellipsoid.maximumRadius;              BoundingSphere.transform(                primitiveCommand.boundingSphere,                command.modelMatrix,                command.boundingVolume              );            }          }        }      }      const children = n.children;      if (defined(children)) {        const childrenLength = children.length;        for (let k = 0; k < childrenLength; ++k) {          const child = children[k];          // A node's transform needs to be updated if          // - It was targeted for animation this frame, or          // - Any of its ancestors were targeted for animation this frame          // PERFORMANCE_IDEA: if a child has multiple parents and only one of the parents          // is dirty, all the subtrees for each child instance will be dirty; we probably          // won't see this in the wild often.          child.dirtyNumber = Math.max(child.dirtyNumber, n.dirtyNumber);          if (child.dirtyNumber === maxDirtyNumber || justLoaded) {            // Don't check for modelTransformChanged since if only the model's model matrix changed,            // we do not need to rebuild the local transform-to-root, only the final            // [model's-model-matrix][transform-to-root] above.            getNodeMatrix(child, child.transformToRoot);            Matrix4.multiplyTransformation(              transformToRoot,              child.transformToRoot,              child.transformToRoot            );          }          nodeStack.push(child);        }      }    }  }  ++model._maxDirtyNumber;}let scratchObjectSpace = new Matrix4();function applySkins(model) {  const skinnedNodes = model._runtime.skinnedNodes;  const length = skinnedNodes.length;  for (let i = 0; i < length; ++i) {    const node = skinnedNodes[i];    scratchObjectSpace = Matrix4.inverseTransformation(      node.transformToRoot,      scratchObjectSpace    );    const computedJointMatrices = node.computedJointMatrices;    const joints = node.joints;    const bindShapeMatrix = node.bindShapeMatrix;    const inverseBindMatrices = node.inverseBindMatrices;    const inverseBindMatricesLength = inverseBindMatrices.length;    for (let m = 0; m < inverseBindMatricesLength; ++m) {      // [joint-matrix] = [node-to-root^-1][joint-to-root][inverse-bind][bind-shape]      if (!defined(computedJointMatrices[m])) {        computedJointMatrices[m] = new Matrix4();      }      computedJointMatrices[m] = Matrix4.multiplyTransformation(        scratchObjectSpace,        joints[m].transformToRoot,        computedJointMatrices[m]      );      computedJointMatrices[m] = Matrix4.multiplyTransformation(        computedJointMatrices[m],        inverseBindMatrices[m],        computedJointMatrices[m]      );      if (defined(bindShapeMatrix)) {        // NOTE: bindShapeMatrix is glTF 1.0 only, removed in glTF 2.0.        computedJointMatrices[m] = Matrix4.multiplyTransformation(          computedJointMatrices[m],          bindShapeMatrix,          computedJointMatrices[m]        );      }    }  }}function updatePerNodeShow(model) {  // Totally not worth it, but we could optimize this:  // http://help.agi.com/AGIComponents/html/BlogDeletionInBoundingVolumeHierarchies.htm  const rootNodes = model._runtime.rootNodes;  const length = rootNodes.length;  const nodeStack = scratchNodeStack;  for (let i = 0; i < length; ++i) {    let n = rootNodes[i];    n.computedShow = n.publicNode.show;    nodeStack.push(n);    while (nodeStack.length > 0) {      n = nodeStack.pop();      const show = n.computedShow;      const nodeCommands = n.commands;      const nodeCommandsLength = nodeCommands.length;      for (let j = 0; j < nodeCommandsLength; ++j) {        nodeCommands[j].show = show;      }      // if commandsLength is zero, the node has a light or camera      const children = n.children;      if (defined(children)) {        const childrenLength = children.length;        for (let k = 0; k < childrenLength; ++k) {          const child = children[k];          // Parent needs to be shown for child to be shown.          child.computedShow = show && child.publicNode.show;          nodeStack.push(child);        }      }    }  }}function updatePickIds(model, context) {  const id = model.id;  if (model._id !== id) {    model._id = id;    const pickIds = model._pickIds;    const length = pickIds.length;    for (let i = 0; i < length; ++i) {      pickIds[i].object.id = id;    }  }}function updateWireframe(model) {  if (model._debugWireframe !== model.debugWireframe) {    model._debugWireframe = model.debugWireframe;    // This assumes the original primitive was TRIANGLES and that the triangles    // are connected for the wireframe to look perfect.    const primitiveType = model.debugWireframe      ? PrimitiveType.LINES      : PrimitiveType.TRIANGLES;    const nodeCommands = model._nodeCommands;    const length = nodeCommands.length;    for (let i = 0; i < length; ++i) {      nodeCommands[i].command.primitiveType = primitiveType;    }  }}function updateShowBoundingVolume(model) {  if (model.debugShowBoundingVolume !== model._debugShowBoundingVolume) {    model._debugShowBoundingVolume = model.debugShowBoundingVolume;    const debugShowBoundingVolume = model.debugShowBoundingVolume;    const nodeCommands = model._nodeCommands;    const length = nodeCommands.length;    for (let i = 0; i < length; ++i) {      nodeCommands[i].command.debugShowBoundingVolume = debugShowBoundingVolume;    }  }}function updateShadows(model) {  if (model.shadows !== model._shadows) {    model._shadows = model.shadows;    const castShadows = ShadowMode.castShadows(model.shadows);    const receiveShadows = ShadowMode.receiveShadows(model.shadows);    const nodeCommands = model._nodeCommands;    const length = nodeCommands.length;    for (let i = 0; i < length; i++) {      const nodeCommand = nodeCommands[i];      nodeCommand.command.castShadows = castShadows;      nodeCommand.command.receiveShadows = receiveShadows;    }  }}function getTranslucentRenderState(model, renderState) {  const rs = clone(renderState, true);  rs.cull.enabled = false;  rs.depthTest.enabled = true;  rs.depthMask = false;  rs.blending = BlendingState.ALPHA_BLEND;  if (model.opaquePass === Pass.CESIUM_3D_TILE) {    rs.stencilTest = StencilConstants.setCesium3DTileBit();    rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;  }  return RenderState.fromCache(rs);}function deriveTranslucentCommand(model, command) {  const translucentCommand = DrawCommand.shallowClone(command);  translucentCommand.pass = Pass.TRANSLUCENT;  translucentCommand.renderState = getTranslucentRenderState(    model,    command.renderState  );  return translucentCommand;}function updateColor(model, frameState, forceDerive) {  // Generate translucent commands when the blend color has an alpha in the range (0.0, 1.0) exclusive  const scene3DOnly = frameState.scene3DOnly;  const alpha = model.color.alpha;  if (alpha > 0.0 && alpha < 1.0) {    const nodeCommands = model._nodeCommands;    const length = nodeCommands.length;    if (      length > 0 &&      (!defined(nodeCommands[0].translucentCommand) || forceDerive)    ) {      for (let i = 0; i < length; ++i) {        const nodeCommand = nodeCommands[i];        const command = nodeCommand.command;        nodeCommand.translucentCommand = deriveTranslucentCommand(          model,          command        );        if (!scene3DOnly) {          const command2D = nodeCommand.command2D;          nodeCommand.translucentCommand2D = deriveTranslucentCommand(            model,            command2D          );        }      }    }  }}function getDisableCullingRenderState(renderState) {  const rs = clone(renderState, true);  rs.cull.enabled = false;  return RenderState.fromCache(rs);}function deriveDisableCullingCommand(command) {  const disableCullingCommand = DrawCommand.shallowClone(command);  disableCullingCommand.renderState = getDisableCullingRenderState(    command.renderState  );  return disableCullingCommand;}function updateBackFaceCulling(model, frameState, forceDerive) {  const scene3DOnly = frameState.scene3DOnly;  const backFaceCulling = model.backFaceCulling;  if (!backFaceCulling) {    const nodeCommands = model._nodeCommands;    const length = nodeCommands.length;    if (      length > 0 &&      (!defined(nodeCommands[0].disableCullingCommand) || forceDerive)    ) {      for (let i = 0; i < length; ++i) {        const nodeCommand = nodeCommands[i];        const command = nodeCommand.command;        nodeCommand.disableCullingCommand = deriveDisableCullingCommand(          command        );        if (!scene3DOnly) {          const command2D = nodeCommand.command2D;          nodeCommand.disableCullingCommand2D = deriveDisableCullingCommand(            command2D          );        }      }    }  }}function getProgramId(model, program) {  const programs = model._rendererResources.programs;  for (const id in programs) {    if (programs.hasOwnProperty(id)) {      if (programs[id] === program) {        return id;      }    }  }}function createSilhouetteProgram(model, program, frameState) {  let vs = program.vertexShaderSource.sources[0];  const attributeLocations = program._attributeLocations;  const normalAttributeName = model._normalAttributeName;  // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/  vs = ShaderSource.replaceMain(vs, "gltf_silhouette_main");  vs +=    `${      "uniform float gltf_silhouetteSize; \n" +      "void main() \n" +      "{ \n" +      "    gltf_silhouette_main(); \n" +      "    vec3 n = normalize(czm_normal3D * "    }${normalAttributeName}); \n` +    `    n.x *= czm_projection[0][0]; \n` +    `    n.y *= czm_projection[1][1]; \n` +    `    vec4 clip = gl_Position; \n` +    `    clip.xy += n.xy * clip.w * gltf_silhouetteSize * czm_pixelRatio / czm_viewport.z; \n` +    `    gl_Position = clip; \n` +    `}`;  const fs =    "uniform vec4 gltf_silhouetteColor; \n" +    "void main() \n" +    "{ \n" +    "    gl_FragColor = czm_gammaCorrect(gltf_silhouetteColor); \n" +    "}";  return ShaderProgram.fromCache({    context: frameState.context,    vertexShaderSource: vs,    fragmentShaderSource: fs,    attributeLocations: attributeLocations,  });}function hasSilhouette(model, frameState) {  return (    silhouetteSupported(frameState.context) &&    model.silhouetteSize > 0.0 &&    model.silhouetteColor.alpha > 0.0 &&    defined(model._normalAttributeName)  );}function hasTranslucentCommands(model) {  const nodeCommands = model._nodeCommands;  const length = nodeCommands.length;  for (let i = 0; i < length; ++i) {    const nodeCommand = nodeCommands[i];    const command = nodeCommand.command;    if (command.pass === Pass.TRANSLUCENT) {      return true;    }  }  return false;}function isTranslucent(model) {  return model.color.alpha > 0.0 && model.color.alpha < 1.0;}function isInvisible(model) {  return model.color.alpha === 0.0;}function alphaDirty(currAlpha, prevAlpha) {  // Returns whether the alpha state has changed between invisible, translucent, or opaque  return (    Math.floor(currAlpha) !== Math.floor(prevAlpha) ||    Math.ceil(currAlpha) !== Math.ceil(prevAlpha)  );}let silhouettesLength = 0;function createSilhouetteCommands(model, frameState) {  // Wrap around after exceeding the 8-bit stencil limit.  // The reference is unique to each model until this point.  const stencilReference = ++silhouettesLength % 255;  // If the model is translucent the silhouette needs to be in the translucent pass.  // Otherwise the silhouette would be rendered before the model.  const silhouetteTranslucent =    hasTranslucentCommands(model) ||    isTranslucent(model) ||    model.silhouetteColor.alpha < 1.0;  const silhouettePrograms = model._rendererResources.silhouettePrograms;  const scene3DOnly = frameState.scene3DOnly;  const nodeCommands = model._nodeCommands;  const length = nodeCommands.length;  for (let i = 0; i < length; ++i) {    const nodeCommand = nodeCommands[i];    const command = nodeCommand.command;    // Create model command    const modelCommand = isTranslucent(model)      ? nodeCommand.translucentCommand      : command;    const silhouetteModelCommand = DrawCommand.shallowClone(modelCommand);    let renderState = clone(modelCommand.renderState);    // Write the reference value into the stencil buffer.    renderState.stencilTest = {      enabled: true,      frontFunction: WebGLConstants.ALWAYS,      backFunction: WebGLConstants.ALWAYS,      reference: stencilReference,      mask: ~0,      frontOperation: {        fail: WebGLConstants.KEEP,        zFail: WebGLConstants.KEEP,        zPass: WebGLConstants.REPLACE,      },      backOperation: {        fail: WebGLConstants.KEEP,        zFail: WebGLConstants.KEEP,        zPass: WebGLConstants.REPLACE,      },    };    if (isInvisible(model)) {      // When the model is invisible disable color and depth writes but still write into the stencil buffer      renderState.colorMask = {        red: false,        green: false,        blue: false,        alpha: false,      };      renderState.depthMask = false;    }    renderState = RenderState.fromCache(renderState);    silhouetteModelCommand.renderState = renderState;    nodeCommand.silhouetteModelCommand = silhouetteModelCommand;    // Create color command    const silhouetteColorCommand = DrawCommand.shallowClone(command);    renderState = clone(command.renderState, true);    renderState.depthTest.enabled = true;    renderState.cull.enabled = false;    if (silhouetteTranslucent) {      silhouetteColorCommand.pass = Pass.TRANSLUCENT;      renderState.depthMask = false;      renderState.blending = BlendingState.ALPHA_BLEND;    }    // Only render silhouette if the value in the stencil buffer equals the reference    renderState.stencilTest = {      enabled: true,      frontFunction: WebGLConstants.NOTEQUAL,      backFunction: WebGLConstants.NOTEQUAL,      reference: stencilReference,      mask: ~0,      frontOperation: {        fail: WebGLConstants.KEEP,        zFail: WebGLConstants.KEEP,        zPass: WebGLConstants.KEEP,      },      backOperation: {        fail: WebGLConstants.KEEP,        zFail: WebGLConstants.KEEP,        zPass: WebGLConstants.KEEP,      },    };    renderState = RenderState.fromCache(renderState);    // If the silhouette program has already been cached use it    const program = command.shaderProgram;    const id = getProgramId(model, program);    let silhouetteProgram = silhouettePrograms[id];    if (!defined(silhouetteProgram)) {      silhouetteProgram = createSilhouetteProgram(model, program, frameState);      silhouettePrograms[id] = silhouetteProgram;    }    const silhouetteUniformMap = combine(command.uniformMap, {      gltf_silhouetteColor: createSilhouetteColorFunction(model),      gltf_silhouetteSize: createSilhouetteSizeFunction(model),    });    silhouetteColorCommand.renderState = renderState;    silhouetteColorCommand.shaderProgram = silhouetteProgram;    silhouetteColorCommand.uniformMap = silhouetteUniformMap;    silhouetteColorCommand.castShadows = false;    silhouetteColorCommand.receiveShadows = false;    nodeCommand.silhouetteColorCommand = silhouetteColorCommand;    if (!scene3DOnly) {      const command2D = nodeCommand.command2D;      const silhouetteModelCommand2D = DrawCommand.shallowClone(        silhouetteModelCommand      );      silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume;      silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix;      nodeCommand.silhouetteModelCommand2D = silhouetteModelCommand2D;      const silhouetteColorCommand2D = DrawCommand.shallowClone(        silhouetteColorCommand      );      silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume;      silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix;      nodeCommand.silhouetteColorCommand2D = silhouetteColorCommand2D;    }  }}function modifyShaderForClippingPlanes(  shader,  clippingPlaneCollection,  context) {  shader = ShaderSource.replaceMain(shader, "gltf_clip_main");  shader += `${Model._getClippingFunction(clippingPlaneCollection, context)}\n`;  shader += `${    "uniform highp sampler2D gltf_clippingPlanes; \n" +    "uniform mat4 gltf_clippingPlanesMatrix; \n" +    "uniform vec4 gltf_clippingPlanesEdgeStyle; \n" +    "void main() \n" +    "{ \n" +    "    gltf_clip_main(); \n"  }${getClipAndStyleCode(    "gltf_clippingPlanes",    "gltf_clippingPlanesMatrix",    "gltf_clippingPlanesEdgeStyle"  )}} \n`;  return shader;}function updateSilhouette(model, frameState, force) {  // Generate silhouette commands when the silhouette size is greater than 0.0 and the alpha is greater than 0.0  // There are two silhouette commands:  //     1. silhouetteModelCommand : render model normally while enabling stencil mask  //     2. silhouetteColorCommand : render enlarged model with a solid color while enabling stencil tests  if (!hasSilhouette(model, frameState)) {    return;  }  const nodeCommands = model._nodeCommands;  const dirty =    nodeCommands.length > 0 &&    (alphaDirty(model.color.alpha, model._colorPreviousAlpha) ||      alphaDirty(        model.silhouetteColor.alpha,        model._silhouetteColorPreviousAlpha      ) ||      !defined(nodeCommands[0].silhouetteModelCommand));  model._colorPreviousAlpha = model.color.alpha;  model._silhouetteColorPreviousAlpha = model.silhouetteColor.alpha;  if (dirty || force) {    createSilhouetteCommands(model, frameState);  }}function updateClippingPlanes(model, frameState) {  const clippingPlanes = model._clippingPlanes;  if (defined(clippingPlanes) && clippingPlanes.owner === model) {    if (clippingPlanes.enabled) {      clippingPlanes.update(frameState);    }  }}const scratchBoundingSphere = new BoundingSphere();function scaleInPixels(positionWC, radius, frameState) {  scratchBoundingSphere.center = positionWC;  scratchBoundingSphere.radius = radius;  return frameState.camera.getPixelSize(    scratchBoundingSphere,    frameState.context.drawingBufferWidth,    frameState.context.drawingBufferHeight  );}const scratchPosition = new Cartesian3();const scratchCartographic = new Cartographic();function getScale(model, frameState) {  let scale = model.scale;  if (model.minimumPixelSize !== 0.0) {    // Compute size of bounding sphere in pixels    const context = frameState.context;    const maxPixelSize = Math.max(      context.drawingBufferWidth,      context.drawingBufferHeight    );    const m = defined(model._clampedModelMatrix)      ? model._clampedModelMatrix      : model.modelMatrix;    scratchPosition.x = m[12];    scratchPosition.y = m[13];    scratchPosition.z = m[14];    if (defined(model._rtcCenter)) {      Cartesian3.add(model._rtcCenter, scratchPosition, scratchPosition);    }    if (model._mode !== SceneMode.SCENE3D) {      const projection = frameState.mapProjection;      const cartographic = projection.ellipsoid.cartesianToCartographic(        scratchPosition,        scratchCartographic      );      projection.project(cartographic, scratchPosition);      Cartesian3.fromElements(        scratchPosition.z,        scratchPosition.x,        scratchPosition.y,        scratchPosition      );    }    const radius = model.boundingSphere.radius;    const metersPerPixel = scaleInPixels(scratchPosition, radius, frameState);    // metersPerPixel is always > 0.0    const pixelsPerMeter = 1.0 / metersPerPixel;    const diameterInPixels = Math.min(      pixelsPerMeter * (2.0 * radius),      maxPixelSize    );    // Maintain model's minimum pixel size    if (diameterInPixels < model.minimumPixelSize) {      scale =        (model.minimumPixelSize * metersPerPixel) /        (2.0 * model._initialRadius);    }  }  return defined(model.maximumScale)    ? Math.min(model.maximumScale, scale)    : scale;}function releaseCachedGltf(model) {  if (    defined(model._cacheKey) &&    defined(model._cachedGltf) &&    --model._cachedGltf.count === 0  ) {    delete gltfCache[model._cacheKey];  }  model._cachedGltf = undefined;}///////////////////////////////////////////////////////////////////////////function CachedRendererResources(context, cacheKey) {  this.buffers = undefined;  this.vertexArrays = undefined;  this.programs = undefined;  this.sourceShaders = undefined;  this.silhouettePrograms = undefined;  this.textures = undefined;  this.samplers = undefined;  this.renderStates = undefined;  this.ready = false;  this.context = context;  this.cacheKey = cacheKey;  this.count = 0;}function destroy(property) {  for (const name in property) {    if (property.hasOwnProperty(name)) {      property[name].destroy();    }  }}function destroyCachedRendererResources(resources) {  destroy(resources.buffers);  destroy(resources.vertexArrays);  destroy(resources.programs);  destroy(resources.silhouettePrograms);  destroy(resources.textures);}CachedRendererResources.prototype.release = function () {  if (--this.count === 0) {    if (defined(this.cacheKey)) {      // Remove if this was cached      delete this.context.cache.modelRendererResourceCache[this.cacheKey];    }    destroyCachedRendererResources(this);    return destroyObject(this);  }  return undefined;};///////////////////////////////////////////////////////////////////////////function getUpdateHeightCallback(model, ellipsoid, cartoPosition) {  return function (clampedPosition) {    if (model.heightReference === HeightReference.RELATIVE_TO_GROUND) {      const clampedCart = ellipsoid.cartesianToCartographic(        clampedPosition,        scratchCartographic      );      clampedCart.height += cartoPosition.height;      ellipsoid.cartographicToCartesian(clampedCart, clampedPosition);    }    const clampedModelMatrix = model._clampedModelMatrix;    // Modify clamped model matrix to use new height    Matrix4.clone(model.modelMatrix, clampedModelMatrix);    clampedModelMatrix[12] = clampedPosition.x;    clampedModelMatrix[13] = clampedPosition.y;    clampedModelMatrix[14] = clampedPosition.z;    model._heightChanged = true;  };}function updateClamping(model) {  if (defined(model._removeUpdateHeightCallback)) {    model._removeUpdateHeightCallback();    model._removeUpdateHeightCallback = undefined;  }  const scene = model._scene;  if (    !defined(scene) ||    !defined(scene.globe) ||    model.heightReference === HeightReference.NONE  ) {    //>>includeStart('debug', pragmas.debug);    if (model.heightReference !== HeightReference.NONE) {      throw new DeveloperError(        "Height reference is not supported without a scene and globe."      );    }    //>>includeEnd('debug');    model._clampedModelMatrix = undefined;    return;  }  const globe = scene.globe;  const ellipsoid = globe.ellipsoid;  // Compute cartographic position so we don't recompute every update  const modelMatrix = model.modelMatrix;  scratchPosition.x = modelMatrix[12];  scratchPosition.y = modelMatrix[13];  scratchPosition.z = modelMatrix[14];  const cartoPosition = ellipsoid.cartesianToCartographic(scratchPosition);  if (!defined(model._clampedModelMatrix)) {    model._clampedModelMatrix = Matrix4.clone(modelMatrix, new Matrix4());  }  // Install callback to handle updating of terrain tiles  const surface = globe._surface;  model._removeUpdateHeightCallback = surface.updateHeight(    cartoPosition,    getUpdateHeightCallback(model, ellipsoid, cartoPosition)  );  // Set the correct height now  const height = globe.getHeight(cartoPosition);  if (defined(height)) {    // Get callback with cartoPosition being the non-clamped position    const cb = getUpdateHeightCallback(model, ellipsoid, cartoPosition);    // Compute the clamped cartesian and call updateHeight callback    Cartographic.clone(cartoPosition, scratchCartographic);    scratchCartographic.height = height;    ellipsoid.cartographicToCartesian(scratchCartographic, scratchPosition);    cb(scratchPosition);  }}const scratchDisplayConditionCartesian = new Cartesian3();const scratchDistanceDisplayConditionCartographic = new Cartographic();function distanceDisplayConditionVisible(model, frameState) {  let distance2;  const ddc = model.distanceDisplayCondition;  const nearSquared = ddc.near * ddc.near;  const farSquared = ddc.far * ddc.far;  if (frameState.mode === SceneMode.SCENE2D) {    const frustum2DWidth =      frameState.camera.frustum.right - frameState.camera.frustum.left;    distance2 = frustum2DWidth * 0.5;    distance2 = distance2 * distance2;  } else {    // Distance to center of primitive's reference frame    let position = Matrix4.getTranslation(      model.modelMatrix,      scratchDisplayConditionCartesian    );    if (frameState.mode === SceneMode.COLUMBUS_VIEW) {      const projection = frameState.mapProjection;      const ellipsoid = projection.ellipsoid;      const cartographic = ellipsoid.cartesianToCartographic(        position,        scratchDistanceDisplayConditionCartographic      );      position = projection.project(cartographic, position);      Cartesian3.fromElements(position.z, position.x, position.y, position);    }    distance2 = Cartesian3.distanceSquared(      position,      frameState.camera.positionWC    );  }  return distance2 >= nearSquared && distance2 <= farSquared;}const scratchIBLReferenceFrameMatrix4 = new Matrix4();const scratchIBLReferenceFrameMatrix3 = new Matrix3();const scratchClippingPlanesMatrix = new Matrix4();/** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. * <p> * Do not call this function directly.  This is documented just to * list the exceptions that may be propagated when the scene is rendered: * </p> * * @exception {RuntimeError} Failed to load external reference. */Model.prototype.update = function (frameState) {  if (frameState.mode === SceneMode.MORPHING) {    return;  }  if (!FeatureDetection.supportsWebP.initialized) {    FeatureDetection.supportsWebP.initialize();    return;  }  const context = frameState.context;  this._defaultTexture = context.defaultTexture;  const supportsWebP = FeatureDetection.supportsWebP();  if (this._state === ModelState.NEEDS_LOAD && defined(this.gltf)) {    // Use renderer resources from cache instead of loading/creating them?    let cachedRendererResources;    const cacheKey = this.cacheKey;    if (defined(cacheKey)) {      // cache key given? this model will pull from or contribute to context level cache      context.cache.modelRendererResourceCache = defaultValue(        context.cache.modelRendererResourceCache,        {}      );      const modelCaches = context.cache.modelRendererResourceCache;      cachedRendererResources = modelCaches[this.cacheKey];      if (defined(cachedRendererResources)) {        if (!cachedRendererResources.ready) {          // Cached resources for the model are not loaded yet.  We'll          // try again every frame until they are.          return;        }        ++cachedRendererResources.count;        this._loadRendererResourcesFromCache = true;      } else {        cachedRendererResources = new CachedRendererResources(          context,          cacheKey        );        cachedRendererResources.count = 1;        modelCaches[this.cacheKey] = cachedRendererResources;      }      this._cachedRendererResources = cachedRendererResources;    } else {      // cache key not given? this model doesn't care about context level cache at all. Cache is here to simplify freeing on destroy.      cachedRendererResources = new CachedRendererResources(context);      cachedRendererResources.count = 1;      this._cachedRendererResources = cachedRendererResources;    }    this._state = ModelState.LOADING;    if (this._state !== ModelState.FAILED) {      const extensions = this.gltf.extensions;      if (defined(extensions) && defined(extensions.CESIUM_RTC)) {        const center = Cartesian3.fromArray(extensions.CESIUM_RTC.center);        if (!Cartesian3.equals(center, Cartesian3.ZERO)) {          this._rtcCenter3D = center;          const projection = frameState.mapProjection;          const ellipsoid = projection.ellipsoid;          const cartographic = ellipsoid.cartesianToCartographic(            this._rtcCenter3D          );          const projectedCart = projection.project(cartographic);          Cartesian3.fromElements(            projectedCart.z,            projectedCart.x,            projectedCart.y,            projectedCart          );          this._rtcCenter2D = projectedCart;          this._rtcCenterEye = new Cartesian3();          this._rtcCenter = this._rtcCenter3D;        }      }      addPipelineExtras(this.gltf);      this._loadResources = new ModelLoadResources();      if (!this._loadRendererResourcesFromCache) {        // Buffers are required to updateVersion        ModelUtility.parseBuffers(this, bufferLoad);      }    }  }  const loadResources = this._loadResources;  const incrementallyLoadTextures = this._incrementallyLoadTextures;  let justLoaded = false;  if (this._state === ModelState.LOADING) {    // Transition from LOADING -> LOADED once resources are downloaded and created.    // Textures may continue to stream in while in the LOADED state.    if (loadResources.pendingBufferLoads === 0) {      if (!loadResources.initialized) {        frameState.brdfLutGenerator.update(frameState);        ModelUtility.checkSupportedExtensions(          this.extensionsRequired,          supportsWebP        );        ModelUtility.updateForwardAxis(this);        // glTF pipeline updates, not needed if loading from cache        if (!defined(this.gltf.extras.sourceVersion)) {          const gltf = this.gltf;          // Add the original version so it remains cached          gltf.extras.sourceVersion = ModelUtility.getAssetVersion(gltf);          gltf.extras.sourceKHRTechniquesWebGL = defined(            ModelUtility.getUsedExtensions(gltf).KHR_techniques_webgl          );          this._sourceVersion = gltf.extras.sourceVersion;          this._sourceKHRTechniquesWebGL = gltf.extras.sourceKHRTechniquesWebGL;          updateVersion(gltf);          addDefaults(gltf);          const options = {            addBatchIdToGeneratedShaders: this._addBatchIdToGeneratedShaders,          };          processModelMaterialsCommon(gltf, options);          processPbrMaterials(gltf, options);        }        this._sourceVersion = this.gltf.extras.sourceVersion;        this._sourceKHRTechniquesWebGL = this.gltf.extras.sourceKHRTechniquesWebGL;        // Skip dequantizing in the shader if not encoded        this._dequantizeInShader =          this._dequantizeInShader && DracoLoader.hasExtension(this);        // We do this after to make sure that the ids don't change        addBuffersToLoadResources(this);        parseArticulations(this);        parseTechniques(this);        if (!this._loadRendererResourcesFromCache) {          parseBufferViews(this);          parseShaders(this);          parsePrograms(this);          parseTextures(this, context, supportsWebP);        }        parseMaterials(this);        parseMeshes(this);        parseNodes(this);        parseCredits(this);        // Start draco decoding        DracoLoader.parse(this, context);        loadResources.initialized = true;      }      if (!loadResources.finishedDecoding()) {        DracoLoader.decodeModel(this, context).catch(          ModelUtility.getFailedLoadFunction(this, "model", this.basePath)        );      }      if (loadResources.finishedDecoding() && !loadResources.resourcesParsed) {        this._boundingSphere = ModelUtility.computeBoundingSphere(this);        this._initialRadius = this._boundingSphere.radius;        DracoLoader.cacheDataForModel(this);        loadResources.resourcesParsed = true;      }      if (        loadResources.resourcesParsed &&        loadResources.pendingShaderLoads === 0      ) {        if (this.showOutline) {          ModelOutlineLoader.outlinePrimitives(this);        }        createResources(this, frameState);      }    }    if (      loadResources.finished() ||      (incrementallyLoadTextures &&        loadResources.finishedEverythingButTextureCreation())    ) {      this._state = ModelState.LOADED;      justLoaded = true;    }  }  // Incrementally stream textures.  if (defined(loadResources) && this._state === ModelState.LOADED) {    if (incrementallyLoadTextures && !justLoaded) {      createResources(this, frameState);    }    if (loadResources.finished()) {      this._loadResources = undefined; // Clear CPU memory since WebGL resources were created.      const resources = this._rendererResources;      const cachedResources = this._cachedRendererResources;      cachedResources.buffers = resources.buffers;      cachedResources.vertexArrays = resources.vertexArrays;      cachedResources.programs = resources.programs;      cachedResources.sourceShaders = resources.sourceShaders;      cachedResources.silhouettePrograms = resources.silhouettePrograms;      cachedResources.textures = resources.textures;      cachedResources.samplers = resources.samplers;      cachedResources.renderStates = resources.renderStates;      cachedResources.ready = true;      // The normal attribute name is required for silhouettes, so get it before the gltf JSON is released      this._normalAttributeName = ModelUtility.getAttributeOrUniformBySemantic(        this.gltf,        "NORMAL"      );      // Vertex arrays are unique to this model, do not store in cache.      if (defined(this._precreatedAttributes)) {        cachedResources.vertexArrays = {};      }      if (this.releaseGltfJson) {        releaseCachedGltf(this);      }    }  }  const silhouette = hasSilhouette(this, frameState);  const translucent = isTranslucent(this);  const invisible = isInvisible(this);  const backFaceCulling = this.backFaceCulling;  const displayConditionPassed = defined(this.distanceDisplayCondition)    ? distanceDisplayConditionVisible(this, frameState)    : true;  const show =    this.show &&    displayConditionPassed &&    this.scale !== 0.0 &&    (!invisible || silhouette);  this._imageBasedLighting.update(frameState);  if ((show && this._state === ModelState.LOADED) || justLoaded) {    const animated =      this.activeAnimations.update(frameState) || this._cesiumAnimationsDirty;    this._cesiumAnimationsDirty = false;    this._dirty = false;    let modelMatrix = this.modelMatrix;    const modeChanged = frameState.mode !== this._mode;    this._mode = frameState.mode;    // Model's model matrix needs to be updated    const modelTransformChanged =      !Matrix4.equals(this._modelMatrix, modelMatrix) ||      this._scale !== this.scale ||      this._minimumPixelSize !== this.minimumPixelSize ||      this.minimumPixelSize !== 0.0 || // Minimum pixel size changed or is enabled      this._maximumScale !== this.maximumScale ||      this._heightReference !== this.heightReference ||      this._heightChanged ||      modeChanged;    if (modelTransformChanged || justLoaded) {      Matrix4.clone(modelMatrix, this._modelMatrix);      updateClamping(this);      if (defined(this._clampedModelMatrix)) {        modelMatrix = this._clampedModelMatrix;      }      this._scale = this.scale;      this._minimumPixelSize = this.minimumPixelSize;      this._maximumScale = this.maximumScale;      this._heightReference = this.heightReference;      this._heightChanged = false;      const scale = getScale(this, frameState);      const computedModelMatrix = this._computedModelMatrix;      Matrix4.multiplyByUniformScale(modelMatrix, scale, computedModelMatrix);      if (this._upAxis === Axis.Y) {        Matrix4.multiplyTransformation(          computedModelMatrix,          Axis.Y_UP_TO_Z_UP,          computedModelMatrix        );      } else if (this._upAxis === Axis.X) {        Matrix4.multiplyTransformation(          computedModelMatrix,          Axis.X_UP_TO_Z_UP,          computedModelMatrix        );      }      if (this.forwardAxis === Axis.Z) {        // glTF 2.0 has a Z-forward convention that must be adapted here to X-forward.        Matrix4.multiplyTransformation(          computedModelMatrix,          Axis.Z_UP_TO_X_UP,          computedModelMatrix        );      }    }    // Update modelMatrix throughout the graph as needed    if (animated || modelTransformChanged || justLoaded) {      updateNodeHierarchyModelMatrix(        this,        modelTransformChanged,        justLoaded,        frameState.mapProjection      );      this._dirty = true;      if (animated || justLoaded) {        // Apply skins if animation changed any node transforms        applySkins(this);      }    }    if (this._perNodeShowDirty) {      this._perNodeShowDirty = false;      updatePerNodeShow(this);    }    updatePickIds(this, context);    updateWireframe(this);    updateShowBoundingVolume(this);    updateShadows(this);    updateClippingPlanes(this, frameState);    // Regenerate shaders if ClippingPlaneCollection state changed or it was removed    const clippingPlanes = this._clippingPlanes;    let currentClippingPlanesState = 0;    // If defined, use the reference matrix to transform miscellaneous properties like    // clipping planes and IBL instead of the modelMatrix. This is so that when    // models are part of a tileset these properties get transformed relative to    // a common reference (such as the root).    const referenceMatrix = defaultValue(this.referenceMatrix, modelMatrix);    if (      this._imageBasedLighting.useSphericalHarmonicCoefficients ||      this._imageBasedLighting.useSpecularEnvironmentMaps    ) {      let iblReferenceFrameMatrix3 = scratchIBLReferenceFrameMatrix3;      let iblReferenceFrameMatrix4 = scratchIBLReferenceFrameMatrix4;      iblReferenceFrameMatrix4 = Matrix4.multiply(        context.uniformState.view3D,        referenceMatrix,        iblReferenceFrameMatrix4      );      iblReferenceFrameMatrix3 = Matrix4.getMatrix3(        iblReferenceFrameMatrix4,        iblReferenceFrameMatrix3      );      iblReferenceFrameMatrix3 = Matrix3.getRotation(        iblReferenceFrameMatrix3,        iblReferenceFrameMatrix3      );      this._iblReferenceFrameMatrix = Matrix3.transpose(        iblReferenceFrameMatrix3,        this._iblReferenceFrameMatrix      );    }    this._shouldRegenerateShaders =      this._shouldRegenerateShaders ||      this._imageBasedLighting.shouldRegenerateShaders;    if (isClippingEnabled(this)) {      let clippingPlanesMatrix = scratchClippingPlanesMatrix;      clippingPlanesMatrix = Matrix4.multiply(        context.uniformState.view3D,        referenceMatrix,        clippingPlanesMatrix      );      clippingPlanesMatrix = Matrix4.multiply(        clippingPlanesMatrix,        clippingPlanes.modelMatrix,        clippingPlanesMatrix      );      this._clippingPlanesMatrix = Matrix4.inverseTranspose(        clippingPlanesMatrix,        this._clippingPlanesMatrix      );      currentClippingPlanesState = clippingPlanes.clippingPlanesState;    }    this._shouldRegenerateShaders =      this._shouldRegenerateShaders ||      this._clippingPlanesState !== currentClippingPlanesState;    this._clippingPlanesState = currentClippingPlanesState;    // Regenerate shaders if color shading changed from last update    const currentlyColorShadingEnabled = isColorShadingEnabled(this);    if (currentlyColorShadingEnabled !== this._colorShadingEnabled) {      this._colorShadingEnabled = currentlyColorShadingEnabled;      this._shouldRegenerateShaders = true;    }    // Regenerate shaders if splitting was enabled/disabled from last update    const splittingEnabled = this.splitDirection !== SplitDirection.NONE;    if (this._splittingEnabled !== splittingEnabled) {      this._splittingEnabled = splittingEnabled;      this._shouldRegenerateShaders = true;    }    if (this._shouldRegenerateShaders) {      regenerateShaders(this, frameState);    } else {      updateColor(this, frameState, false);      updateBackFaceCulling(this, frameState, false);      updateSilhouette(this, frameState, false);    }  }  if (justLoaded) {    // Called after modelMatrix update.    const model = this;    frameState.afterRender.push(function () {      model._ready = true;      model._readyPromise.resolve(model);    });    return;  }  // We don't check show at the top of the function since we  // want to be able to progressively load models when they are not shown,  // and then have them visible immediately when show is set to true.  if (show && !this._ignoreCommands) {    // PERFORMANCE_IDEA: This is terrible    const commandList = frameState.commandList;    const passes = frameState.passes;    const nodeCommands = this._nodeCommands;    const length = nodeCommands.length;    let i;    let nc;    const idl2D =      frameState.mapProjection.ellipsoid.maximumRadius * CesiumMath.PI;    let boundingVolume;    if (passes.render || (passes.pick && this.allowPicking)) {      for (i = 0; i < length; ++i) {        nc = nodeCommands[i];        if (nc.show) {          let command = nc.command;          if (silhouette) {            command = nc.silhouetteModelCommand;          } else if (translucent) {            command = nc.translucentCommand;          } else if (!backFaceCulling) {            command = nc.disableCullingCommand;          }          commandList.push(command);          boundingVolume = nc.command.boundingVolume;          if (            frameState.mode === SceneMode.SCENE2D &&            (boundingVolume.center.y + boundingVolume.radius > idl2D ||              boundingVolume.center.y - boundingVolume.radius < idl2D)          ) {            let command2D = nc.command2D;            if (silhouette) {              command2D = nc.silhouetteModelCommand2D;            } else if (translucent) {              command2D = nc.translucentCommand2D;            } else if (!backFaceCulling) {              command2D = nc.disableCullingCommand2D;            }            commandList.push(command2D);          }        }      }      if (silhouette && !passes.pick) {        // Render second silhouette pass        for (i = 0; i < length; ++i) {          nc = nodeCommands[i];          if (nc.show) {            commandList.push(nc.silhouetteColorCommand);            boundingVolume = nc.command.boundingVolume;            if (              frameState.mode === SceneMode.SCENE2D &&              (boundingVolume.center.y + boundingVolume.radius > idl2D ||                boundingVolume.center.y - boundingVolume.radius < idl2D)            ) {              commandList.push(nc.silhouetteColorCommand2D);            }          }        }      }    }  }  const credit = this._credit;  if (defined(credit)) {    frameState.creditDisplay.addCredit(credit);  }  const resourceCredits = this._resourceCredits;  const resourceCreditsLength = resourceCredits.length;  for (let c = 0; c < resourceCreditsLength; c++) {    frameState.creditDisplay.addCredit(resourceCredits[c]);  }  const gltfCredits = this._gltfCredits;  const gltfCreditsLength = gltfCredits.length;  for (let c = 0; c < gltfCreditsLength; c++) {    frameState.creditDisplay.addCredit(gltfCredits[c]);  }};function destroyIfNotCached(rendererResources, cachedRendererResources) {  if (rendererResources.programs !== cachedRendererResources.programs) {    destroy(rendererResources.programs);  }  if (    rendererResources.silhouettePrograms !==    cachedRendererResources.silhouettePrograms  ) {    destroy(rendererResources.silhouettePrograms);  }}// Run from update iff:// - everything is loaded// - clipping planes state change OR color state set// Run this from destructor after removing color state and clipping plane statefunction regenerateShaders(model, frameState) {  // In regards to _cachedRendererResources:  // Fair to assume that this is data that should just never get modified due to clipping planes, model color, or splitting.  // So if clipping planes, model color, or splitting are active:  // - delink _rendererResources.*programs and create new dictionaries.  // - do NOT destroy any programs - might be used by copies of the model or by might be needed in the future if clipping planes/model color is deactivated  // If clipping planes, model color, and splitting inactive:  // - destroy _rendererResources.*programs  // - relink _rendererResources.*programs to _cachedRendererResources  // In both cases, need to mark commands as dirty, re-run derived commands (elsewhere)  const rendererResources = model._rendererResources;  const cachedRendererResources = model._cachedRendererResources;  destroyIfNotCached(rendererResources, cachedRendererResources);  let programId;  if (    isClippingEnabled(model) ||    isColorShadingEnabled(model) ||    model.splitDirection !== SplitDirection.NONE ||    model._shouldRegenerateShaders  ) {    model._shouldRegenerateShaders = false;    rendererResources.programs = {};    rendererResources.silhouettePrograms = {};    const visitedPrograms = {};    const techniques = model._sourceTechniques;    let technique;    for (const techniqueId in techniques) {      if (techniques.hasOwnProperty(techniqueId)) {        technique = techniques[techniqueId];        programId = technique.program;        if (!visitedPrograms[programId]) {          visitedPrograms[programId] = true;          recreateProgram(            {              programId: programId,              techniqueId: techniqueId,            },            model,            frameState.context          );        }      }    }  } else {    rendererResources.programs = cachedRendererResources.programs;    rendererResources.silhouettePrograms =      cachedRendererResources.silhouettePrograms;  }  // Fix all the commands, marking them as dirty so everything that derives will re-derive  const rendererPrograms = rendererResources.programs;  const nodeCommands = model._nodeCommands;  const commandCount = nodeCommands.length;  for (let i = 0; i < commandCount; ++i) {    const nodeCommand = nodeCommands[i];    programId = nodeCommand.programId;    const renderProgram = rendererPrograms[programId];    nodeCommand.command.shaderProgram = renderProgram;    if (defined(nodeCommand.command2D)) {      nodeCommand.command2D.shaderProgram = renderProgram;    }  }  // Force update silhouette commands/shaders  updateColor(model, frameState, true);  updateBackFaceCulling(model, frameState, true);  updateSilhouette(model, frameState, true);}/** * Returns true if this object was destroyed; otherwise, false. * <br /><br /> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>. * * @see Model#destroy */Model.prototype.isDestroyed = function () {  return false;};/** * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. * <br /><br /> * Once an object is destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.  Therefore, * assign the return value (<code>undefined</code>) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * model = model && model.destroy(); * * @see Model#isDestroyed */Model.prototype.destroy = function () {  // Vertex arrays are unique to this model, destroy here.  if (defined(this._precreatedAttributes)) {    destroy(this._rendererResources.vertexArrays);  }  if (defined(this._removeUpdateHeightCallback)) {    this._removeUpdateHeightCallback();    this._removeUpdateHeightCallback = undefined;  }  if (defined(this._terrainProviderChangedCallback)) {    this._terrainProviderChangedCallback();    this._terrainProviderChangedCallback = undefined;  }  // Shaders modified for clipping and for color don't get cached, so destroy these manually  if (defined(this._cachedRendererResources)) {    destroyIfNotCached(this._rendererResources, this._cachedRendererResources);  }  this._rendererResources = undefined;  this._cachedRendererResources =    this._cachedRendererResources && this._cachedRendererResources.release();  DracoLoader.destroyCachedDataForModel(this);  const pickIds = this._pickIds;  const length = pickIds.length;  for (let i = 0; i < length; ++i) {    pickIds[i].destroy();  }  releaseCachedGltf(this);  this._quantizedVertexShaders = undefined;  // Only destroy the ClippingPlaneCollection if this is the owner - if this model is part of a Cesium3DTileset,  // _clippingPlanes references a ClippingPlaneCollection that this model does not own.  const clippingPlaneCollection = this._clippingPlanes;  if (    defined(clippingPlaneCollection) &&    !clippingPlaneCollection.isDestroyed() &&    clippingPlaneCollection.owner === this  ) {    clippingPlaneCollection.destroy();  }  this._clippingPlanes = undefined;  if (    this._shouldDestroyImageBasedLighting &&    !this._imageBasedLighting.isDestroyed()  ) {    this._imageBasedLighting.destroy();  }  this._imageBasedLighting = undefined;  return destroyObject(this);};// exposed for testingModel._getClippingFunction = getClippingFunction;Model._modifyShaderForColor = modifyShaderForColor;export default Model;
 |