Model.js 192 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartesian4 from "../Core/Cartesian4.js";
  4. import Cartographic from "../Core/Cartographic.js";
  5. import Check from "../Core/Check.js";
  6. import clone from "../Core/clone.js";
  7. import Color from "../Core/Color.js";
  8. import combine from "../Core/combine.js";
  9. import createGuid from "../Core/createGuid.js";
  10. import Credit from "../Core/Credit.js";
  11. import defaultValue from "../Core/defaultValue.js";
  12. import defer from "../Core/defer.js";
  13. import defined from "../Core/defined.js";
  14. import deprecationWarning from "../Core/deprecationWarning.js";
  15. import destroyObject from "../Core/destroyObject.js";
  16. import DeveloperError from "../Core/DeveloperError.js";
  17. import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js";
  18. import FeatureDetection from "../Core/FeatureDetection.js";
  19. import getAbsoluteUri from "../Core/getAbsoluteUri.js";
  20. import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";
  21. import getMagic from "../Core/getMagic.js";
  22. import getStringFromTypedArray from "../Core/getStringFromTypedArray.js";
  23. import IndexDatatype from "../Core/IndexDatatype.js";
  24. import ImageBasedLighting from "./ImageBasedLighting.js";
  25. import loadImageFromTypedArray from "../Core/loadImageFromTypedArray.js";
  26. import loadKTX2 from "../Core/loadKTX2.js";
  27. import CesiumMath from "../Core/Math.js";
  28. import Matrix3 from "../Core/Matrix3.js";
  29. import Matrix4 from "../Core/Matrix4.js";
  30. import PixelFormat from "../Core/PixelFormat.js";
  31. import PrimitiveType from "../Core/PrimitiveType.js";
  32. import Quaternion from "../Core/Quaternion.js";
  33. import Resource from "../Core/Resource.js";
  34. import Transforms from "../Core/Transforms.js";
  35. import WebGLConstants from "../Core/WebGLConstants.js";
  36. import Buffer from "../Renderer/Buffer.js";
  37. import BufferUsage from "../Renderer/BufferUsage.js";
  38. import DrawCommand from "../Renderer/DrawCommand.js";
  39. import Pass from "../Renderer/Pass.js";
  40. import RenderState from "../Renderer/RenderState.js";
  41. import Sampler from "../Renderer/Sampler.js";
  42. import ShaderProgram from "../Renderer/ShaderProgram.js";
  43. import ShaderSource from "../Renderer/ShaderSource.js";
  44. import Texture from "../Renderer/Texture.js";
  45. import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
  46. import TextureWrap from "../Renderer/TextureWrap.js";
  47. import VertexArray from "../Renderer/VertexArray.js";
  48. import addDefaults from "./GltfPipeline/addDefaults.js";
  49. import addPipelineExtras from "./GltfPipeline/addPipelineExtras.js";
  50. import ForEach from "./GltfPipeline/ForEach.js";
  51. import getAccessorByteStride from "./GltfPipeline/getAccessorByteStride.js";
  52. import usesExtension from "./GltfPipeline/usesExtension.js";
  53. import numberOfComponentsForType from "./GltfPipeline/numberOfComponentsForType.js";
  54. import parseGlb from "./GltfPipeline/parseGlb.js";
  55. import updateVersion from "./GltfPipeline/updateVersion.js";
  56. import Axis from "./Axis.js";
  57. import BlendingState from "./BlendingState.js";
  58. import ClippingPlaneCollection from "./ClippingPlaneCollection.js";
  59. import ColorBlendMode from "./ColorBlendMode.js";
  60. import DepthFunction from "./DepthFunction.js";
  61. import DracoLoader from "./DracoLoader.js";
  62. import getClipAndStyleCode from "./getClipAndStyleCode.js";
  63. import getClippingFunction from "./getClippingFunction.js";
  64. import HeightReference from "./HeightReference.js";
  65. import JobType from "./JobType.js";
  66. import ModelAnimationCache from "./ModelAnimationCache.js";
  67. import ModelAnimationCollection from "./ModelAnimationCollection.js";
  68. import ModelLoadResources from "./ModelLoadResources.js";
  69. import ModelMaterial from "./ModelMaterial.js";
  70. import ModelMesh from "./ModelMesh.js";
  71. import ModelNode from "./ModelNode.js";
  72. import ModelOutlineLoader from "./ModelOutlineLoader.js";
  73. import ModelUtility from "./ModelUtility.js";
  74. import OctahedralProjectedCubeMap from "./OctahedralProjectedCubeMap.js";
  75. import processModelMaterialsCommon from "./processModelMaterialsCommon.js";
  76. import processPbrMaterials from "./processPbrMaterials.js";
  77. import SceneMode from "./SceneMode.js";
  78. import ShadowMode from "./ShadowMode.js";
  79. import SplitDirection from "./SplitDirection.js";
  80. import Splitter from "./Splitter.js";
  81. import StencilConstants from "./StencilConstants.js";
  82. const boundingSphereCartesian3Scratch = new Cartesian3();
  83. const ModelState = ModelUtility.ModelState;
  84. // glTF MIME types discussed in https://github.com/KhronosGroup/glTF/issues/412 and https://github.com/KhronosGroup/glTF/issues/943
  85. const defaultModelAccept =
  86. "model/gltf-binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01";
  87. const articulationEpsilon = CesiumMath.EPSILON16;
  88. ///////////////////////////////////////////////////////////////////////////
  89. function setCachedGltf(model, cachedGltf) {
  90. model._cachedGltf = cachedGltf;
  91. }
  92. // glTF JSON can be big given embedded geometry, textures, and animations, so we
  93. // cache it across all models using the same url/cache-key. This also reduces the
  94. // slight overhead in assigning defaults to missing values.
  95. //
  96. // Note that this is a global cache, compared to renderer resources, which
  97. // are cached per context.
  98. function CachedGltf(options) {
  99. this._gltf = options.gltf;
  100. this.ready = options.ready;
  101. this.modelsToLoad = [];
  102. this.count = 0;
  103. }
  104. Object.defineProperties(CachedGltf.prototype, {
  105. gltf: {
  106. set: function (value) {
  107. this._gltf = value;
  108. },
  109. get: function () {
  110. return this._gltf;
  111. },
  112. },
  113. });
  114. CachedGltf.prototype.makeReady = function (gltfJson) {
  115. this.gltf = gltfJson;
  116. const models = this.modelsToLoad;
  117. const length = models.length;
  118. for (let i = 0; i < length; ++i) {
  119. const m = models[i];
  120. if (!m.isDestroyed()) {
  121. setCachedGltf(m, this);
  122. }
  123. }
  124. this.modelsToLoad = undefined;
  125. this.ready = true;
  126. };
  127. const gltfCache = {};
  128. const uriToGuid = {};
  129. ///////////////////////////////////////////////////////////////////////////
  130. /**
  131. * A 3D model based on glTF, the runtime asset format for WebGL, OpenGL ES, and OpenGL.
  132. * <p>
  133. * Cesium includes support for geometry and materials, glTF animations, and glTF skinning.
  134. * In addition, individual glTF nodes are pickable with {@link Scene#pick} and animatable
  135. * with {@link Model#getNode}. glTF cameras and lights are not currently supported.
  136. * </p>
  137. * <p>
  138. * An external glTF asset is created with {@link Model.fromGltf}. glTF JSON can also be
  139. * created at runtime and passed to this constructor function. In either case, the
  140. * {@link Model#readyPromise} is resolved when the model is ready to render, i.e.,
  141. * when the external binary, image, and shader files are downloaded and the WebGL
  142. * resources are created.
  143. * </p>
  144. * <p>
  145. * Cesium supports glTF assets with the following extensions:
  146. * <ul>
  147. * <li>
  148. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)}
  149. * </li><li>
  150. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)}
  151. * </li><li>
  152. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)}
  153. * </li><li>
  154. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations}
  155. * </li><li>
  156. * {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)}
  157. * </li><li>
  158. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression}
  159. * </li><li>
  160. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness}
  161. * </li><li>
  162. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit}
  163. * </li><li>
  164. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl}
  165. * </li><li>
  166. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform}
  167. * </li><li>
  168. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_basisu|KHR_texture_basisu}
  169. * </li>
  170. * </ul>
  171. * </p>
  172. * <p>
  173. * Note: for models with compressed textures using the KHR_texture_basisu extension, we recommend power of 2 textures in both dimensions
  174. * 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})
  175. * 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}).
  176. * </p>
  177. * <p>
  178. * 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
  179. * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated
  180. * relative to a local origin.
  181. * </p>
  182. *
  183. * @alias Model
  184. * @constructor
  185. *
  186. * @param {Object} [options] Object with the following properties:
  187. * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] A glTF JSON object, or a binary glTF buffer.
  188. * @param {Resource|String} [options.basePath=''] The base path that paths in the glTF JSON are relative to.
  189. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  190. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  191. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  192. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  193. * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize.
  194. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
  195. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
  196. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
  197. * @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.
  198. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
  199. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources.
  200. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  201. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  202. * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain.
  203. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property.
  204. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed.
  205. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color.
  206. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
  207. * @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.
  208. * @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.
  209. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels.
  210. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
  211. * @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.
  212. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
  213. * @param {ImageBasedLighting} [options.imageBasedLighting] The properties for managing image-based lighting on this model.
  214. * @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.
  215. * @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.
  216. * @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.
  217. * @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.
  218. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
  219. * @param {Boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen.
  220. * @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.
  221. * @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.
  222. * @param {SplitDirection} [options.splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this model.
  223. *
  224. *
  225. * @see Model.fromGltf
  226. *
  227. * @demo {@link https://sandcastle.cesium.com/index.html?src=3D%20Models.html|Cesium Sandcastle Models Demo}
  228. */
  229. function Model(options) {
  230. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  231. const cacheKey = options.cacheKey;
  232. this._cacheKey = cacheKey;
  233. this._cachedGltf = undefined;
  234. this._releaseGltfJson = defaultValue(options.releaseGltfJson, false);
  235. let cachedGltf;
  236. if (
  237. defined(cacheKey) &&
  238. defined(gltfCache[cacheKey]) &&
  239. gltfCache[cacheKey].ready
  240. ) {
  241. // glTF JSON is in cache and ready
  242. cachedGltf = gltfCache[cacheKey];
  243. ++cachedGltf.count;
  244. } else {
  245. // glTF was explicitly provided, e.g., when a user uses the Model constructor directly
  246. let gltf = options.gltf;
  247. if (defined(gltf)) {
  248. if (gltf instanceof ArrayBuffer) {
  249. gltf = new Uint8Array(gltf);
  250. }
  251. if (gltf instanceof Uint8Array) {
  252. // Binary glTF
  253. const parsedGltf = parseGlb(gltf);
  254. cachedGltf = new CachedGltf({
  255. gltf: parsedGltf,
  256. ready: true,
  257. });
  258. } else {
  259. // Normal glTF (JSON)
  260. cachedGltf = new CachedGltf({
  261. gltf: options.gltf,
  262. ready: true,
  263. });
  264. }
  265. cachedGltf.count = 1;
  266. if (defined(cacheKey)) {
  267. gltfCache[cacheKey] = cachedGltf;
  268. }
  269. }
  270. }
  271. setCachedGltf(this, cachedGltf);
  272. const basePath = defaultValue(options.basePath, "");
  273. this._resource = Resource.createIfNeeded(basePath);
  274. // User specified credit
  275. let credit = options.credit;
  276. if (typeof credit === "string") {
  277. credit = new Credit(credit);
  278. }
  279. this._credit = credit;
  280. // List of credits to be added from the Resource if it is an IonResource
  281. this._resourceCredits = [];
  282. // List of credits to be added from the glTF
  283. this._gltfCredits = [];
  284. this._showCreditsOnScreen = defaultValue(options.showCreditsOnScreen, false);
  285. /**
  286. * Determines if the model primitive will be shown.
  287. *
  288. * @type {Boolean}
  289. *
  290. * @default true
  291. */
  292. this.show = defaultValue(options.show, true);
  293. /**
  294. * The silhouette color.
  295. *
  296. * @type {Color}
  297. *
  298. * @default Color.RED
  299. */
  300. this.silhouetteColor = defaultValue(options.silhouetteColor, Color.RED);
  301. this._silhouetteColor = new Color();
  302. this._silhouetteColorPreviousAlpha = 1.0;
  303. this._normalAttributeName = undefined;
  304. /**
  305. * The size of the silhouette in pixels.
  306. *
  307. * @type {Number}
  308. *
  309. * @default 0.0
  310. */
  311. this.silhouetteSize = defaultValue(options.silhouetteSize, 0.0);
  312. /**
  313. * The 4x4 transformation matrix that transforms the model from model to world coordinates.
  314. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  315. * Local reference frames can be used by providing a different transformation matrix, like that returned
  316. * by {@link Transforms.eastNorthUpToFixedFrame}.
  317. *
  318. * @type {Matrix4}
  319. *
  320. * @default {@link Matrix4.IDENTITY}
  321. *
  322. * @example
  323. * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  324. * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  325. */
  326. this.modelMatrix = Matrix4.clone(
  327. defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  328. );
  329. this._modelMatrix = Matrix4.clone(this.modelMatrix);
  330. this._clampedModelMatrix = undefined;
  331. /**
  332. * A uniform scale applied to this model before the {@link Model#modelMatrix}.
  333. * Values greater than <code>1.0</code> increase the size of the model; values
  334. * less than <code>1.0</code> decrease.
  335. *
  336. * @type {Number}
  337. *
  338. * @default 1.0
  339. */
  340. this.scale = defaultValue(options.scale, 1.0);
  341. this._scale = this.scale;
  342. /**
  343. * The approximate minimum pixel size of the model regardless of zoom.
  344. * This can be used to ensure that a model is visible even when the viewer
  345. * zooms out. When <code>0.0</code>, no minimum size is enforced.
  346. *
  347. * @type {Number}
  348. *
  349. * @default 0.0
  350. */
  351. this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0);
  352. this._minimumPixelSize = this.minimumPixelSize;
  353. /**
  354. * The maximum scale size for a model. This can be used to give
  355. * an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model
  356. * is never an unreasonable scale.
  357. *
  358. * @type {Number}
  359. */
  360. this.maximumScale = options.maximumScale;
  361. this._maximumScale = this.maximumScale;
  362. /**
  363. * User-defined object returned when the model is picked.
  364. *
  365. * @type Object
  366. *
  367. * @default undefined
  368. *
  369. * @see Scene#pick
  370. */
  371. this.id = options.id;
  372. this._id = options.id;
  373. /**
  374. * Returns the height reference of the model
  375. *
  376. * @type {HeightReference}
  377. *
  378. * @default HeightReference.NONE
  379. */
  380. this.heightReference = defaultValue(
  381. options.heightReference,
  382. HeightReference.NONE
  383. );
  384. this._heightReference = this.heightReference;
  385. this._heightChanged = false;
  386. this._removeUpdateHeightCallback = undefined;
  387. const scene = options.scene;
  388. this._scene = scene;
  389. if (defined(scene) && defined(scene.terrainProviderChanged)) {
  390. this._terrainProviderChangedCallback = scene.terrainProviderChanged.addEventListener(
  391. function () {
  392. this._heightChanged = true;
  393. },
  394. this
  395. );
  396. }
  397. /**
  398. * Used for picking primitives that wrap a model.
  399. *
  400. * @private
  401. */
  402. this._pickObject = options.pickObject;
  403. this._allowPicking = defaultValue(options.allowPicking, true);
  404. this._ready = false;
  405. this._readyPromise = defer();
  406. /**
  407. * The currently playing glTF animations.
  408. *
  409. * @type {ModelAnimationCollection}
  410. */
  411. this.activeAnimations = new ModelAnimationCollection(this);
  412. /**
  413. * Determines if the model's animations should hold a pose over frames where no keyframes are specified.
  414. *
  415. * @type {Boolean}
  416. */
  417. this.clampAnimations = defaultValue(options.clampAnimations, true);
  418. this._defaultTexture = undefined;
  419. this._incrementallyLoadTextures = defaultValue(
  420. options.incrementallyLoadTextures,
  421. true
  422. );
  423. this._asynchronous = defaultValue(options.asynchronous, true);
  424. /**
  425. * Determines whether the model casts or receives shadows from light sources.
  426. *
  427. * @type {ShadowMode}
  428. *
  429. * @default ShadowMode.ENABLED
  430. */
  431. this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
  432. this._shadows = this.shadows;
  433. /**
  434. * A color that blends with the model's rendered color.
  435. *
  436. * @type {Color}
  437. *
  438. * @default Color.WHITE
  439. */
  440. this.color = Color.clone(defaultValue(options.color, Color.WHITE));
  441. this._colorPreviousAlpha = 1.0;
  442. /**
  443. * Defines how the color blends with the model.
  444. *
  445. * @type {ColorBlendMode}
  446. *
  447. * @default ColorBlendMode.HIGHLIGHT
  448. */
  449. this.colorBlendMode = defaultValue(
  450. options.colorBlendMode,
  451. ColorBlendMode.HIGHLIGHT
  452. );
  453. /**
  454. * Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>.
  455. * A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with
  456. * any value in-between resulting in a mix of the two.
  457. *
  458. * @type {Number}
  459. *
  460. * @default 0.5
  461. */
  462. this.colorBlendAmount = defaultValue(options.colorBlendAmount, 0.5);
  463. this._colorShadingEnabled = false;
  464. this._clippingPlanes = undefined;
  465. this.clippingPlanes = options.clippingPlanes;
  466. // Used for checking if shaders need to be regenerated due to clipping plane changes.
  467. this._clippingPlanesState = 0;
  468. // If defined, use this matrix to transform miscellaneous properties like
  469. // clipping planes and IBL instead of the modelMatrix. This is so that when
  470. // models are part of a tileset these properties get transformed relative to
  471. // a common reference (such as the root).
  472. this.referenceMatrix = undefined;
  473. /**
  474. * Whether to cull back-facing geometry. When true, back face culling is
  475. * determined by the material's doubleSided property; when false, back face
  476. * culling is disabled. Back faces are not culled if {@link Model#color} is
  477. * translucent or {@link Model#silhouetteSize} is greater than 0.0.
  478. *
  479. * @type {Boolean}
  480. *
  481. * @default true
  482. */
  483. this.backFaceCulling = defaultValue(options.backFaceCulling, true);
  484. /**
  485. * Whether to display the outline for models using the
  486. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension.
  487. * When true, outlines are displayed. When false, outlines are not displayed.
  488. *
  489. * @type {Boolean}
  490. * @readonly
  491. *
  492. * @default true
  493. */
  494. this.showOutline = defaultValue(options.showOutline, true);
  495. /**
  496. * The {@link SplitDirection} to apply to this model.
  497. *
  498. * @type {SplitDirection}
  499. * @default {@link SplitDirection.NONE}
  500. */
  501. this.splitDirection = defaultValue(
  502. options.splitDirection,
  503. SplitDirection.NONE
  504. );
  505. this._splittingEnabled = false;
  506. /**
  507. * This property is for debugging only; it is not for production use nor is it optimized.
  508. * <p>
  509. * Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds
  510. * to one draw command. A glTF mesh has an array of primitives, often of length one.
  511. * </p>
  512. *
  513. * @type {Boolean}
  514. *
  515. * @default false
  516. */
  517. this.debugShowBoundingVolume = defaultValue(
  518. options.debugShowBoundingVolume,
  519. false
  520. );
  521. this._debugShowBoundingVolume = false;
  522. /**
  523. * This property is for debugging only; it is not for production use nor is it optimized.
  524. * <p>
  525. * Draws the model in wireframe.
  526. * </p>
  527. *
  528. * @type {Boolean}
  529. *
  530. * @default false
  531. */
  532. this.debugWireframe = defaultValue(options.debugWireframe, false);
  533. this._debugWireframe = false;
  534. this._distanceDisplayCondition = options.distanceDisplayCondition;
  535. // Undocumented options
  536. this._addBatchIdToGeneratedShaders = options.addBatchIdToGeneratedShaders;
  537. this._precreatedAttributes = options.precreatedAttributes;
  538. this._vertexShaderLoaded = options.vertexShaderLoaded;
  539. this._fragmentShaderLoaded = options.fragmentShaderLoaded;
  540. this._uniformMapLoaded = options.uniformMapLoaded;
  541. this._pickIdLoaded = options.pickIdLoaded;
  542. this._ignoreCommands = defaultValue(options.ignoreCommands, false);
  543. this._requestType = options.requestType;
  544. this._upAxis = defaultValue(options.upAxis, Axis.Y);
  545. this._gltfForwardAxis = Axis.Z;
  546. this._forwardAxis = options.forwardAxis;
  547. /**
  548. * @private
  549. * @readonly
  550. */
  551. this.cull = defaultValue(options.cull, true);
  552. /**
  553. * @private
  554. * @readonly
  555. */
  556. this.opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
  557. this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale
  558. this._clippingPlanesMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from reference matrix and the current view matrix
  559. this._iblReferenceFrameMatrix = Matrix3.clone(Matrix3.IDENTITY); // Derived from reference matrix and the current view matrix
  560. this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins
  561. this._boundingSphere = undefined;
  562. this._scaledBoundingSphere = new BoundingSphere();
  563. this._state = ModelState.NEEDS_LOAD;
  564. this._loadResources = undefined;
  565. this._mode = undefined;
  566. this._perNodeShowDirty = false; // true when the Cesium API was used to change a node's show property
  567. this._cesiumAnimationsDirty = false; // true when the Cesium API, not a glTF animation, changed a node transform
  568. this._dirty = false; // true when the model was transformed this frame
  569. this._maxDirtyNumber = 0; // Used in place of a dirty boolean flag to avoid an extra graph traversal
  570. this._runtime = {
  571. animations: undefined,
  572. articulationsByName: undefined,
  573. articulationsByStageKey: undefined,
  574. stagesByKey: undefined,
  575. rootNodes: undefined,
  576. nodes: undefined, // Indexed with the node's index
  577. nodesByName: undefined, // Indexed with name property in the node
  578. skinnedNodes: undefined,
  579. meshesByName: undefined, // Indexed with the name property in the mesh
  580. materialsByName: undefined, // Indexed with the name property in the material
  581. materialsById: undefined, // Indexed with the material's index
  582. };
  583. this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation
  584. this._extensionsUsed = undefined; // Cached used glTF extensions
  585. this._extensionsRequired = undefined; // Cached required glTF extensions
  586. this._quantizedUniforms = {}; // Quantized uniforms for each program for WEB3D_quantized_attributes
  587. this._programPrimitives = {};
  588. this._rendererResources = {
  589. // Cached between models with the same url/cache-key
  590. buffers: {},
  591. vertexArrays: {},
  592. programs: {},
  593. sourceShaders: {},
  594. silhouettePrograms: {},
  595. textures: {},
  596. samplers: {},
  597. renderStates: {},
  598. };
  599. this._cachedRendererResources = undefined;
  600. this._loadRendererResourcesFromCache = false;
  601. this._dequantizeInShader = defaultValue(options.dequantizeInShader, true);
  602. this._decodedData = {};
  603. this._cachedGeometryByteLength = 0;
  604. this._cachedTexturesByteLength = 0;
  605. this._geometryByteLength = 0;
  606. this._texturesByteLength = 0;
  607. this._trianglesLength = 0;
  608. this._pointsLength = 0;
  609. // Hold references for shader reconstruction.
  610. // Hold these separately because _cachedGltf may get released (this.releaseGltfJson)
  611. this._sourceTechniques = {};
  612. this._sourcePrograms = {};
  613. this._quantizedVertexShaders = {};
  614. this._nodeCommands = [];
  615. this._pickIds = [];
  616. // CESIUM_RTC extension
  617. this._rtcCenter = undefined; // reference to either 3D or 2D
  618. this._rtcCenterEye = undefined; // in eye coordinates
  619. this._rtcCenter3D = undefined; // in world coordinates
  620. this._rtcCenter2D = undefined; // in projected world coordinates
  621. this._sourceVersion = undefined;
  622. this._sourceKHRTechniquesWebGL = undefined;
  623. this._lightColor = Cartesian3.clone(options.lightColor);
  624. const hasIndividualIBLParameters =
  625. defined(options.imageBasedLightingFactor) ||
  626. defined(options.luminanceAtZenith) ||
  627. defined(options.sphericalHarmonicCoefficients) ||
  628. defined(options.specularEnvironmentMaps);
  629. if (defined(options.imageBasedLighting)) {
  630. this._imageBasedLighting = options.imageBasedLighting;
  631. this._shouldDestroyImageBasedLighting = false;
  632. } else if (hasIndividualIBLParameters) {
  633. deprecationWarning(
  634. "ImageBasedLightingConstructor",
  635. "Individual image-based lighting parameters were deprecated in Cesium 1.92. They will be removed in version 1.94. Use options.imageBasedLighting instead."
  636. );
  637. // Create image-based lighting from the old constructor parameters.
  638. this._imageBasedLighting = new ImageBasedLighting({
  639. imageBasedLightingFactor: options.imageBasedLightingFactor,
  640. luminanceAtZenith: options.luminanceAtZenith,
  641. sphericalHarmonicCoefficients: options.sphericalHarmonicCoefficients,
  642. specularEnvironmentMaps: options.specularEnvironmentMaps,
  643. });
  644. this._shouldDestroyImageBasedLighting = true;
  645. } else {
  646. this._imageBasedLighting = new ImageBasedLighting();
  647. this._shouldDestroyImageBasedLighting = true;
  648. }
  649. this._shouldRegenerateShaders = false;
  650. }
  651. Object.defineProperties(Model.prototype, {
  652. /**
  653. * The object for the glTF JSON, including properties with default values omitted
  654. * from the JSON provided to this model.
  655. *
  656. * @memberof Model.prototype
  657. *
  658. * @type {Object}
  659. * @readonly
  660. *
  661. * @default undefined
  662. */
  663. gltf: {
  664. get: function () {
  665. return defined(this._cachedGltf) ? this._cachedGltf.gltf : undefined;
  666. },
  667. },
  668. /**
  669. * When <code>true</code>, the glTF JSON is not stored with the model once the model is
  670. * loaded (when {@link Model#ready} is <code>true</code>). This saves memory when
  671. * geometry, textures, and animations are embedded in the .gltf file.
  672. * This is especially useful for cases like 3D buildings, where each .gltf model is unique
  673. * and caching the glTF JSON is not effective.
  674. *
  675. * @memberof Model.prototype
  676. *
  677. * @type {Boolean}
  678. * @readonly
  679. *
  680. * @default false
  681. *
  682. * @private
  683. */
  684. releaseGltfJson: {
  685. get: function () {
  686. return this._releaseGltfJson;
  687. },
  688. },
  689. /**
  690. * The key identifying this model in the model cache for glTF JSON, renderer resources, and animations.
  691. * Caching saves memory and improves loading speed when several models with the same url are created.
  692. * <p>
  693. * This key is automatically generated when the model is created with {@link Model.fromGltf}. If the model
  694. * is created directly from glTF JSON using the {@link Model} constructor, this key can be manually
  695. * provided; otherwise, the model will not be changed.
  696. * </p>
  697. *
  698. * @memberof Model.prototype
  699. *
  700. * @type {String}
  701. * @readonly
  702. *
  703. * @private
  704. */
  705. cacheKey: {
  706. get: function () {
  707. return this._cacheKey;
  708. },
  709. },
  710. /**
  711. * The base path that paths in the glTF JSON are relative to. The base
  712. * path is the same path as the path containing the .gltf file
  713. * minus the .gltf file, when binary, image, and shader files are
  714. * in the same directory as the .gltf. When this is <code>''</code>,
  715. * the app's base path is used.
  716. *
  717. * @memberof Model.prototype
  718. *
  719. * @type {String}
  720. * @readonly
  721. *
  722. * @default ''
  723. */
  724. basePath: {
  725. get: function () {
  726. return this._resource.url;
  727. },
  728. },
  729. /**
  730. * The model's bounding sphere in its local coordinate system. This does not take into
  731. * account glTF animations and skins nor does it take into account {@link Model#minimumPixelSize}.
  732. *
  733. * @memberof Model.prototype
  734. *
  735. * @type {BoundingSphere}
  736. * @readonly
  737. *
  738. * @default undefined
  739. *
  740. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  741. *
  742. * @example
  743. * // Center in WGS84 coordinates
  744. * const center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
  745. */
  746. boundingSphere: {
  747. get: function () {
  748. //>>includeStart('debug', pragmas.debug);
  749. if (this._state !== ModelState.LOADED) {
  750. throw new DeveloperError(
  751. "The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true."
  752. );
  753. }
  754. //>>includeEnd('debug');
  755. let modelMatrix = this.modelMatrix;
  756. if (
  757. this.heightReference !== HeightReference.NONE &&
  758. this._clampedModelMatrix
  759. ) {
  760. modelMatrix = this._clampedModelMatrix;
  761. }
  762. const nonUniformScale = Matrix4.getScale(
  763. modelMatrix,
  764. boundingSphereCartesian3Scratch
  765. );
  766. const scale = defined(this.maximumScale)
  767. ? Math.min(this.maximumScale, this.scale)
  768. : this.scale;
  769. Cartesian3.multiplyByScalar(nonUniformScale, scale, nonUniformScale);
  770. const scaledBoundingSphere = this._scaledBoundingSphere;
  771. scaledBoundingSphere.center = Cartesian3.multiplyComponents(
  772. this._boundingSphere.center,
  773. nonUniformScale,
  774. scaledBoundingSphere.center
  775. );
  776. scaledBoundingSphere.radius =
  777. Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius;
  778. if (defined(this._rtcCenter)) {
  779. Cartesian3.add(
  780. this._rtcCenter,
  781. scaledBoundingSphere.center,
  782. scaledBoundingSphere.center
  783. );
  784. }
  785. return scaledBoundingSphere;
  786. },
  787. },
  788. /**
  789. * When <code>true</code>, this model is ready to render, i.e., the external binary, image,
  790. * and shader files were downloaded and the WebGL resources were created. This is set to
  791. * <code>true</code> right before {@link Model#readyPromise} is resolved.
  792. *
  793. * @memberof Model.prototype
  794. *
  795. * @type {Boolean}
  796. * @readonly
  797. *
  798. * @default false
  799. */
  800. ready: {
  801. get: function () {
  802. return this._ready;
  803. },
  804. },
  805. /**
  806. * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image,
  807. * and shader files were downloaded and the WebGL resources were created.
  808. * <p>
  809. * This promise is resolved at the end of the frame before the first frame the model is rendered in.
  810. * </p>
  811. *
  812. * @memberof Model.prototype
  813. * @type {Promise.<Model>}
  814. * @readonly
  815. *
  816. * @example
  817. * // Play all animations at half-speed when the model is ready to render
  818. * Promise.resolve(model.readyPromise).then(function(model) {
  819. * model.activeAnimations.addAll({
  820. * multiplier : 0.5
  821. * });
  822. * }).catch(function(error){
  823. * window.alert(error);
  824. * });
  825. *
  826. * @see Model#ready
  827. */
  828. readyPromise: {
  829. get: function () {
  830. return this._readyPromise.promise;
  831. },
  832. },
  833. /**
  834. * Determines if model WebGL resource creation will be spread out over several frames or
  835. * block until completion once all glTF files are loaded.
  836. *
  837. * @memberof Model.prototype
  838. *
  839. * @type {Boolean}
  840. * @readonly
  841. *
  842. * @default true
  843. */
  844. asynchronous: {
  845. get: function () {
  846. return this._asynchronous;
  847. },
  848. },
  849. /**
  850. * When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  851. *
  852. * @memberof Model.prototype
  853. *
  854. * @type {Boolean}
  855. * @readonly
  856. *
  857. * @default true
  858. */
  859. allowPicking: {
  860. get: function () {
  861. return this._allowPicking;
  862. },
  863. },
  864. /**
  865. * Determine if textures may continue to stream in after the model is loaded.
  866. *
  867. * @memberof Model.prototype
  868. *
  869. * @type {Boolean}
  870. * @readonly
  871. *
  872. * @default true
  873. */
  874. incrementallyLoadTextures: {
  875. get: function () {
  876. return this._incrementallyLoadTextures;
  877. },
  878. },
  879. /**
  880. * Return the number of pending texture loads.
  881. *
  882. * @memberof Model.prototype
  883. *
  884. * @type {Number}
  885. * @readonly
  886. */
  887. pendingTextureLoads: {
  888. get: function () {
  889. return defined(this._loadResources)
  890. ? this._loadResources.pendingTextureLoads
  891. : 0;
  892. },
  893. },
  894. /**
  895. * Returns true if the model was transformed this frame
  896. *
  897. * @memberof Model.prototype
  898. *
  899. * @type {Boolean}
  900. * @readonly
  901. *
  902. * @private
  903. */
  904. dirty: {
  905. get: function () {
  906. return this._dirty;
  907. },
  908. },
  909. /**
  910. * Gets or sets the condition specifying at what distance from the camera that this model will be displayed.
  911. * @memberof Model.prototype
  912. * @type {DistanceDisplayCondition}
  913. * @default undefined
  914. */
  915. distanceDisplayCondition: {
  916. get: function () {
  917. return this._distanceDisplayCondition;
  918. },
  919. set: function (value) {
  920. //>>includeStart('debug', pragmas.debug);
  921. if (defined(value) && value.far <= value.near) {
  922. throw new DeveloperError("far must be greater than near");
  923. }
  924. //>>includeEnd('debug');
  925. this._distanceDisplayCondition = DistanceDisplayCondition.clone(
  926. value,
  927. this._distanceDisplayCondition
  928. );
  929. },
  930. },
  931. extensionsUsed: {
  932. get: function () {
  933. if (!defined(this._extensionsUsed)) {
  934. this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf);
  935. }
  936. return this._extensionsUsed;
  937. },
  938. },
  939. extensionsRequired: {
  940. get: function () {
  941. if (!defined(this._extensionsRequired)) {
  942. this._extensionsRequired = ModelUtility.getRequiredExtensions(
  943. this.gltf
  944. );
  945. }
  946. return this._extensionsRequired;
  947. },
  948. },
  949. /**
  950. * Gets the model's up-axis.
  951. * By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up.
  952. *
  953. * @memberof Model.prototype
  954. *
  955. * @type {Number}
  956. * @default Axis.Y
  957. * @readonly
  958. *
  959. * @private
  960. */
  961. upAxis: {
  962. get: function () {
  963. return this._upAxis;
  964. },
  965. },
  966. /**
  967. * Gets the model's forward axis.
  968. * By default, glTF 2.0 models are z-forward according to the glTF spec, however older
  969. * glTF (1.0, 0.8) models used x-forward. Note that only Axis.X and Axis.Z are supported.
  970. *
  971. * @memberof Model.prototype
  972. *
  973. * @type {Number}
  974. * @default Axis.Z
  975. * @readonly
  976. *
  977. * @private
  978. */
  979. forwardAxis: {
  980. get: function () {
  981. if (defined(this._forwardAxis)) {
  982. return this._forwardAxis;
  983. }
  984. return this._gltfForwardAxis;
  985. },
  986. },
  987. /**
  988. * Gets the model's triangle count.
  989. *
  990. * @private
  991. */
  992. trianglesLength: {
  993. get: function () {
  994. return this._trianglesLength;
  995. },
  996. },
  997. /**
  998. * Gets the model's point count.
  999. *
  1000. * @private
  1001. */
  1002. pointsLength: {
  1003. get: function () {
  1004. return this._pointsLength;
  1005. },
  1006. },
  1007. /**
  1008. * Gets the model's geometry memory in bytes. This includes all vertex and index buffers.
  1009. *
  1010. * @private
  1011. */
  1012. geometryByteLength: {
  1013. get: function () {
  1014. return this._geometryByteLength;
  1015. },
  1016. },
  1017. /**
  1018. * Gets the model's texture memory in bytes.
  1019. *
  1020. * @private
  1021. */
  1022. texturesByteLength: {
  1023. get: function () {
  1024. return this._texturesByteLength;
  1025. },
  1026. },
  1027. /**
  1028. * Gets the model's cached geometry memory in bytes. This includes all vertex and index buffers.
  1029. *
  1030. * @private
  1031. */
  1032. cachedGeometryByteLength: {
  1033. get: function () {
  1034. return this._cachedGeometryByteLength;
  1035. },
  1036. },
  1037. /**
  1038. * Gets the model's cached texture memory in bytes.
  1039. *
  1040. * @private
  1041. */
  1042. cachedTexturesByteLength: {
  1043. get: function () {
  1044. return this._cachedTexturesByteLength;
  1045. },
  1046. },
  1047. /**
  1048. * The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
  1049. *
  1050. * @memberof Model.prototype
  1051. *
  1052. * @type {ClippingPlaneCollection}
  1053. */
  1054. clippingPlanes: {
  1055. get: function () {
  1056. return this._clippingPlanes;
  1057. },
  1058. set: function (value) {
  1059. if (value === this._clippingPlanes) {
  1060. return;
  1061. }
  1062. // Handle destroying, checking of unknown, checking for existing ownership
  1063. ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");
  1064. },
  1065. },
  1066. /**
  1067. * @private
  1068. */
  1069. pickIds: {
  1070. get: function () {
  1071. return this._pickIds;
  1072. },
  1073. },
  1074. /**
  1075. * The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
  1076. * <p>
  1077. * For example, disabling additional light sources by setting <code>model.imageBasedLightingFactor = new Cesium.Cartesian2(0.0, 0.0)</code> will make the
  1078. * model much darker. Here, increasing the intensity of the light source will make the model brighter.
  1079. * </p>
  1080. *
  1081. * @memberof Model.prototype
  1082. *
  1083. * @type {Cartesian3}
  1084. * @default undefined
  1085. */
  1086. lightColor: {
  1087. get: function () {
  1088. return this._lightColor;
  1089. },
  1090. set: function (value) {
  1091. const lightColor = this._lightColor;
  1092. if (value === lightColor || Cartesian3.equals(value, lightColor)) {
  1093. return;
  1094. }
  1095. this._shouldRegenerateShaders =
  1096. this._shouldRegenerateShaders ||
  1097. (defined(lightColor) && !defined(value)) ||
  1098. (defined(value) && !defined(lightColor));
  1099. this._lightColor = Cartesian3.clone(value, lightColor);
  1100. },
  1101. },
  1102. /**
  1103. * The properties for managing image-based lighting on this model.
  1104. *
  1105. * @memberof Model.prototype
  1106. *
  1107. * @type {ImageBasedLighting}
  1108. */
  1109. imageBasedLighting: {
  1110. get: function () {
  1111. return this._imageBasedLighting;
  1112. },
  1113. set: function (value) {
  1114. //>>includeStart('debug', pragmas.debug);
  1115. Check.typeOf.object("imageBasedLighting", this._imageBasedLighting);
  1116. //>>includeEnd('debug');
  1117. if (value !== this._imageBasedLighting) {
  1118. if (
  1119. this._shouldDestroyImageBasedLighting &&
  1120. !this._imageBasedLighting.isDestroyed()
  1121. ) {
  1122. this._imageBasedLighting.destroy();
  1123. }
  1124. this._imageBasedLighting = value;
  1125. this._shouldDestroyImageBasedLighting = false;
  1126. this._shouldRegenerateShaders = true;
  1127. }
  1128. },
  1129. },
  1130. /**
  1131. * Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final
  1132. * diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources.
  1133. *
  1134. * @memberof Model.prototype
  1135. *
  1136. * @type {Cartesian2}
  1137. * @default Cartesian2(1.0, 1.0)
  1138. */
  1139. imageBasedLightingFactor: {
  1140. get: function () {
  1141. return this._imageBasedLighting.imageBasedLightingFactor;
  1142. },
  1143. set: function (value) {
  1144. this._imageBasedLighting.imageBasedLightingFactor = value;
  1145. },
  1146. },
  1147. /**
  1148. * The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
  1149. * This is used when {@link Model#specularEnvironmentMaps} and {@link Model#sphericalHarmonicCoefficients} are not defined.
  1150. *
  1151. * @memberof Model.prototype
  1152. *
  1153. * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
  1154. * @type {Number}
  1155. * @default 0.2
  1156. */
  1157. luminanceAtZenith: {
  1158. get: function () {
  1159. return this._imageBasedLighting.luminanceAtZenith;
  1160. },
  1161. set: function (value) {
  1162. this._imageBasedLighting.luminanceAtZenith = value;
  1163. },
  1164. },
  1165. /**
  1166. * The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. When <code>undefined</code>, a diffuse irradiance
  1167. * computed from the atmosphere color is used.
  1168. * <p>
  1169. * There are nine <code>Cartesian3</code> coefficients.
  1170. * 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>
  1171. * </p>
  1172. *
  1173. * These values can be obtained by preprocessing the environment map using the <code>cmgen</code> tool of
  1174. * {@link https://github.com/google/filament/releases|Google's Filament project}. This will also generate a KTX file that can be
  1175. * supplied to {@link Model#specularEnvironmentMaps}.
  1176. *
  1177. * @memberof Model.prototype
  1178. *
  1179. * @type {Cartesian3[]}
  1180. * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
  1181. * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps}
  1182. */
  1183. sphericalHarmonicCoefficients: {
  1184. get: function () {
  1185. return this._imageBasedLighting.sphericalHarmonicCoefficients;
  1186. },
  1187. set: function (value) {
  1188. this._imageBasedLighting.sphericalHarmonicCoefficients = value;
  1189. },
  1190. },
  1191. /**
  1192. * A URL to a KTX2 file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
  1193. *
  1194. * @memberof Model.prototype
  1195. * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo}
  1196. * @type {String}
  1197. * @see Model#sphericalHarmonicCoefficients
  1198. */
  1199. specularEnvironmentMaps: {
  1200. get: function () {
  1201. return this._imageBasedLighting.specularEnvironmentMaps;
  1202. },
  1203. set: function (value) {
  1204. this._imageBasedLighting.specularEnvironmentMaps = value;
  1205. },
  1206. },
  1207. /**
  1208. * Gets the credit that will be displayed for the model
  1209. * @memberof Model.prototype
  1210. * @type {Credit}
  1211. */
  1212. credit: {
  1213. get: function () {
  1214. return this._credit;
  1215. },
  1216. },
  1217. /**
  1218. * Gets or sets whether the credits of the model will be displayed on the screen
  1219. * @memberof Model.prototype
  1220. * @type {Boolean}
  1221. */
  1222. showCreditsOnScreen: {
  1223. get: function () {
  1224. return this._showCreditsOnScreen;
  1225. },
  1226. set: function (value) {
  1227. if (this._showCreditsOnScreen !== value) {
  1228. if (defined(this._credit)) {
  1229. this._credit.showOnScreen = value;
  1230. }
  1231. const resourceCreditsLength = this._resourceCredits.length;
  1232. for (let i = 0; i < resourceCreditsLength; i++) {
  1233. this._resourceCredits[i].showOnScreen = value;
  1234. }
  1235. const gltfCreditsLength = this._gltfCredits.length;
  1236. for (let i = 0; i < gltfCreditsLength; i++) {
  1237. this._gltfCredits[i].showOnScreen = value;
  1238. }
  1239. }
  1240. this._showCreditsOnScreen = value;
  1241. },
  1242. },
  1243. });
  1244. function silhouetteSupported(context) {
  1245. return context.stencilBuffer;
  1246. }
  1247. function isColorShadingEnabled(model) {
  1248. return (
  1249. !Color.equals(model.color, Color.WHITE) ||
  1250. model.colorBlendMode !== ColorBlendMode.HIGHLIGHT
  1251. );
  1252. }
  1253. function isClippingEnabled(model) {
  1254. const clippingPlanes = model._clippingPlanes;
  1255. return (
  1256. defined(clippingPlanes) &&
  1257. clippingPlanes.enabled &&
  1258. clippingPlanes.length !== 0
  1259. );
  1260. }
  1261. /**
  1262. * Determines if silhouettes are supported.
  1263. *
  1264. * @param {Scene} scene The scene.
  1265. * @returns {Boolean} <code>true</code> if silhouettes are supported; otherwise, returns <code>false</code>
  1266. */
  1267. Model.silhouetteSupported = function (scene) {
  1268. return silhouetteSupported(scene.context);
  1269. };
  1270. function containsGltfMagic(uint8Array) {
  1271. const magic = getMagic(uint8Array);
  1272. return magic === "glTF";
  1273. }
  1274. /**
  1275. * <p>
  1276. * Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image,
  1277. * and shader files are downloaded and the WebGL resources are created, the {@link Model#readyPromise} is resolved.
  1278. * </p>
  1279. * <p>
  1280. * The model can be a traditional glTF asset with a .gltf extension or a Binary glTF using the .glb extension.
  1281. * </p>
  1282. * <p>
  1283. * Cesium supports glTF assets with the following extensions:
  1284. * <ul>
  1285. * <li>
  1286. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)}
  1287. * </li><li>
  1288. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)}
  1289. * </li><li>
  1290. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)}
  1291. * </li><li>
  1292. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations}
  1293. * </li><li>
  1294. * {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)}
  1295. * </li><li>
  1296. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression}
  1297. * </li><li>
  1298. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness}
  1299. * </li><li>
  1300. * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit}
  1301. * </li><li>
  1302. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl}
  1303. * </li><li>
  1304. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform}
  1305. * </li><li>
  1306. * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_basisu/README.md|KHR_texture_basisu}
  1307. * </li>
  1308. * </ul>
  1309. * </p>
  1310. * <p>
  1311. * 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
  1312. * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated
  1313. * relative to a local origin.
  1314. * </p>
  1315. *
  1316. * @param {Object} options Object with the following properties:
  1317. * @param {Resource|String} options.url The url to the .gltf file.
  1318. * @param {Resource|String} [options.basePath] The base path that paths in the glTF JSON are relative to.
  1319. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  1320. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  1321. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  1322. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  1323. * @param {Number} [options.maximumScale] The maximum scale for the model.
  1324. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
  1325. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
  1326. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
  1327. * @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.
  1328. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
  1329. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources.
  1330. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  1331. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  1332. * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain.
  1333. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property.
  1334. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed.
  1335. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color.
  1336. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
  1337. * @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.
  1338. * @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.
  1339. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels.
  1340. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
  1341. * @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.
  1342. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
  1343. * @param {ImageBasedLighting} [options.imageBasedLighting] The properties for managing image-based lighting for this tileset.
  1344. * @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.
  1345. * @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.
  1346. * @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.
  1347. * @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.
  1348. * @param {Credit|String} [options.credit] A credit for the model, which is displayed on the canvas.
  1349. * @param {Boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen.
  1350. * @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.
  1351. * @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.
  1352. * @returns {Model} The newly created model.
  1353. *
  1354. * @example
  1355. * // Example 1. Create a model from a glTF asset
  1356. * const model = scene.primitives.add(Cesium.Model.fromGltf({
  1357. * url : './duck/duck.gltf'
  1358. * }));
  1359. *
  1360. * @example
  1361. * // Example 2. Create model and provide all properties and events
  1362. * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  1363. * const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  1364. *
  1365. * const model = scene.primitives.add(Cesium.Model.fromGltf({
  1366. * url : './duck/duck.gltf',
  1367. * show : true, // default
  1368. * modelMatrix : modelMatrix,
  1369. * scale : 2.0, // double size
  1370. * minimumPixelSize : 128, // never smaller than 128 pixels
  1371. * maximumScale: 20000, // never larger than 20000 * model size (overrides minimumPixelSize)
  1372. * allowPicking : false, // not pickable
  1373. * debugShowBoundingVolume : false, // default
  1374. * debugWireframe : false
  1375. * }));
  1376. *
  1377. * model.readyPromise.then(function(model) {
  1378. * // Play all animations when the model is ready to render
  1379. * model.activeAnimations.addAll();
  1380. * });
  1381. */
  1382. Model.fromGltf = function (options) {
  1383. //>>includeStart('debug', pragmas.debug);
  1384. if (!defined(options) || !defined(options.url)) {
  1385. throw new DeveloperError("options.url is required");
  1386. }
  1387. //>>includeEnd('debug');
  1388. const url = options.url;
  1389. options = clone(options);
  1390. // Create resource for the model file
  1391. const modelResource = Resource.createIfNeeded(url);
  1392. // Setup basePath to get dependent files
  1393. const basePath = defaultValue(options.basePath, modelResource.clone());
  1394. const resource = Resource.createIfNeeded(basePath);
  1395. // If no cache key is provided, use a GUID.
  1396. // Check using a URI to GUID dictionary that we have not already added this model.
  1397. let cacheKey = defaultValue(
  1398. options.cacheKey,
  1399. uriToGuid[getAbsoluteUri(modelResource.url)]
  1400. );
  1401. if (!defined(cacheKey)) {
  1402. cacheKey = createGuid();
  1403. uriToGuid[getAbsoluteUri(modelResource.url)] = cacheKey;
  1404. }
  1405. if (defined(options.basePath) && !defined(options.cacheKey)) {
  1406. cacheKey += resource.url;
  1407. }
  1408. options.cacheKey = cacheKey;
  1409. options.basePath = resource;
  1410. const model = new Model(options);
  1411. let cachedGltf = gltfCache[cacheKey];
  1412. if (!defined(cachedGltf)) {
  1413. cachedGltf = new CachedGltf({
  1414. ready: false,
  1415. });
  1416. cachedGltf.count = 1;
  1417. cachedGltf.modelsToLoad.push(model);
  1418. setCachedGltf(model, cachedGltf);
  1419. gltfCache[cacheKey] = cachedGltf;
  1420. // Add Accept header if we need it
  1421. if (!defined(modelResource.headers.Accept)) {
  1422. modelResource.headers.Accept = defaultModelAccept;
  1423. }
  1424. modelResource
  1425. .fetchArrayBuffer()
  1426. .then(function (arrayBuffer) {
  1427. const array = new Uint8Array(arrayBuffer);
  1428. if (containsGltfMagic(array)) {
  1429. // Load binary glTF
  1430. const parsedGltf = parseGlb(array);
  1431. cachedGltf.makeReady(parsedGltf);
  1432. } else {
  1433. // Load text (JSON) glTF
  1434. const json = getJsonFromTypedArray(array);
  1435. cachedGltf.makeReady(json);
  1436. }
  1437. const resourceCredits = model._resourceCredits;
  1438. const credits = modelResource.credits;
  1439. if (defined(credits)) {
  1440. const length = credits.length;
  1441. for (let i = 0; i < length; i++) {
  1442. resourceCredits.push(credits[i]);
  1443. }
  1444. }
  1445. })
  1446. .catch(
  1447. ModelUtility.getFailedLoadFunction(model, "model", modelResource.url)
  1448. );
  1449. } else if (!cachedGltf.ready) {
  1450. // Cache hit but the fetchArrayBuffer() or fetchText() request is still pending
  1451. ++cachedGltf.count;
  1452. cachedGltf.modelsToLoad.push(model);
  1453. }
  1454. // else if the cached glTF is defined and ready, the
  1455. // model constructor will pick it up using the cache key.
  1456. return model;
  1457. };
  1458. /**
  1459. * For the unit tests to verify model caching.
  1460. *
  1461. * @private
  1462. */
  1463. Model._gltfCache = gltfCache;
  1464. function getRuntime(model, runtimeName, name) {
  1465. //>>includeStart('debug', pragmas.debug);
  1466. if (model._state !== ModelState.LOADED) {
  1467. throw new DeveloperError(
  1468. "The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true."
  1469. );
  1470. }
  1471. if (!defined(name)) {
  1472. throw new DeveloperError("name is required.");
  1473. }
  1474. //>>includeEnd('debug');
  1475. return model._runtime[runtimeName][name];
  1476. }
  1477. /**
  1478. * Returns the glTF node with the given <code>name</code> property. This is used to
  1479. * modify a node's transform for animation outside of glTF animations.
  1480. *
  1481. * @param {String} name The glTF name of the node.
  1482. * @returns {ModelNode} The node or <code>undefined</code> if no node with <code>name</code> exists.
  1483. *
  1484. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1485. *
  1486. * @example
  1487. * // Apply non-uniform scale to node LOD3sp
  1488. * const node = model.getNode('LOD3sp');
  1489. * node.matrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix);
  1490. */
  1491. Model.prototype.getNode = function (name) {
  1492. const node = getRuntime(this, "nodesByName", name);
  1493. return defined(node) ? node.publicNode : undefined;
  1494. };
  1495. /**
  1496. * Returns the glTF mesh with the given <code>name</code> property.
  1497. *
  1498. * @param {String} name The glTF name of the mesh.
  1499. *
  1500. * @returns {ModelMesh} The mesh or <code>undefined</code> if no mesh with <code>name</code> exists.
  1501. *
  1502. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1503. */
  1504. Model.prototype.getMesh = function (name) {
  1505. return getRuntime(this, "meshesByName", name);
  1506. };
  1507. /**
  1508. * Returns the glTF material with the given <code>name</code> property.
  1509. *
  1510. * @param {String} name The glTF name of the material.
  1511. * @returns {ModelMaterial} The material or <code>undefined</code> if no material with <code>name</code> exists.
  1512. *
  1513. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1514. */
  1515. Model.prototype.getMaterial = function (name) {
  1516. return getRuntime(this, "materialsByName", name);
  1517. };
  1518. /**
  1519. * Sets the current value of an articulation stage. After setting one or multiple stage values, call
  1520. * Model.applyArticulations() to cause the node matrices to be recalculated.
  1521. *
  1522. * @param {String} articulationStageKey The name of the articulation, a space, and the name of the stage.
  1523. * @param {Number} value The numeric value of this stage of the articulation.
  1524. *
  1525. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1526. *
  1527. * @see Model#applyArticulations
  1528. */
  1529. Model.prototype.setArticulationStage = function (articulationStageKey, value) {
  1530. //>>includeStart('debug', pragmas.debug);
  1531. Check.typeOf.number("value", value);
  1532. //>>includeEnd('debug');
  1533. const stage = getRuntime(this, "stagesByKey", articulationStageKey);
  1534. const articulation = getRuntime(
  1535. this,
  1536. "articulationsByStageKey",
  1537. articulationStageKey
  1538. );
  1539. if (defined(stage) && defined(articulation)) {
  1540. value = CesiumMath.clamp(value, stage.minimumValue, stage.maximumValue);
  1541. if (
  1542. !CesiumMath.equalsEpsilon(stage.currentValue, value, articulationEpsilon)
  1543. ) {
  1544. stage.currentValue = value;
  1545. articulation.isDirty = true;
  1546. }
  1547. }
  1548. };
  1549. const scratchArticulationCartesian = new Cartesian3();
  1550. const scratchArticulationRotation = new Matrix3();
  1551. /**
  1552. * Modifies a Matrix4 by applying a transformation for a given value of a stage. Note this is different usage
  1553. * from the typical <code>result</code> parameter, in that the incoming value of <code>result</code> is
  1554. * meaningful. Various stages of an articulation can be multiplied together, so their
  1555. * transformations are all merged into a composite Matrix4 representing them all.
  1556. *
  1557. * @param {object} stage The stage of an articulation that is being evaluated.
  1558. * @param {Matrix4} result The matrix to be modified.
  1559. * @returns {Matrix4} A matrix transformed as requested by the articulation stage.
  1560. *
  1561. * @private
  1562. */
  1563. function applyArticulationStageMatrix(stage, result) {
  1564. //>>includeStart('debug', pragmas.debug);
  1565. Check.typeOf.object("stage", stage);
  1566. Check.typeOf.object("result", result);
  1567. //>>includeEnd('debug');
  1568. const value = stage.currentValue;
  1569. const cartesian = scratchArticulationCartesian;
  1570. let rotation;
  1571. switch (stage.type) {
  1572. case "xRotate":
  1573. rotation = Matrix3.fromRotationX(
  1574. CesiumMath.toRadians(value),
  1575. scratchArticulationRotation
  1576. );
  1577. Matrix4.multiplyByMatrix3(result, rotation, result);
  1578. break;
  1579. case "yRotate":
  1580. rotation = Matrix3.fromRotationY(
  1581. CesiumMath.toRadians(value),
  1582. scratchArticulationRotation
  1583. );
  1584. Matrix4.multiplyByMatrix3(result, rotation, result);
  1585. break;
  1586. case "zRotate":
  1587. rotation = Matrix3.fromRotationZ(
  1588. CesiumMath.toRadians(value),
  1589. scratchArticulationRotation
  1590. );
  1591. Matrix4.multiplyByMatrix3(result, rotation, result);
  1592. break;
  1593. case "xTranslate":
  1594. cartesian.x = value;
  1595. cartesian.y = 0.0;
  1596. cartesian.z = 0.0;
  1597. Matrix4.multiplyByTranslation(result, cartesian, result);
  1598. break;
  1599. case "yTranslate":
  1600. cartesian.x = 0.0;
  1601. cartesian.y = value;
  1602. cartesian.z = 0.0;
  1603. Matrix4.multiplyByTranslation(result, cartesian, result);
  1604. break;
  1605. case "zTranslate":
  1606. cartesian.x = 0.0;
  1607. cartesian.y = 0.0;
  1608. cartesian.z = value;
  1609. Matrix4.multiplyByTranslation(result, cartesian, result);
  1610. break;
  1611. case "xScale":
  1612. cartesian.x = value;
  1613. cartesian.y = 1.0;
  1614. cartesian.z = 1.0;
  1615. Matrix4.multiplyByScale(result, cartesian, result);
  1616. break;
  1617. case "yScale":
  1618. cartesian.x = 1.0;
  1619. cartesian.y = value;
  1620. cartesian.z = 1.0;
  1621. Matrix4.multiplyByScale(result, cartesian, result);
  1622. break;
  1623. case "zScale":
  1624. cartesian.x = 1.0;
  1625. cartesian.y = 1.0;
  1626. cartesian.z = value;
  1627. Matrix4.multiplyByScale(result, cartesian, result);
  1628. break;
  1629. case "uniformScale":
  1630. Matrix4.multiplyByUniformScale(result, value, result);
  1631. break;
  1632. default:
  1633. break;
  1634. }
  1635. return result;
  1636. }
  1637. const scratchApplyArticulationTransform = new Matrix4();
  1638. /**
  1639. * Applies any modified articulation stages to the matrix of each node that participates
  1640. * in any articulation. Note that this will overwrite any nodeTransformations on participating nodes.
  1641. *
  1642. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1643. */
  1644. Model.prototype.applyArticulations = function () {
  1645. const articulationsByName = this._runtime.articulationsByName;
  1646. for (const articulationName in articulationsByName) {
  1647. if (articulationsByName.hasOwnProperty(articulationName)) {
  1648. const articulation = articulationsByName[articulationName];
  1649. if (articulation.isDirty) {
  1650. articulation.isDirty = false;
  1651. const numNodes = articulation.nodes.length;
  1652. for (let n = 0; n < numNodes; ++n) {
  1653. const node = articulation.nodes[n];
  1654. let transform = Matrix4.clone(
  1655. node.originalMatrix,
  1656. scratchApplyArticulationTransform
  1657. );
  1658. const numStages = articulation.stages.length;
  1659. for (let s = 0; s < numStages; ++s) {
  1660. const stage = articulation.stages[s];
  1661. transform = applyArticulationStageMatrix(stage, transform);
  1662. }
  1663. node.matrix = transform;
  1664. }
  1665. }
  1666. }
  1667. }
  1668. };
  1669. ///////////////////////////////////////////////////////////////////////////
  1670. function addBuffersToLoadResources(model) {
  1671. const gltf = model.gltf;
  1672. const loadResources = model._loadResources;
  1673. ForEach.buffer(gltf, function (buffer, id) {
  1674. loadResources.buffers[id] = buffer.extras._pipeline.source;
  1675. });
  1676. }
  1677. function bufferLoad(model, id) {
  1678. return function (arrayBuffer) {
  1679. const loadResources = model._loadResources;
  1680. const buffer = new Uint8Array(arrayBuffer);
  1681. --loadResources.pendingBufferLoads;
  1682. model.gltf.buffers[id].extras._pipeline.source = buffer;
  1683. };
  1684. }
  1685. function parseBufferViews(model) {
  1686. const bufferViews = model.gltf.bufferViews;
  1687. const vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate;
  1688. // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below.
  1689. ForEach.bufferView(model.gltf, function (bufferView, id) {
  1690. if (bufferView.target === WebGLConstants.ARRAY_BUFFER) {
  1691. vertexBuffersToCreate.enqueue(id);
  1692. }
  1693. });
  1694. const indexBuffersToCreate = model._loadResources.indexBuffersToCreate;
  1695. const indexBufferIds = {};
  1696. // The Cesium Renderer requires knowing the datatype for an index buffer
  1697. // at creation type, which is not part of the glTF bufferview so loop
  1698. // through glTF accessors to create the bufferview's index buffer.
  1699. ForEach.accessor(model.gltf, function (accessor) {
  1700. const bufferViewId = accessor.bufferView;
  1701. if (!defined(bufferViewId)) {
  1702. return;
  1703. }
  1704. const bufferView = bufferViews[bufferViewId];
  1705. if (
  1706. bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER &&
  1707. !defined(indexBufferIds[bufferViewId])
  1708. ) {
  1709. indexBufferIds[bufferViewId] = true;
  1710. indexBuffersToCreate.enqueue({
  1711. id: bufferViewId,
  1712. componentType: accessor.componentType,
  1713. });
  1714. }
  1715. });
  1716. }
  1717. function parseTechniques(model) {
  1718. // retain references to gltf techniques
  1719. const gltf = model.gltf;
  1720. if (!usesExtension(gltf, "KHR_techniques_webgl")) {
  1721. return;
  1722. }
  1723. const sourcePrograms = model._sourcePrograms;
  1724. const sourceTechniques = model._sourceTechniques;
  1725. const programs = gltf.extensions.KHR_techniques_webgl.programs;
  1726. ForEach.technique(gltf, function (technique, techniqueId) {
  1727. sourceTechniques[techniqueId] = clone(technique);
  1728. const programId = technique.program;
  1729. if (!defined(sourcePrograms[programId])) {
  1730. sourcePrograms[programId] = clone(programs[programId]);
  1731. }
  1732. });
  1733. }
  1734. function shaderLoad(model, type, id) {
  1735. return function (source) {
  1736. const loadResources = model._loadResources;
  1737. loadResources.shaders[id] = {
  1738. source: source,
  1739. type: type,
  1740. bufferView: undefined,
  1741. };
  1742. --loadResources.pendingShaderLoads;
  1743. model._rendererResources.sourceShaders[id] = source;
  1744. };
  1745. }
  1746. function parseShaders(model) {
  1747. const gltf = model.gltf;
  1748. const buffers = gltf.buffers;
  1749. const bufferViews = gltf.bufferViews;
  1750. const sourceShaders = model._rendererResources.sourceShaders;
  1751. ForEach.shader(gltf, function (shader, id) {
  1752. // Shader references either uri (external or base64-encoded) or bufferView
  1753. if (defined(shader.bufferView)) {
  1754. const bufferViewId = shader.bufferView;
  1755. const bufferView = bufferViews[bufferViewId];
  1756. const bufferId = bufferView.buffer;
  1757. const buffer = buffers[bufferId];
  1758. const source = getStringFromTypedArray(
  1759. buffer.extras._pipeline.source,
  1760. bufferView.byteOffset,
  1761. bufferView.byteLength
  1762. );
  1763. sourceShaders[id] = source;
  1764. } else if (defined(shader.extras._pipeline.source)) {
  1765. sourceShaders[id] = shader.extras._pipeline.source;
  1766. } else {
  1767. ++model._loadResources.pendingShaderLoads;
  1768. const shaderResource = model._resource.getDerivedResource({
  1769. url: shader.uri,
  1770. });
  1771. shaderResource
  1772. .fetchText()
  1773. .then(shaderLoad(model, shader.type, id))
  1774. .catch(
  1775. ModelUtility.getFailedLoadFunction(
  1776. model,
  1777. "shader",
  1778. shaderResource.url
  1779. )
  1780. );
  1781. }
  1782. });
  1783. }
  1784. function parsePrograms(model) {
  1785. const sourceTechniques = model._sourceTechniques;
  1786. for (const techniqueId in sourceTechniques) {
  1787. if (sourceTechniques.hasOwnProperty(techniqueId)) {
  1788. const technique = sourceTechniques[techniqueId];
  1789. model._loadResources.programsToCreate.enqueue({
  1790. programId: technique.program,
  1791. techniqueId: techniqueId,
  1792. });
  1793. }
  1794. }
  1795. }
  1796. function parseArticulations(model) {
  1797. const articulationsByName = {};
  1798. const articulationsByStageKey = {};
  1799. const runtimeStagesByKey = {};
  1800. model._runtime.articulationsByName = articulationsByName;
  1801. model._runtime.articulationsByStageKey = articulationsByStageKey;
  1802. model._runtime.stagesByKey = runtimeStagesByKey;
  1803. const gltf = model.gltf;
  1804. if (
  1805. !usesExtension(gltf, "AGI_articulations") ||
  1806. !defined(gltf.extensions) ||
  1807. !defined(gltf.extensions.AGI_articulations)
  1808. ) {
  1809. return;
  1810. }
  1811. const gltfArticulations = gltf.extensions.AGI_articulations.articulations;
  1812. if (!defined(gltfArticulations)) {
  1813. return;
  1814. }
  1815. const numArticulations = gltfArticulations.length;
  1816. for (let i = 0; i < numArticulations; ++i) {
  1817. const articulation = clone(gltfArticulations[i]);
  1818. articulation.nodes = [];
  1819. articulation.isDirty = true;
  1820. articulationsByName[articulation.name] = articulation;
  1821. const numStages = articulation.stages.length;
  1822. for (let s = 0; s < numStages; ++s) {
  1823. const stage = articulation.stages[s];
  1824. stage.currentValue = stage.initialValue;
  1825. const stageKey = `${articulation.name} ${stage.name}`;
  1826. articulationsByStageKey[stageKey] = articulation;
  1827. runtimeStagesByKey[stageKey] = stage;
  1828. }
  1829. }
  1830. }
  1831. function imageLoad(model, textureId) {
  1832. return function (image) {
  1833. const loadResources = model._loadResources;
  1834. --loadResources.pendingTextureLoads;
  1835. // Images transcoded from KTX2 can contain multiple mip levels:
  1836. // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
  1837. let mipLevels;
  1838. if (Array.isArray(image)) {
  1839. // highest detail mip should be level 0
  1840. mipLevels = image.slice(1, image.length).map(function (mipLevel) {
  1841. return mipLevel.bufferView;
  1842. });
  1843. image = image[0];
  1844. }
  1845. loadResources.texturesToCreate.enqueue({
  1846. id: textureId,
  1847. image: image,
  1848. bufferView: image.bufferView,
  1849. width: image.width,
  1850. height: image.height,
  1851. internalFormat: image.internalFormat,
  1852. mipLevels: mipLevels,
  1853. });
  1854. };
  1855. }
  1856. const ktx2Regex = /(^data:image\/ktx2)|(\.ktx2$)/i;
  1857. function parseTextures(model, context, supportsWebP) {
  1858. const gltf = model.gltf;
  1859. const images = gltf.images;
  1860. let uri;
  1861. ForEach.texture(gltf, function (texture, id) {
  1862. let imageId = texture.source;
  1863. if (
  1864. defined(texture.extensions) &&
  1865. defined(texture.extensions.EXT_texture_webp) &&
  1866. supportsWebP
  1867. ) {
  1868. imageId = texture.extensions.EXT_texture_webp.source;
  1869. } else if (
  1870. defined(texture.extensions) &&
  1871. defined(texture.extensions.KHR_texture_basisu) &&
  1872. context.supportsBasis
  1873. ) {
  1874. imageId = texture.extensions.KHR_texture_basisu.source;
  1875. }
  1876. const gltfImage = images[imageId];
  1877. const bufferViewId = gltfImage.bufferView;
  1878. const mimeType = gltfImage.mimeType;
  1879. uri = gltfImage.uri;
  1880. // Image references either uri (external or base64-encoded) or bufferView
  1881. if (defined(bufferViewId)) {
  1882. model._loadResources.texturesToCreateFromBufferView.enqueue({
  1883. id: id,
  1884. image: undefined,
  1885. bufferView: bufferViewId,
  1886. mimeType: mimeType,
  1887. });
  1888. } else {
  1889. ++model._loadResources.pendingTextureLoads;
  1890. const imageResource = model._resource.getDerivedResource({
  1891. url: uri,
  1892. });
  1893. let promise;
  1894. if (ktx2Regex.test(uri)) {
  1895. promise = loadKTX2(imageResource);
  1896. } else {
  1897. promise = imageResource.fetchImage({
  1898. skipColorSpaceConversion: true,
  1899. preferImageBitmap: true,
  1900. });
  1901. }
  1902. promise
  1903. .then(imageLoad(model, id, imageId))
  1904. .catch(
  1905. ModelUtility.getFailedLoadFunction(model, "image", imageResource.url)
  1906. );
  1907. }
  1908. });
  1909. }
  1910. const scratchArticulationStageInitialTransform = new Matrix4();
  1911. function parseNodes(model) {
  1912. const runtimeNodes = {};
  1913. const runtimeNodesByName = {};
  1914. const skinnedNodes = [];
  1915. const skinnedNodesIds = model._loadResources.skinnedNodesIds;
  1916. const articulationsByName = model._runtime.articulationsByName;
  1917. ForEach.node(model.gltf, function (node, id) {
  1918. const runtimeNode = {
  1919. // Animation targets
  1920. matrix: undefined,
  1921. translation: undefined,
  1922. rotation: undefined,
  1923. scale: undefined,
  1924. // Per-node show inherited from parent
  1925. computedShow: true,
  1926. // Computed transforms
  1927. transformToRoot: new Matrix4(),
  1928. computedMatrix: new Matrix4(),
  1929. dirtyNumber: 0, // The frame this node was made dirty by an animation; for graph traversal
  1930. // Rendering
  1931. commands: [], // empty for transform, light, and camera nodes
  1932. // Skinned node
  1933. inverseBindMatrices: undefined, // undefined when node is not skinned
  1934. bindShapeMatrix: undefined, // undefined when node is not skinned or identity
  1935. joints: [], // empty when node is not skinned
  1936. computedJointMatrices: [], // empty when node is not skinned
  1937. // Joint node
  1938. jointName: node.jointName, // undefined when node is not a joint
  1939. weights: [],
  1940. // Graph pointers
  1941. children: [], // empty for leaf nodes
  1942. parents: [], // empty for root nodes
  1943. // Publicly-accessible ModelNode instance to modify animation targets
  1944. publicNode: undefined,
  1945. };
  1946. runtimeNode.publicNode = new ModelNode(
  1947. model,
  1948. node,
  1949. runtimeNode,
  1950. id,
  1951. ModelUtility.getTransform(node)
  1952. );
  1953. runtimeNodes[id] = runtimeNode;
  1954. runtimeNodesByName[node.name] = runtimeNode;
  1955. if (defined(node.skin)) {
  1956. skinnedNodesIds.push(id);
  1957. skinnedNodes.push(runtimeNode);
  1958. }
  1959. if (
  1960. defined(node.extensions) &&
  1961. defined(node.extensions.AGI_articulations)
  1962. ) {
  1963. const articulationName =
  1964. node.extensions.AGI_articulations.articulationName;
  1965. if (defined(articulationName)) {
  1966. let transform = Matrix4.clone(
  1967. runtimeNode.publicNode.originalMatrix,
  1968. scratchArticulationStageInitialTransform
  1969. );
  1970. const articulation = articulationsByName[articulationName];
  1971. articulation.nodes.push(runtimeNode.publicNode);
  1972. const numStages = articulation.stages.length;
  1973. for (let s = 0; s < numStages; ++s) {
  1974. const stage = articulation.stages[s];
  1975. transform = applyArticulationStageMatrix(stage, transform);
  1976. }
  1977. runtimeNode.publicNode.matrix = transform;
  1978. }
  1979. }
  1980. });
  1981. model._runtime.nodes = runtimeNodes;
  1982. model._runtime.nodesByName = runtimeNodesByName;
  1983. model._runtime.skinnedNodes = skinnedNodes;
  1984. }
  1985. function parseMaterials(model) {
  1986. const gltf = model.gltf;
  1987. const techniques = model._sourceTechniques;
  1988. const runtimeMaterialsByName = {};
  1989. const runtimeMaterialsById = {};
  1990. const uniformMaps = model._uniformMaps;
  1991. ForEach.material(gltf, function (material, materialId) {
  1992. // Allocated now so ModelMaterial can keep a reference to it.
  1993. uniformMaps[materialId] = {
  1994. uniformMap: undefined,
  1995. values: undefined,
  1996. jointMatrixUniformName: undefined,
  1997. morphWeightsUniformName: undefined,
  1998. };
  1999. const modelMaterial = new ModelMaterial(model, material, materialId);
  2000. if (
  2001. defined(material.extensions) &&
  2002. defined(material.extensions.KHR_techniques_webgl)
  2003. ) {
  2004. const techniqueId = material.extensions.KHR_techniques_webgl.technique;
  2005. modelMaterial._technique = techniqueId;
  2006. modelMaterial._program = techniques[techniqueId].program;
  2007. ForEach.materialValue(material, function (value, uniformName) {
  2008. if (!defined(modelMaterial._values)) {
  2009. modelMaterial._values = {};
  2010. }
  2011. modelMaterial._values[uniformName] = clone(value);
  2012. });
  2013. }
  2014. runtimeMaterialsByName[material.name] = modelMaterial;
  2015. runtimeMaterialsById[materialId] = modelMaterial;
  2016. });
  2017. model._runtime.materialsByName = runtimeMaterialsByName;
  2018. model._runtime.materialsById = runtimeMaterialsById;
  2019. }
  2020. function parseMeshes(model) {
  2021. const runtimeMeshesByName = {};
  2022. const runtimeMaterialsById = model._runtime.materialsById;
  2023. ForEach.mesh(model.gltf, function (mesh, meshId) {
  2024. runtimeMeshesByName[mesh.name] = new ModelMesh(
  2025. mesh,
  2026. runtimeMaterialsById,
  2027. meshId
  2028. );
  2029. if (
  2030. defined(model.extensionsUsed.WEB3D_quantized_attributes) ||
  2031. model._dequantizeInShader
  2032. ) {
  2033. // Cache primitives according to their program
  2034. ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {
  2035. const programId = getProgramForPrimitive(model, primitive);
  2036. let programPrimitives = model._programPrimitives[programId];
  2037. if (!defined(programPrimitives)) {
  2038. programPrimitives = {};
  2039. model._programPrimitives[programId] = programPrimitives;
  2040. }
  2041. programPrimitives[`${meshId}.primitive.${primitiveId}`] = primitive;
  2042. });
  2043. }
  2044. });
  2045. model._runtime.meshesByName = runtimeMeshesByName;
  2046. }
  2047. function parseCredits(model) {
  2048. const asset = model.gltf.asset;
  2049. const copyright = asset.copyright;
  2050. if (!defined(copyright)) {
  2051. return;
  2052. }
  2053. const showOnScreen = model._showCreditsOnScreen;
  2054. const credits = copyright.split(";").map(function (string) {
  2055. return new Credit(string.trim(), showOnScreen);
  2056. });
  2057. model._gltfCredits = credits;
  2058. }
  2059. ///////////////////////////////////////////////////////////////////////////
  2060. const CreateVertexBufferJob = function () {
  2061. this.id = undefined;
  2062. this.model = undefined;
  2063. this.context = undefined;
  2064. };
  2065. CreateVertexBufferJob.prototype.set = function (id, model, context) {
  2066. this.id = id;
  2067. this.model = model;
  2068. this.context = context;
  2069. };
  2070. CreateVertexBufferJob.prototype.execute = function () {
  2071. createVertexBuffer(this.id, this.model, this.context);
  2072. };
  2073. ///////////////////////////////////////////////////////////////////////////
  2074. function createVertexBuffer(bufferViewId, model, context) {
  2075. const loadResources = model._loadResources;
  2076. const bufferViews = model.gltf.bufferViews;
  2077. let bufferView = bufferViews[bufferViewId];
  2078. // Use bufferView created at runtime
  2079. if (!defined(bufferView)) {
  2080. bufferView = loadResources.createdBufferViews[bufferViewId];
  2081. }
  2082. const vertexBuffer = Buffer.createVertexBuffer({
  2083. context: context,
  2084. typedArray: loadResources.getBuffer(bufferView),
  2085. usage: BufferUsage.STATIC_DRAW,
  2086. });
  2087. vertexBuffer.vertexArrayDestroyable = false;
  2088. model._rendererResources.buffers[bufferViewId] = vertexBuffer;
  2089. model._geometryByteLength += vertexBuffer.sizeInBytes;
  2090. }
  2091. ///////////////////////////////////////////////////////////////////////////
  2092. const CreateIndexBufferJob = function () {
  2093. this.id = undefined;
  2094. this.componentType = undefined;
  2095. this.model = undefined;
  2096. this.context = undefined;
  2097. };
  2098. CreateIndexBufferJob.prototype.set = function (
  2099. id,
  2100. componentType,
  2101. model,
  2102. context
  2103. ) {
  2104. this.id = id;
  2105. this.componentType = componentType;
  2106. this.model = model;
  2107. this.context = context;
  2108. };
  2109. CreateIndexBufferJob.prototype.execute = function () {
  2110. createIndexBuffer(this.id, this.componentType, this.model, this.context);
  2111. };
  2112. ///////////////////////////////////////////////////////////////////////////
  2113. function createIndexBuffer(bufferViewId, componentType, model, context) {
  2114. const loadResources = model._loadResources;
  2115. const bufferViews = model.gltf.bufferViews;
  2116. let bufferView = bufferViews[bufferViewId];
  2117. // Use bufferView created at runtime
  2118. if (!defined(bufferView)) {
  2119. bufferView = loadResources.createdBufferViews[bufferViewId];
  2120. }
  2121. const indexBuffer = Buffer.createIndexBuffer({
  2122. context: context,
  2123. typedArray: loadResources.getBuffer(bufferView),
  2124. usage: BufferUsage.STATIC_DRAW,
  2125. indexDatatype: componentType,
  2126. });
  2127. indexBuffer.vertexArrayDestroyable = false;
  2128. model._rendererResources.buffers[bufferViewId] = indexBuffer;
  2129. model._geometryByteLength += indexBuffer.sizeInBytes;
  2130. }
  2131. const scratchVertexBufferJob = new CreateVertexBufferJob();
  2132. const scratchIndexBufferJob = new CreateIndexBufferJob();
  2133. function createBuffers(model, frameState) {
  2134. const loadResources = model._loadResources;
  2135. if (loadResources.pendingBufferLoads !== 0) {
  2136. return;
  2137. }
  2138. const context = frameState.context;
  2139. const vertexBuffersToCreate = loadResources.vertexBuffersToCreate;
  2140. const indexBuffersToCreate = loadResources.indexBuffersToCreate;
  2141. let i;
  2142. if (model.asynchronous) {
  2143. while (vertexBuffersToCreate.length > 0) {
  2144. scratchVertexBufferJob.set(vertexBuffersToCreate.peek(), model, context);
  2145. if (
  2146. !frameState.jobScheduler.execute(scratchVertexBufferJob, JobType.BUFFER)
  2147. ) {
  2148. break;
  2149. }
  2150. vertexBuffersToCreate.dequeue();
  2151. }
  2152. while (indexBuffersToCreate.length > 0) {
  2153. i = indexBuffersToCreate.peek();
  2154. scratchIndexBufferJob.set(i.id, i.componentType, model, context);
  2155. if (
  2156. !frameState.jobScheduler.execute(scratchIndexBufferJob, JobType.BUFFER)
  2157. ) {
  2158. break;
  2159. }
  2160. indexBuffersToCreate.dequeue();
  2161. }
  2162. } else {
  2163. while (vertexBuffersToCreate.length > 0) {
  2164. createVertexBuffer(vertexBuffersToCreate.dequeue(), model, context);
  2165. }
  2166. while (indexBuffersToCreate.length > 0) {
  2167. i = indexBuffersToCreate.dequeue();
  2168. createIndexBuffer(i.id, i.componentType, model, context);
  2169. }
  2170. }
  2171. }
  2172. function getProgramForPrimitive(model, primitive) {
  2173. const material = model._runtime.materialsById[primitive.material];
  2174. if (!defined(material)) {
  2175. return;
  2176. }
  2177. return material._program;
  2178. }
  2179. function modifyShaderForQuantizedAttributes(shader, programName, model) {
  2180. let primitive;
  2181. const primitives = model._programPrimitives[programName];
  2182. // If no primitives were cached for this program, there's no need to modify the shader
  2183. if (!defined(primitives)) {
  2184. return shader;
  2185. }
  2186. let primitiveId;
  2187. for (primitiveId in primitives) {
  2188. if (primitives.hasOwnProperty(primitiveId)) {
  2189. primitive = primitives[primitiveId];
  2190. if (getProgramForPrimitive(model, primitive) === programName) {
  2191. break;
  2192. }
  2193. }
  2194. }
  2195. // This is not needed after the program is processed, free the memory
  2196. model._programPrimitives[programName] = undefined;
  2197. let result;
  2198. if (model.extensionsUsed.WEB3D_quantized_attributes) {
  2199. result = ModelUtility.modifyShaderForQuantizedAttributes(
  2200. model.gltf,
  2201. primitive,
  2202. shader
  2203. );
  2204. model._quantizedUniforms[programName] = result.uniforms;
  2205. } else {
  2206. const decodedData = model._decodedData[primitiveId];
  2207. if (defined(decodedData)) {
  2208. result = ModelUtility.modifyShaderForDracoQuantizedAttributes(
  2209. model.gltf,
  2210. primitive,
  2211. shader,
  2212. decodedData.attributes
  2213. );
  2214. } else {
  2215. return shader;
  2216. }
  2217. }
  2218. return result.shader;
  2219. }
  2220. function modifyShaderForColor(shader) {
  2221. shader = ShaderSource.replaceMain(shader, "gltf_blend_main");
  2222. shader +=
  2223. "uniform vec4 gltf_color; \n" +
  2224. "uniform float gltf_colorBlend; \n" +
  2225. "void main() \n" +
  2226. "{ \n" +
  2227. " gltf_blend_main(); \n" +
  2228. " gl_FragColor.rgb = mix(gl_FragColor.rgb, gltf_color.rgb, gltf_colorBlend); \n" +
  2229. " float highlight = ceil(gltf_colorBlend); \n" +
  2230. " gl_FragColor.rgb *= mix(gltf_color.rgb, vec3(1.0), highlight); \n" +
  2231. " gl_FragColor.a *= gltf_color.a; \n" +
  2232. "} \n";
  2233. return shader;
  2234. }
  2235. function modifyShader(shader, programName, callback) {
  2236. if (defined(callback)) {
  2237. shader = callback(shader, programName);
  2238. }
  2239. return shader;
  2240. }
  2241. const CreateProgramJob = function () {
  2242. this.programToCreate = undefined;
  2243. this.model = undefined;
  2244. this.context = undefined;
  2245. };
  2246. CreateProgramJob.prototype.set = function (programToCreate, model, context) {
  2247. this.programToCreate = programToCreate;
  2248. this.model = model;
  2249. this.context = context;
  2250. };
  2251. CreateProgramJob.prototype.execute = function () {
  2252. createProgram(this.programToCreate, this.model, this.context);
  2253. };
  2254. ///////////////////////////////////////////////////////////////////////////
  2255. // When building programs for the first time, do not include modifiers for clipping planes and color
  2256. // since this is the version of the program that will be cached for use with other Models.
  2257. function createProgram(programToCreate, model, context) {
  2258. const programId = programToCreate.programId;
  2259. const techniqueId = programToCreate.techniqueId;
  2260. const program = model._sourcePrograms[programId];
  2261. const shaders = model._rendererResources.sourceShaders;
  2262. let vs = shaders[program.vertexShader];
  2263. const fs = shaders[program.fragmentShader];
  2264. const quantizedVertexShaders = model._quantizedVertexShaders;
  2265. if (
  2266. model.extensionsUsed.WEB3D_quantized_attributes ||
  2267. model._dequantizeInShader
  2268. ) {
  2269. let quantizedVS = quantizedVertexShaders[programId];
  2270. if (!defined(quantizedVS)) {
  2271. quantizedVS = modifyShaderForQuantizedAttributes(vs, programId, model);
  2272. quantizedVertexShaders[programId] = quantizedVS;
  2273. }
  2274. vs = quantizedVS;
  2275. }
  2276. const drawVS = modifyShader(vs, programId, model._vertexShaderLoaded);
  2277. let drawFS = modifyShader(fs, programId, model._fragmentShaderLoaded);
  2278. if (!defined(model._uniformMapLoaded)) {
  2279. drawFS = `uniform vec4 czm_pickColor;\n${drawFS}`;
  2280. }
  2281. const imageBasedLighting = model._imageBasedLighting;
  2282. const useIBL = imageBasedLighting.enabled;
  2283. if (useIBL) {
  2284. drawFS = `#define USE_IBL_LIGHTING \n\n${drawFS}`;
  2285. }
  2286. if (defined(model._lightColor)) {
  2287. drawFS = `#define USE_CUSTOM_LIGHT_COLOR \n\n${drawFS}`;
  2288. }
  2289. if (model._sourceVersion !== "2.0" || model._sourceKHRTechniquesWebGL) {
  2290. drawFS = ShaderSource.replaceMain(drawFS, "non_gamma_corrected_main");
  2291. drawFS =
  2292. `${drawFS}\n` +
  2293. `void main() { \n` +
  2294. ` non_gamma_corrected_main(); \n` +
  2295. ` gl_FragColor = czm_gammaCorrect(gl_FragColor); \n` +
  2296. `} \n`;
  2297. }
  2298. if (OctahedralProjectedCubeMap.isSupported(context)) {
  2299. const useSHC = imageBasedLighting.useSphericalHarmonicCoefficients;
  2300. const useSEM = imageBasedLighting.useSpecularEnvironmentMaps;
  2301. const addMatrix = useSHC || useSEM || useIBL;
  2302. if (addMatrix) {
  2303. drawFS = `uniform mat3 gltf_iblReferenceFrameMatrix; \n${drawFS}`;
  2304. }
  2305. if (defined(imageBasedLighting.sphericalHarmonicCoefficients)) {
  2306. drawFS = `${
  2307. "#define DIFFUSE_IBL \n" +
  2308. "#define CUSTOM_SPHERICAL_HARMONICS \n" +
  2309. "uniform vec3 gltf_sphericalHarmonicCoefficients[9]; \n"
  2310. }${drawFS}`;
  2311. } else if (imageBasedLighting.useDefaultSphericalHarmonics) {
  2312. drawFS = `#define DIFFUSE_IBL \n${drawFS}`;
  2313. }
  2314. if (
  2315. defined(imageBasedLighting.specularEnvironmentMapAtlas) &&
  2316. imageBasedLighting.specularEnvironmentMapAtlas.ready
  2317. ) {
  2318. drawFS = `${
  2319. "#define SPECULAR_IBL \n" +
  2320. "#define CUSTOM_SPECULAR_IBL \n" +
  2321. "uniform sampler2D gltf_specularMap; \n" +
  2322. "uniform vec2 gltf_specularMapSize; \n" +
  2323. "uniform float gltf_maxSpecularLOD; \n"
  2324. }${drawFS}`;
  2325. } else if (imageBasedLighting.useDefaultSpecularMaps) {
  2326. drawFS = `#define SPECULAR_IBL \n${drawFS}`;
  2327. }
  2328. }
  2329. if (defined(imageBasedLighting.luminanceAtZenith)) {
  2330. drawFS = `${
  2331. "#define USE_SUN_LUMINANCE \n" + "uniform float gltf_luminanceAtZenith;\n"
  2332. }${drawFS}`;
  2333. }
  2334. createAttributesAndProgram(
  2335. programId,
  2336. techniqueId,
  2337. drawFS,
  2338. drawVS,
  2339. model,
  2340. context
  2341. );
  2342. }
  2343. function recreateProgram(programToCreate, model, context) {
  2344. const programId = programToCreate.programId;
  2345. const techniqueId = programToCreate.techniqueId;
  2346. const program = model._sourcePrograms[programId];
  2347. const shaders = model._rendererResources.sourceShaders;
  2348. const quantizedVertexShaders = model._quantizedVertexShaders;
  2349. const clippingPlaneCollection = model.clippingPlanes;
  2350. const addClippingPlaneCode = isClippingEnabled(model);
  2351. let vs = shaders[program.vertexShader];
  2352. const fs = shaders[program.fragmentShader];
  2353. if (
  2354. model.extensionsUsed.WEB3D_quantized_attributes ||
  2355. model._dequantizeInShader
  2356. ) {
  2357. vs = quantizedVertexShaders[programId];
  2358. }
  2359. let finalFS = fs;
  2360. if (isColorShadingEnabled(model)) {
  2361. finalFS = Model._modifyShaderForColor(finalFS);
  2362. }
  2363. if (addClippingPlaneCode) {
  2364. finalFS = modifyShaderForClippingPlanes(
  2365. finalFS,
  2366. clippingPlaneCollection,
  2367. context
  2368. );
  2369. }
  2370. if (model.splitDirection !== SplitDirection.NONE) {
  2371. finalFS = Splitter.modifyFragmentShader(finalFS);
  2372. }
  2373. const drawVS = modifyShader(vs, programId, model._vertexShaderLoaded);
  2374. let drawFS = modifyShader(finalFS, programId, model._fragmentShaderLoaded);
  2375. if (!defined(model._uniformMapLoaded)) {
  2376. drawFS = `uniform vec4 czm_pickColor;\n${drawFS}`;
  2377. }
  2378. const imageBasedLighting = model._imageBasedLighting;
  2379. const useIBL = imageBasedLighting.enabled;
  2380. if (useIBL) {
  2381. drawFS = `#define USE_IBL_LIGHTING \n\n${drawFS}`;
  2382. }
  2383. if (defined(model._lightColor)) {
  2384. drawFS = `#define USE_CUSTOM_LIGHT_COLOR \n\n${drawFS}`;
  2385. }
  2386. if (model._sourceVersion !== "2.0" || model._sourceKHRTechniquesWebGL) {
  2387. drawFS = ShaderSource.replaceMain(drawFS, "non_gamma_corrected_main");
  2388. drawFS =
  2389. `${drawFS}\n` +
  2390. `void main() { \n` +
  2391. ` non_gamma_corrected_main(); \n` +
  2392. ` gl_FragColor = czm_gammaCorrect(gl_FragColor); \n` +
  2393. `} \n`;
  2394. }
  2395. if (OctahedralProjectedCubeMap.isSupported(context)) {
  2396. const useSHC = imageBasedLighting.useSphericalHarmonicCoefficients;
  2397. const useSEM = imageBasedLighting.useSpecularEnvironmentMaps;
  2398. const addMatrix = useSHC || useSEM || useIBL;
  2399. if (addMatrix) {
  2400. drawFS = `uniform mat3 gltf_iblReferenceFrameMatrix; \n${drawFS}`;
  2401. }
  2402. if (defined(imageBasedLighting.sphericalHarmonicCoefficients)) {
  2403. drawFS = `${
  2404. "#define DIFFUSE_IBL \n" +
  2405. "#define CUSTOM_SPHERICAL_HARMONICS \n" +
  2406. "uniform vec3 gltf_sphericalHarmonicCoefficients[9]; \n"
  2407. }${drawFS}`;
  2408. } else if (imageBasedLighting.useDefaultSphericalHarmonics) {
  2409. drawFS = `#define DIFFUSE_IBL \n${drawFS}`;
  2410. }
  2411. if (
  2412. defined(imageBasedLighting.specularEnvironmentMapAtlas) &&
  2413. imageBasedLighting.specularEnvironmentMapAtlas.ready
  2414. ) {
  2415. drawFS = `${
  2416. "#define SPECULAR_IBL \n" +
  2417. "#define CUSTOM_SPECULAR_IBL \n" +
  2418. "uniform sampler2D gltf_specularMap; \n" +
  2419. "uniform vec2 gltf_specularMapSize; \n" +
  2420. "uniform float gltf_maxSpecularLOD; \n"
  2421. }${drawFS}`;
  2422. } else if (imageBasedLighting.useDefaultSpecularMaps) {
  2423. drawFS = `#define SPECULAR_IBL \n${drawFS}`;
  2424. }
  2425. }
  2426. if (defined(imageBasedLighting.luminanceAtZenith)) {
  2427. drawFS = `${
  2428. "#define USE_SUN_LUMINANCE \n" + "uniform float gltf_luminanceAtZenith;\n"
  2429. }${drawFS}`;
  2430. }
  2431. createAttributesAndProgram(
  2432. programId,
  2433. techniqueId,
  2434. drawFS,
  2435. drawVS,
  2436. model,
  2437. context
  2438. );
  2439. }
  2440. function createAttributesAndProgram(
  2441. programId,
  2442. techniqueId,
  2443. drawFS,
  2444. drawVS,
  2445. model,
  2446. context
  2447. ) {
  2448. const technique = model._sourceTechniques[techniqueId];
  2449. const attributeLocations = ModelUtility.createAttributeLocations(
  2450. technique,
  2451. model._precreatedAttributes
  2452. );
  2453. model._rendererResources.programs[programId] = ShaderProgram.fromCache({
  2454. context: context,
  2455. vertexShaderSource: drawVS,
  2456. fragmentShaderSource: drawFS,
  2457. attributeLocations: attributeLocations,
  2458. });
  2459. }
  2460. const scratchCreateProgramJob = new CreateProgramJob();
  2461. function createPrograms(model, frameState) {
  2462. const loadResources = model._loadResources;
  2463. const programsToCreate = loadResources.programsToCreate;
  2464. if (loadResources.pendingShaderLoads !== 0) {
  2465. return;
  2466. }
  2467. // PERFORMANCE_IDEA: this could be more fine-grained by looking
  2468. // at the shader's bufferView's to determine the buffer dependencies.
  2469. if (loadResources.pendingBufferLoads !== 0) {
  2470. return;
  2471. }
  2472. const context = frameState.context;
  2473. if (model.asynchronous) {
  2474. while (programsToCreate.length > 0) {
  2475. scratchCreateProgramJob.set(programsToCreate.peek(), model, context);
  2476. if (
  2477. !frameState.jobScheduler.execute(
  2478. scratchCreateProgramJob,
  2479. JobType.PROGRAM
  2480. )
  2481. ) {
  2482. break;
  2483. }
  2484. programsToCreate.dequeue();
  2485. }
  2486. } else {
  2487. // Create all loaded programs this frame
  2488. while (programsToCreate.length > 0) {
  2489. createProgram(programsToCreate.dequeue(), model, context);
  2490. }
  2491. }
  2492. }
  2493. function getOnImageCreatedFromTypedArray(loadResources, gltfTexture) {
  2494. return function (image) {
  2495. loadResources.texturesToCreate.enqueue({
  2496. id: gltfTexture.id,
  2497. image: image,
  2498. bufferView: undefined,
  2499. });
  2500. --loadResources.pendingBufferViewToImage;
  2501. };
  2502. }
  2503. function loadTexturesFromBufferViews(model) {
  2504. const loadResources = model._loadResources;
  2505. if (loadResources.pendingBufferLoads !== 0) {
  2506. return;
  2507. }
  2508. while (loadResources.texturesToCreateFromBufferView.length > 0) {
  2509. const gltfTexture = loadResources.texturesToCreateFromBufferView.dequeue();
  2510. const gltf = model.gltf;
  2511. const bufferView = gltf.bufferViews[gltfTexture.bufferView];
  2512. const imageId = gltf.textures[gltfTexture.id].source;
  2513. const onerror = ModelUtility.getFailedLoadFunction(
  2514. model,
  2515. "image",
  2516. `id: ${gltfTexture.id}, bufferView: ${gltfTexture.bufferView}`
  2517. );
  2518. if (gltfTexture.mimeType === "image/ktx2") {
  2519. // Need to make a copy of the embedded KTX2 buffer otherwise the underlying
  2520. // ArrayBuffer may be accessed on both the worker and the main thread and
  2521. // throw an error like "Cannot perform Construct on a detached ArrayBuffer".
  2522. // Look into SharedArrayBuffer at some point to get around this.
  2523. const ktxBuffer = new Uint8Array(loadResources.getBuffer(bufferView));
  2524. loadKTX2(ktxBuffer)
  2525. .then(imageLoad(model, gltfTexture.id, imageId))
  2526. .catch(onerror);
  2527. ++model._loadResources.pendingTextureLoads;
  2528. } else {
  2529. const onload = getOnImageCreatedFromTypedArray(
  2530. loadResources,
  2531. gltfTexture
  2532. );
  2533. loadImageFromTypedArray({
  2534. uint8Array: loadResources.getBuffer(bufferView),
  2535. format: gltfTexture.mimeType,
  2536. flipY: false,
  2537. skipColorSpaceConversion: true,
  2538. })
  2539. .then(onload)
  2540. .catch(onerror);
  2541. ++loadResources.pendingBufferViewToImage;
  2542. }
  2543. }
  2544. }
  2545. function createSamplers(model) {
  2546. const loadResources = model._loadResources;
  2547. if (loadResources.createSamplers) {
  2548. loadResources.createSamplers = false;
  2549. const rendererSamplers = model._rendererResources.samplers;
  2550. ForEach.sampler(model.gltf, function (sampler, samplerId) {
  2551. rendererSamplers[samplerId] = new Sampler({
  2552. wrapS: sampler.wrapS,
  2553. wrapT: sampler.wrapT,
  2554. minificationFilter: sampler.minFilter,
  2555. magnificationFilter: sampler.magFilter,
  2556. });
  2557. });
  2558. }
  2559. }
  2560. ///////////////////////////////////////////////////////////////////////////
  2561. const CreateTextureJob = function () {
  2562. this.gltfTexture = undefined;
  2563. this.model = undefined;
  2564. this.context = undefined;
  2565. };
  2566. CreateTextureJob.prototype.set = function (gltfTexture, model, context) {
  2567. this.gltfTexture = gltfTexture;
  2568. this.model = model;
  2569. this.context = context;
  2570. };
  2571. CreateTextureJob.prototype.execute = function () {
  2572. createTexture(this.gltfTexture, this.model, this.context);
  2573. };
  2574. ///////////////////////////////////////////////////////////////////////////
  2575. function createTexture(gltfTexture, model, context) {
  2576. const textures = model.gltf.textures;
  2577. const texture = textures[gltfTexture.id];
  2578. const rendererSamplers = model._rendererResources.samplers;
  2579. let sampler = rendererSamplers[texture.sampler];
  2580. if (!defined(sampler)) {
  2581. sampler = new Sampler({
  2582. wrapS: TextureWrap.REPEAT,
  2583. wrapT: TextureWrap.REPEAT,
  2584. });
  2585. }
  2586. let usesTextureTransform = false;
  2587. const materials = model.gltf.materials;
  2588. const materialsLength = materials.length;
  2589. for (let i = 0; i < materialsLength; ++i) {
  2590. const material = materials[i];
  2591. if (
  2592. defined(material.extensions) &&
  2593. defined(material.extensions.KHR_techniques_webgl)
  2594. ) {
  2595. const values = material.extensions.KHR_techniques_webgl.values;
  2596. for (const valueName in values) {
  2597. if (
  2598. values.hasOwnProperty(valueName) &&
  2599. valueName.indexOf("Texture") !== -1
  2600. ) {
  2601. const value = values[valueName];
  2602. if (
  2603. value.index === gltfTexture.id &&
  2604. defined(value.extensions) &&
  2605. defined(value.extensions.KHR_texture_transform)
  2606. ) {
  2607. usesTextureTransform = true;
  2608. break;
  2609. }
  2610. }
  2611. }
  2612. }
  2613. if (usesTextureTransform) {
  2614. break;
  2615. }
  2616. }
  2617. const wrapS = sampler.wrapS;
  2618. const wrapT = sampler.wrapT;
  2619. let minFilter = sampler.minificationFilter;
  2620. if (
  2621. usesTextureTransform &&
  2622. minFilter !== TextureMinificationFilter.LINEAR &&
  2623. minFilter !== TextureMinificationFilter.NEAREST
  2624. ) {
  2625. if (
  2626. minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||
  2627. minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR
  2628. ) {
  2629. minFilter = TextureMinificationFilter.NEAREST;
  2630. } else {
  2631. minFilter = TextureMinificationFilter.LINEAR;
  2632. }
  2633. sampler = new Sampler({
  2634. wrapS: sampler.wrapS,
  2635. wrapT: sampler.wrapT,
  2636. minificationFilter: minFilter,
  2637. magnificationFilter: sampler.magnificationFilter,
  2638. });
  2639. }
  2640. const internalFormat = gltfTexture.internalFormat;
  2641. const mipmap =
  2642. !(
  2643. defined(internalFormat) && PixelFormat.isCompressedFormat(internalFormat)
  2644. ) &&
  2645. (minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||
  2646. minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR ||
  2647. minFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST ||
  2648. minFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR);
  2649. const requiresNpot =
  2650. mipmap ||
  2651. wrapS === TextureWrap.REPEAT ||
  2652. wrapS === TextureWrap.MIRRORED_REPEAT ||
  2653. wrapT === TextureWrap.REPEAT ||
  2654. wrapT === TextureWrap.MIRRORED_REPEAT;
  2655. let npot;
  2656. let tx;
  2657. let source = gltfTexture.image;
  2658. if (defined(internalFormat)) {
  2659. npot =
  2660. !CesiumMath.isPowerOfTwo(gltfTexture.width) ||
  2661. !CesiumMath.isPowerOfTwo(gltfTexture.height);
  2662. // Warning to encourage power of 2 texture dimensions with KHR_texture_basisu
  2663. if (
  2664. !context.webgl2 &&
  2665. PixelFormat.isCompressedFormat(internalFormat) &&
  2666. npot &&
  2667. requiresNpot
  2668. ) {
  2669. console.warn(
  2670. "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."
  2671. );
  2672. }
  2673. let minificationFilter = sampler.minificationFilter;
  2674. if (
  2675. !defined(gltfTexture.mipLevels) &&
  2676. (minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||
  2677. minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR)
  2678. ) {
  2679. minificationFilter = TextureMinificationFilter.NEAREST;
  2680. } else if (
  2681. !defined(gltfTexture.mipLevels) &&
  2682. (minFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST ||
  2683. minFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR)
  2684. ) {
  2685. minificationFilter = TextureMinificationFilter.LINEAR;
  2686. }
  2687. sampler = new Sampler({
  2688. wrapS: sampler.wrapS,
  2689. wrapT: sampler.wrapT,
  2690. minificationFilter: minificationFilter,
  2691. magnificationFilter: sampler.magnificationFilter,
  2692. });
  2693. tx = new Texture({
  2694. context: context,
  2695. source: {
  2696. arrayBufferView: gltfTexture.bufferView,
  2697. mipLevels: gltfTexture.mipLevels,
  2698. },
  2699. width: gltfTexture.width,
  2700. height: gltfTexture.height,
  2701. pixelFormat: internalFormat,
  2702. sampler: sampler,
  2703. });
  2704. } else if (defined(source)) {
  2705. npot =
  2706. !CesiumMath.isPowerOfTwo(source.width) ||
  2707. !CesiumMath.isPowerOfTwo(source.height);
  2708. if (requiresNpot && npot) {
  2709. // WebGL requires power-of-two texture dimensions for mipmapping and REPEAT/MIRRORED_REPEAT wrap modes.
  2710. const canvas = document.createElement("canvas");
  2711. canvas.width = CesiumMath.nextPowerOfTwo(source.width);
  2712. canvas.height = CesiumMath.nextPowerOfTwo(source.height);
  2713. const canvasContext = canvas.getContext("2d");
  2714. canvasContext.drawImage(
  2715. source,
  2716. 0,
  2717. 0,
  2718. source.width,
  2719. source.height,
  2720. 0,
  2721. 0,
  2722. canvas.width,
  2723. canvas.height
  2724. );
  2725. source = canvas;
  2726. }
  2727. tx = new Texture({
  2728. context: context,
  2729. source: source,
  2730. pixelFormat: texture.internalFormat,
  2731. pixelDatatype: texture.type,
  2732. sampler: sampler,
  2733. flipY: false,
  2734. skipColorSpaceConversion: true,
  2735. });
  2736. // GLTF_SPEC: Support TEXTURE_CUBE_MAP. https://github.com/KhronosGroup/glTF/issues/40
  2737. if (mipmap) {
  2738. tx.generateMipmap();
  2739. }
  2740. }
  2741. if (defined(tx)) {
  2742. model._rendererResources.textures[gltfTexture.id] = tx;
  2743. model._texturesByteLength += tx.sizeInBytes;
  2744. }
  2745. }
  2746. const scratchCreateTextureJob = new CreateTextureJob();
  2747. function createTextures(model, frameState) {
  2748. const context = frameState.context;
  2749. const texturesToCreate = model._loadResources.texturesToCreate;
  2750. if (model.asynchronous) {
  2751. while (texturesToCreate.length > 0) {
  2752. scratchCreateTextureJob.set(texturesToCreate.peek(), model, context);
  2753. if (
  2754. !frameState.jobScheduler.execute(
  2755. scratchCreateTextureJob,
  2756. JobType.TEXTURE
  2757. )
  2758. ) {
  2759. break;
  2760. }
  2761. texturesToCreate.dequeue();
  2762. }
  2763. } else {
  2764. // Create all loaded textures this frame
  2765. while (texturesToCreate.length > 0) {
  2766. createTexture(texturesToCreate.dequeue(), model, context);
  2767. }
  2768. }
  2769. }
  2770. function getAttributeLocations(model, primitive) {
  2771. const techniques = model._sourceTechniques;
  2772. // Retrieve the compiled shader program to assign index values to attributes
  2773. const attributeLocations = {};
  2774. let location;
  2775. let index;
  2776. const material = model._runtime.materialsById[primitive.material];
  2777. if (!defined(material)) {
  2778. return attributeLocations;
  2779. }
  2780. const technique = techniques[material._technique];
  2781. if (!defined(technique)) {
  2782. return attributeLocations;
  2783. }
  2784. const attributes = technique.attributes;
  2785. const program = model._rendererResources.programs[technique.program];
  2786. const programAttributeLocations = program._attributeLocations;
  2787. for (location in programAttributeLocations) {
  2788. if (programAttributeLocations.hasOwnProperty(location)) {
  2789. const attribute = attributes[location];
  2790. if (defined(attribute)) {
  2791. index = programAttributeLocations[location];
  2792. attributeLocations[attribute.semantic] = index;
  2793. }
  2794. }
  2795. }
  2796. // Add pre-created attributes.
  2797. const precreatedAttributes = model._precreatedAttributes;
  2798. if (defined(precreatedAttributes)) {
  2799. for (location in precreatedAttributes) {
  2800. if (precreatedAttributes.hasOwnProperty(location)) {
  2801. index = programAttributeLocations[location];
  2802. attributeLocations[location] = index;
  2803. }
  2804. }
  2805. }
  2806. return attributeLocations;
  2807. }
  2808. function createJoints(model, runtimeSkins) {
  2809. const gltf = model.gltf;
  2810. const skins = gltf.skins;
  2811. const nodes = gltf.nodes;
  2812. const runtimeNodes = model._runtime.nodes;
  2813. const skinnedNodesIds = model._loadResources.skinnedNodesIds;
  2814. const length = skinnedNodesIds.length;
  2815. for (let j = 0; j < length; ++j) {
  2816. const id = skinnedNodesIds[j];
  2817. const skinnedNode = runtimeNodes[id];
  2818. const node = nodes[id];
  2819. const runtimeSkin = runtimeSkins[node.skin];
  2820. skinnedNode.inverseBindMatrices = runtimeSkin.inverseBindMatrices;
  2821. skinnedNode.bindShapeMatrix = runtimeSkin.bindShapeMatrix;
  2822. const gltfJoints = skins[node.skin].joints;
  2823. const jointsLength = gltfJoints.length;
  2824. for (let i = 0; i < jointsLength; ++i) {
  2825. const nodeId = gltfJoints[i];
  2826. const jointNode = runtimeNodes[nodeId];
  2827. skinnedNode.joints.push(jointNode);
  2828. }
  2829. }
  2830. }
  2831. function createSkins(model) {
  2832. const loadResources = model._loadResources;
  2833. if (loadResources.pendingBufferLoads !== 0) {
  2834. return;
  2835. }
  2836. if (!loadResources.createSkins) {
  2837. return;
  2838. }
  2839. loadResources.createSkins = false;
  2840. const gltf = model.gltf;
  2841. const accessors = gltf.accessors;
  2842. const runtimeSkins = {};
  2843. ForEach.skin(gltf, function (skin, id) {
  2844. const accessor = accessors[skin.inverseBindMatrices];
  2845. let bindShapeMatrix;
  2846. if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) {
  2847. bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix);
  2848. }
  2849. runtimeSkins[id] = {
  2850. inverseBindMatrices: ModelAnimationCache.getSkinInverseBindMatrices(
  2851. model,
  2852. accessor
  2853. ),
  2854. bindShapeMatrix: bindShapeMatrix, // not used when undefined
  2855. };
  2856. });
  2857. createJoints(model, runtimeSkins);
  2858. }
  2859. function getChannelEvaluator(model, runtimeNode, targetPath, spline) {
  2860. return function (localAnimationTime) {
  2861. if (defined(spline)) {
  2862. localAnimationTime = model.clampAnimations
  2863. ? spline.clampTime(localAnimationTime)
  2864. : spline.wrapTime(localAnimationTime);
  2865. runtimeNode[targetPath] = spline.evaluate(
  2866. localAnimationTime,
  2867. runtimeNode[targetPath]
  2868. );
  2869. runtimeNode.dirtyNumber = model._maxDirtyNumber;
  2870. }
  2871. };
  2872. }
  2873. function createRuntimeAnimations(model) {
  2874. const loadResources = model._loadResources;
  2875. if (!loadResources.finishedPendingBufferLoads()) {
  2876. return;
  2877. }
  2878. if (!loadResources.createRuntimeAnimations) {
  2879. return;
  2880. }
  2881. loadResources.createRuntimeAnimations = false;
  2882. model._runtime.animations = [];
  2883. const runtimeNodes = model._runtime.nodes;
  2884. const accessors = model.gltf.accessors;
  2885. ForEach.animation(model.gltf, function (animation, i) {
  2886. const channels = animation.channels;
  2887. const samplers = animation.samplers;
  2888. // Find start and stop time for the entire animation
  2889. let startTime = Number.MAX_VALUE;
  2890. let stopTime = -Number.MAX_VALUE;
  2891. const channelsLength = channels.length;
  2892. const channelEvaluators = new Array(channelsLength);
  2893. for (let j = 0; j < channelsLength; ++j) {
  2894. const channel = channels[j];
  2895. const target = channel.target;
  2896. const path = target.path;
  2897. const sampler = samplers[channel.sampler];
  2898. const input = ModelAnimationCache.getAnimationParameterValues(
  2899. model,
  2900. accessors[sampler.input]
  2901. );
  2902. const output = ModelAnimationCache.getAnimationParameterValues(
  2903. model,
  2904. accessors[sampler.output]
  2905. );
  2906. startTime = Math.min(startTime, input[0]);
  2907. stopTime = Math.max(stopTime, input[input.length - 1]);
  2908. const spline = ModelAnimationCache.getAnimationSpline(
  2909. model,
  2910. i,
  2911. animation,
  2912. channel.sampler,
  2913. sampler,
  2914. input,
  2915. path,
  2916. output
  2917. );
  2918. channelEvaluators[j] = getChannelEvaluator(
  2919. model,
  2920. runtimeNodes[target.node],
  2921. target.path,
  2922. spline
  2923. );
  2924. }
  2925. model._runtime.animations[i] = {
  2926. name: animation.name,
  2927. startTime: startTime,
  2928. stopTime: stopTime,
  2929. channelEvaluators: channelEvaluators,
  2930. };
  2931. });
  2932. }
  2933. function createVertexArrays(model, context) {
  2934. const loadResources = model._loadResources;
  2935. if (
  2936. !loadResources.finishedBuffersCreation() ||
  2937. !loadResources.finishedProgramCreation() ||
  2938. !loadResources.createVertexArrays
  2939. ) {
  2940. return;
  2941. }
  2942. loadResources.createVertexArrays = false;
  2943. const rendererBuffers = model._rendererResources.buffers;
  2944. const rendererVertexArrays = model._rendererResources.vertexArrays;
  2945. const gltf = model.gltf;
  2946. const accessors = gltf.accessors;
  2947. ForEach.mesh(gltf, function (mesh, meshId) {
  2948. ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {
  2949. const attributes = [];
  2950. let attributeLocation;
  2951. const attributeLocations = getAttributeLocations(model, primitive);
  2952. const decodedData =
  2953. model._decodedData[`${meshId}.primitive.${primitiveId}`];
  2954. ForEach.meshPrimitiveAttribute(primitive, function (
  2955. accessorId,
  2956. attributeName
  2957. ) {
  2958. // Skip if the attribute is not used by the material, e.g., because the asset
  2959. // was exported with an attribute that wasn't used and the asset wasn't optimized.
  2960. attributeLocation = attributeLocations[attributeName];
  2961. if (defined(attributeLocation)) {
  2962. // Use attributes of previously decoded draco geometry
  2963. if (defined(decodedData)) {
  2964. const decodedAttributes = decodedData.attributes;
  2965. if (decodedAttributes.hasOwnProperty(attributeName)) {
  2966. const decodedAttribute = decodedAttributes[attributeName];
  2967. attributes.push({
  2968. index: attributeLocation,
  2969. vertexBuffer: rendererBuffers[decodedAttribute.bufferView],
  2970. componentsPerAttribute: decodedAttribute.componentsPerAttribute,
  2971. componentDatatype: decodedAttribute.componentDatatype,
  2972. normalize: decodedAttribute.normalized,
  2973. offsetInBytes: decodedAttribute.byteOffset,
  2974. strideInBytes: decodedAttribute.byteStride,
  2975. });
  2976. return;
  2977. }
  2978. }
  2979. const a = accessors[accessorId];
  2980. const normalize = defined(a.normalized) && a.normalized;
  2981. attributes.push({
  2982. index: attributeLocation,
  2983. vertexBuffer: rendererBuffers[a.bufferView],
  2984. componentsPerAttribute: numberOfComponentsForType(a.type),
  2985. componentDatatype: a.componentType,
  2986. normalize: normalize,
  2987. offsetInBytes: a.byteOffset,
  2988. strideInBytes: getAccessorByteStride(gltf, a),
  2989. });
  2990. }
  2991. });
  2992. // Add pre-created attributes
  2993. let attribute;
  2994. let attributeName;
  2995. const precreatedAttributes = model._precreatedAttributes;
  2996. if (defined(precreatedAttributes)) {
  2997. for (attributeName in precreatedAttributes) {
  2998. if (precreatedAttributes.hasOwnProperty(attributeName)) {
  2999. attributeLocation = attributeLocations[attributeName];
  3000. if (defined(attributeLocation)) {
  3001. attribute = precreatedAttributes[attributeName];
  3002. attribute.index = attributeLocation;
  3003. attributes.push(attribute);
  3004. }
  3005. }
  3006. }
  3007. }
  3008. let indexBuffer;
  3009. if (defined(primitive.indices)) {
  3010. const accessor = accessors[primitive.indices];
  3011. let bufferView = accessor.bufferView;
  3012. // Use buffer of previously decoded draco geometry
  3013. if (defined(decodedData)) {
  3014. bufferView = decodedData.bufferView;
  3015. }
  3016. indexBuffer = rendererBuffers[bufferView];
  3017. }
  3018. rendererVertexArrays[
  3019. `${meshId}.primitive.${primitiveId}`
  3020. ] = new VertexArray({
  3021. context: context,
  3022. attributes: attributes,
  3023. indexBuffer: indexBuffer,
  3024. });
  3025. });
  3026. });
  3027. }
  3028. function createRenderStates(model) {
  3029. const loadResources = model._loadResources;
  3030. if (loadResources.createRenderStates) {
  3031. loadResources.createRenderStates = false;
  3032. ForEach.material(model.gltf, function (material, materialId) {
  3033. createRenderStateForMaterial(model, material, materialId);
  3034. });
  3035. }
  3036. }
  3037. function createRenderStateForMaterial(model, material, materialId) {
  3038. const rendererRenderStates = model._rendererResources.renderStates;
  3039. let blendEquationSeparate = [
  3040. WebGLConstants.FUNC_ADD,
  3041. WebGLConstants.FUNC_ADD,
  3042. ];
  3043. let blendFuncSeparate = [
  3044. WebGLConstants.ONE,
  3045. WebGLConstants.ONE_MINUS_SRC_ALPHA,
  3046. WebGLConstants.ONE,
  3047. WebGLConstants.ONE_MINUS_SRC_ALPHA,
  3048. ];
  3049. if (defined(material.extensions) && defined(material.extensions.KHR_blend)) {
  3050. blendEquationSeparate = material.extensions.KHR_blend.blendEquation;
  3051. blendFuncSeparate = material.extensions.KHR_blend.blendFactors;
  3052. }
  3053. const enableCulling = !material.doubleSided;
  3054. const blendingEnabled = material.alphaMode === "BLEND";
  3055. rendererRenderStates[materialId] = RenderState.fromCache({
  3056. cull: {
  3057. enabled: enableCulling,
  3058. },
  3059. depthTest: {
  3060. enabled: true,
  3061. func: DepthFunction.LESS_OR_EQUAL,
  3062. },
  3063. depthMask: !blendingEnabled,
  3064. blending: {
  3065. enabled: blendingEnabled,
  3066. equationRgb: blendEquationSeparate[0],
  3067. equationAlpha: blendEquationSeparate[1],
  3068. functionSourceRgb: blendFuncSeparate[0],
  3069. functionDestinationRgb: blendFuncSeparate[1],
  3070. functionSourceAlpha: blendFuncSeparate[2],
  3071. functionDestinationAlpha: blendFuncSeparate[3],
  3072. },
  3073. });
  3074. }
  3075. ///////////////////////////////////////////////////////////////////////////
  3076. const gltfUniformsFromNode = {
  3077. MODEL: function (uniformState, model, runtimeNode) {
  3078. return function () {
  3079. return runtimeNode.computedMatrix;
  3080. };
  3081. },
  3082. VIEW: function (uniformState, model, runtimeNode) {
  3083. return function () {
  3084. return uniformState.view;
  3085. };
  3086. },
  3087. PROJECTION: function (uniformState, model, runtimeNode) {
  3088. return function () {
  3089. return uniformState.projection;
  3090. };
  3091. },
  3092. MODELVIEW: function (uniformState, model, runtimeNode) {
  3093. const mv = new Matrix4();
  3094. return function () {
  3095. return Matrix4.multiplyTransformation(
  3096. uniformState.view,
  3097. runtimeNode.computedMatrix,
  3098. mv
  3099. );
  3100. };
  3101. },
  3102. CESIUM_RTC_MODELVIEW: function (uniformState, model, runtimeNode) {
  3103. // CESIUM_RTC extension
  3104. const mvRtc = new Matrix4();
  3105. return function () {
  3106. Matrix4.multiplyTransformation(
  3107. uniformState.view,
  3108. runtimeNode.computedMatrix,
  3109. mvRtc
  3110. );
  3111. return Matrix4.setTranslation(mvRtc, model._rtcCenterEye, mvRtc);
  3112. };
  3113. },
  3114. MODELVIEWPROJECTION: function (uniformState, model, runtimeNode) {
  3115. const mvp = new Matrix4();
  3116. return function () {
  3117. Matrix4.multiplyTransformation(
  3118. uniformState.view,
  3119. runtimeNode.computedMatrix,
  3120. mvp
  3121. );
  3122. return Matrix4.multiply(uniformState._projection, mvp, mvp);
  3123. };
  3124. },
  3125. MODELINVERSE: function (uniformState, model, runtimeNode) {
  3126. const mInverse = new Matrix4();
  3127. return function () {
  3128. return Matrix4.inverse(runtimeNode.computedMatrix, mInverse);
  3129. };
  3130. },
  3131. VIEWINVERSE: function (uniformState, model) {
  3132. return function () {
  3133. return uniformState.inverseView;
  3134. };
  3135. },
  3136. PROJECTIONINVERSE: function (uniformState, model, runtimeNode) {
  3137. return function () {
  3138. return uniformState.inverseProjection;
  3139. };
  3140. },
  3141. MODELVIEWINVERSE: function (uniformState, model, runtimeNode) {
  3142. const mv = new Matrix4();
  3143. const mvInverse = new Matrix4();
  3144. return function () {
  3145. Matrix4.multiplyTransformation(
  3146. uniformState.view,
  3147. runtimeNode.computedMatrix,
  3148. mv
  3149. );
  3150. return Matrix4.inverse(mv, mvInverse);
  3151. };
  3152. },
  3153. MODELVIEWPROJECTIONINVERSE: function (uniformState, model, runtimeNode) {
  3154. const mvp = new Matrix4();
  3155. const mvpInverse = new Matrix4();
  3156. return function () {
  3157. Matrix4.multiplyTransformation(
  3158. uniformState.view,
  3159. runtimeNode.computedMatrix,
  3160. mvp
  3161. );
  3162. Matrix4.multiply(uniformState._projection, mvp, mvp);
  3163. return Matrix4.inverse(mvp, mvpInverse);
  3164. };
  3165. },
  3166. MODELINVERSETRANSPOSE: function (uniformState, model, runtimeNode) {
  3167. const mInverse = new Matrix4();
  3168. const mInverseTranspose = new Matrix3();
  3169. return function () {
  3170. Matrix4.inverse(runtimeNode.computedMatrix, mInverse);
  3171. Matrix4.getMatrix3(mInverse, mInverseTranspose);
  3172. return Matrix3.transpose(mInverseTranspose, mInverseTranspose);
  3173. };
  3174. },
  3175. MODELVIEWINVERSETRANSPOSE: function (uniformState, model, runtimeNode) {
  3176. const mv = new Matrix4();
  3177. const mvInverse = new Matrix4();
  3178. const mvInverseTranspose = new Matrix3();
  3179. return function () {
  3180. Matrix4.multiplyTransformation(
  3181. uniformState.view,
  3182. runtimeNode.computedMatrix,
  3183. mv
  3184. );
  3185. Matrix4.inverse(mv, mvInverse);
  3186. Matrix4.getMatrix3(mvInverse, mvInverseTranspose);
  3187. return Matrix3.transpose(mvInverseTranspose, mvInverseTranspose);
  3188. };
  3189. },
  3190. VIEWPORT: function (uniformState, model, runtimeNode) {
  3191. return function () {
  3192. return uniformState.viewportCartesian4;
  3193. };
  3194. },
  3195. };
  3196. function getUniformFunctionFromSource(source, model, semantic, uniformState) {
  3197. const runtimeNode = model._runtime.nodes[source];
  3198. return gltfUniformsFromNode[semantic](uniformState, model, runtimeNode);
  3199. }
  3200. function createUniformsForMaterial(
  3201. model,
  3202. material,
  3203. technique,
  3204. instanceValues,
  3205. context,
  3206. textures,
  3207. defaultTexture
  3208. ) {
  3209. const uniformMap = {};
  3210. const uniformValues = {};
  3211. let jointMatrixUniformName;
  3212. let morphWeightsUniformName;
  3213. ForEach.techniqueUniform(technique, function (uniform, uniformName) {
  3214. // GLTF_SPEC: This does not take into account uniform arrays,
  3215. // indicated by uniforms with a count property.
  3216. //
  3217. // https://github.com/KhronosGroup/glTF/issues/258
  3218. // GLTF_SPEC: In this implementation, material parameters with a
  3219. // semantic or targeted via a source (for animation) are not
  3220. // targetable for material animations. Is this too strict?
  3221. //
  3222. // https://github.com/KhronosGroup/glTF/issues/142
  3223. let uv;
  3224. if (defined(instanceValues) && defined(instanceValues[uniformName])) {
  3225. // Parameter overrides by the instance technique
  3226. uv = ModelUtility.createUniformFunction(
  3227. uniform.type,
  3228. instanceValues[uniformName],
  3229. textures,
  3230. defaultTexture
  3231. );
  3232. uniformMap[uniformName] = uv.func;
  3233. uniformValues[uniformName] = uv;
  3234. } else if (defined(uniform.node)) {
  3235. uniformMap[uniformName] = getUniformFunctionFromSource(
  3236. uniform.node,
  3237. model,
  3238. uniform.semantic,
  3239. context.uniformState
  3240. );
  3241. } else if (defined(uniform.semantic)) {
  3242. if (uniform.semantic === "JOINTMATRIX") {
  3243. jointMatrixUniformName = uniformName;
  3244. } else if (uniform.semantic === "MORPHWEIGHTS") {
  3245. morphWeightsUniformName = uniformName;
  3246. } else if (uniform.semantic === "ALPHACUTOFF") {
  3247. // The material's alphaCutoff value uses a uniform with semantic ALPHACUTOFF.
  3248. // A uniform with this semantic will ignore the instance or default values.
  3249. const alphaMode = material.alphaMode;
  3250. if (defined(alphaMode) && alphaMode === "MASK") {
  3251. const alphaCutoffValue = defaultValue(material.alphaCutoff, 0.5);
  3252. uv = ModelUtility.createUniformFunction(
  3253. uniform.type,
  3254. alphaCutoffValue,
  3255. textures,
  3256. defaultTexture
  3257. );
  3258. uniformMap[uniformName] = uv.func;
  3259. uniformValues[uniformName] = uv;
  3260. }
  3261. } else {
  3262. // Map glTF semantic to Cesium automatic uniform
  3263. uniformMap[uniformName] = ModelUtility.getGltfSemanticUniforms()[
  3264. uniform.semantic
  3265. ](context.uniformState, model);
  3266. }
  3267. } else if (defined(uniform.value)) {
  3268. // Technique value that isn't overridden by a material
  3269. const uv2 = ModelUtility.createUniformFunction(
  3270. uniform.type,
  3271. uniform.value,
  3272. textures,
  3273. defaultTexture
  3274. );
  3275. uniformMap[uniformName] = uv2.func;
  3276. uniformValues[uniformName] = uv2;
  3277. }
  3278. });
  3279. return {
  3280. map: uniformMap,
  3281. values: uniformValues,
  3282. jointMatrixUniformName: jointMatrixUniformName,
  3283. morphWeightsUniformName: morphWeightsUniformName,
  3284. };
  3285. }
  3286. function createUniformMaps(model, context) {
  3287. const loadResources = model._loadResources;
  3288. if (!loadResources.finishedProgramCreation()) {
  3289. return;
  3290. }
  3291. if (!loadResources.createUniformMaps) {
  3292. return;
  3293. }
  3294. loadResources.createUniformMaps = false;
  3295. const gltf = model.gltf;
  3296. const techniques = model._sourceTechniques;
  3297. const uniformMaps = model._uniformMaps;
  3298. const textures = model._rendererResources.textures;
  3299. const defaultTexture = model._defaultTexture;
  3300. ForEach.material(gltf, function (material, materialId) {
  3301. const modelMaterial = model._runtime.materialsById[materialId];
  3302. const technique = techniques[modelMaterial._technique];
  3303. const instanceValues = modelMaterial._values;
  3304. const uniforms = createUniformsForMaterial(
  3305. model,
  3306. material,
  3307. technique,
  3308. instanceValues,
  3309. context,
  3310. textures,
  3311. defaultTexture
  3312. );
  3313. const u = uniformMaps[materialId];
  3314. u.uniformMap = uniforms.map; // uniform name -> function for the renderer
  3315. u.values = uniforms.values; // material parameter name -> ModelMaterial for modifying the parameter at runtime
  3316. u.jointMatrixUniformName = uniforms.jointMatrixUniformName;
  3317. u.morphWeightsUniformName = uniforms.morphWeightsUniformName;
  3318. if (defined(technique.attributes.a_outlineCoordinates)) {
  3319. const outlineTexture = ModelOutlineLoader.createTexture(model, context);
  3320. u.uniformMap.u_outlineTexture = function () {
  3321. return outlineTexture;
  3322. };
  3323. }
  3324. });
  3325. }
  3326. function createUniformsForDracoQuantizedAttributes(decodedData) {
  3327. return ModelUtility.createUniformsForDracoQuantizedAttributes(
  3328. decodedData.attributes
  3329. );
  3330. }
  3331. function createUniformsForQuantizedAttributes(model, primitive) {
  3332. const programId = getProgramForPrimitive(model, primitive);
  3333. const quantizedUniforms = model._quantizedUniforms[programId];
  3334. return ModelUtility.createUniformsForQuantizedAttributes(
  3335. model.gltf,
  3336. primitive,
  3337. quantizedUniforms
  3338. );
  3339. }
  3340. function createPickColorFunction(color) {
  3341. return function () {
  3342. return color;
  3343. };
  3344. }
  3345. function createJointMatricesFunction(runtimeNode) {
  3346. return function () {
  3347. return runtimeNode.computedJointMatrices;
  3348. };
  3349. }
  3350. function createMorphWeightsFunction(runtimeNode) {
  3351. return function () {
  3352. return runtimeNode.weights;
  3353. };
  3354. }
  3355. function createSilhouetteColorFunction(model) {
  3356. return function () {
  3357. return model.silhouetteColor;
  3358. };
  3359. }
  3360. function createSilhouetteSizeFunction(model) {
  3361. return function () {
  3362. return model.silhouetteSize;
  3363. };
  3364. }
  3365. function createColorFunction(model) {
  3366. return function () {
  3367. return model.color;
  3368. };
  3369. }
  3370. function createClippingPlanesMatrixFunction(model) {
  3371. return function () {
  3372. return model._clippingPlanesMatrix;
  3373. };
  3374. }
  3375. function createIBLReferenceFrameMatrixFunction(model) {
  3376. return function () {
  3377. return model._iblReferenceFrameMatrix;
  3378. };
  3379. }
  3380. function createClippingPlanesFunction(model) {
  3381. return function () {
  3382. const clippingPlanes = model.clippingPlanes;
  3383. return !defined(clippingPlanes) || !clippingPlanes.enabled
  3384. ? model._defaultTexture
  3385. : clippingPlanes.texture;
  3386. };
  3387. }
  3388. function createClippingPlanesEdgeStyleFunction(model) {
  3389. return function () {
  3390. const clippingPlanes = model.clippingPlanes;
  3391. if (!defined(clippingPlanes)) {
  3392. return Color.WHITE.withAlpha(0.0);
  3393. }
  3394. const style = Color.clone(clippingPlanes.edgeColor);
  3395. style.alpha = clippingPlanes.edgeWidth;
  3396. return style;
  3397. };
  3398. }
  3399. function createColorBlendFunction(model) {
  3400. return function () {
  3401. return ColorBlendMode.getColorBlend(
  3402. model.colorBlendMode,
  3403. model.colorBlendAmount
  3404. );
  3405. };
  3406. }
  3407. function createIBLFactorFunction(model) {
  3408. return function () {
  3409. return model._imageBasedLighting.imageBasedLightingFactor;
  3410. };
  3411. }
  3412. function createLightColorFunction(model) {
  3413. return function () {
  3414. return model._lightColor;
  3415. };
  3416. }
  3417. function createLuminanceAtZenithFunction(model) {
  3418. return function () {
  3419. return model._imageBasedLighting.luminanceAtZenith;
  3420. };
  3421. }
  3422. function createSphericalHarmonicCoefficientsFunction(model) {
  3423. return function () {
  3424. return model._imageBasedLighting.sphericalHarmonicCoefficients;
  3425. };
  3426. }
  3427. function createSpecularEnvironmentMapFunction(model) {
  3428. return function () {
  3429. return model._imageBasedLighting.specularEnvironmentMapAtlas.texture;
  3430. };
  3431. }
  3432. function createSpecularEnvironmentMapSizeFunction(model) {
  3433. return function () {
  3434. return model._imageBasedLighting.specularEnvironmentMapAtlas.texture
  3435. .dimensions;
  3436. };
  3437. }
  3438. function createSpecularEnvironmentMapLOD(model) {
  3439. return function () {
  3440. return model._imageBasedLighting.specularEnvironmentMapAtlas
  3441. .maximumMipmapLevel;
  3442. };
  3443. }
  3444. function triangleCountFromPrimitiveIndices(primitive, indicesCount) {
  3445. switch (primitive.mode) {
  3446. case PrimitiveType.TRIANGLES:
  3447. return indicesCount / 3;
  3448. case PrimitiveType.TRIANGLE_STRIP:
  3449. case PrimitiveType.TRIANGLE_FAN:
  3450. return Math.max(indicesCount - 2, 0);
  3451. default:
  3452. return 0;
  3453. }
  3454. }
  3455. function createCommand(model, gltfNode, runtimeNode, context, scene3DOnly) {
  3456. const nodeCommands = model._nodeCommands;
  3457. const pickIds = model._pickIds;
  3458. const allowPicking = model.allowPicking;
  3459. const runtimeMeshesByName = model._runtime.meshesByName;
  3460. const resources = model._rendererResources;
  3461. const rendererVertexArrays = resources.vertexArrays;
  3462. const rendererPrograms = resources.programs;
  3463. const rendererRenderStates = resources.renderStates;
  3464. const uniformMaps = model._uniformMaps;
  3465. const gltf = model.gltf;
  3466. const accessors = gltf.accessors;
  3467. const gltfMeshes = gltf.meshes;
  3468. const id = gltfNode.mesh;
  3469. const mesh = gltfMeshes[id];
  3470. const primitives = mesh.primitives;
  3471. const length = primitives.length;
  3472. // The glTF node hierarchy is a DAG so a node can have more than one
  3473. // parent, so a node may already have commands. If so, append more
  3474. // since they will have a different model matrix.
  3475. for (let i = 0; i < length; ++i) {
  3476. const primitive = primitives[i];
  3477. const ix = accessors[primitive.indices];
  3478. const material = model._runtime.materialsById[primitive.material];
  3479. const programId = material._program;
  3480. const decodedData = model._decodedData[`${id}.primitive.${i}`];
  3481. let boundingSphere;
  3482. const positionAccessor = primitive.attributes.POSITION;
  3483. if (defined(positionAccessor)) {
  3484. const minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor);
  3485. boundingSphere = BoundingSphere.fromCornerPoints(
  3486. Cartesian3.fromArray(minMax.min),
  3487. Cartesian3.fromArray(minMax.max)
  3488. );
  3489. }
  3490. const vertexArray = rendererVertexArrays[`${id}.primitive.${i}`];
  3491. let offset;
  3492. let count;
  3493. // Use indices of the previously decoded Draco geometry.
  3494. if (defined(decodedData)) {
  3495. count = decodedData.numberOfIndices;
  3496. offset = 0;
  3497. } else if (defined(ix)) {
  3498. count = ix.count;
  3499. offset = ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType); // glTF has offset in bytes. Cesium has offsets in indices
  3500. } else {
  3501. const positions = accessors[primitive.attributes.POSITION];
  3502. count = positions.count;
  3503. offset = 0;
  3504. }
  3505. // Update model triangle count using number of indices
  3506. model._trianglesLength += triangleCountFromPrimitiveIndices(
  3507. primitive,
  3508. count
  3509. );
  3510. if (primitive.mode === PrimitiveType.POINTS) {
  3511. model._pointsLength += count;
  3512. }
  3513. const um = uniformMaps[primitive.material];
  3514. let uniformMap = um.uniformMap;
  3515. if (defined(um.jointMatrixUniformName)) {
  3516. const jointUniformMap = {};
  3517. jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction(
  3518. runtimeNode
  3519. );
  3520. uniformMap = combine(uniformMap, jointUniformMap);
  3521. }
  3522. if (defined(um.morphWeightsUniformName)) {
  3523. const morphWeightsUniformMap = {};
  3524. morphWeightsUniformMap[
  3525. um.morphWeightsUniformName
  3526. ] = createMorphWeightsFunction(runtimeNode);
  3527. uniformMap = combine(uniformMap, morphWeightsUniformMap);
  3528. }
  3529. uniformMap = combine(uniformMap, {
  3530. gltf_color: createColorFunction(model),
  3531. gltf_colorBlend: createColorBlendFunction(model),
  3532. gltf_clippingPlanes: createClippingPlanesFunction(model),
  3533. gltf_clippingPlanesEdgeStyle: createClippingPlanesEdgeStyleFunction(
  3534. model
  3535. ),
  3536. gltf_clippingPlanesMatrix: createClippingPlanesMatrixFunction(model),
  3537. gltf_iblReferenceFrameMatrix: createIBLReferenceFrameMatrixFunction(
  3538. model
  3539. ),
  3540. gltf_iblFactor: createIBLFactorFunction(model),
  3541. gltf_lightColor: createLightColorFunction(model),
  3542. gltf_sphericalHarmonicCoefficients: createSphericalHarmonicCoefficientsFunction(
  3543. model
  3544. ),
  3545. gltf_specularMap: createSpecularEnvironmentMapFunction(model),
  3546. gltf_specularMapSize: createSpecularEnvironmentMapSizeFunction(model),
  3547. gltf_maxSpecularLOD: createSpecularEnvironmentMapLOD(model),
  3548. gltf_luminanceAtZenith: createLuminanceAtZenithFunction(model),
  3549. });
  3550. Splitter.addUniforms(model, uniformMap);
  3551. // Allow callback to modify the uniformMap
  3552. if (defined(model._uniformMapLoaded)) {
  3553. uniformMap = model._uniformMapLoaded(uniformMap, programId, runtimeNode);
  3554. }
  3555. // Add uniforms for decoding quantized attributes if used
  3556. let quantizedUniformMap = {};
  3557. if (model.extensionsUsed.WEB3D_quantized_attributes) {
  3558. quantizedUniformMap = createUniformsForQuantizedAttributes(
  3559. model,
  3560. primitive
  3561. );
  3562. } else if (model._dequantizeInShader && defined(decodedData)) {
  3563. quantizedUniformMap = createUniformsForDracoQuantizedAttributes(
  3564. decodedData
  3565. );
  3566. }
  3567. uniformMap = combine(uniformMap, quantizedUniformMap);
  3568. const rs = rendererRenderStates[primitive.material];
  3569. const isTranslucent = rs.blending.enabled;
  3570. let owner = model._pickObject;
  3571. if (!defined(owner)) {
  3572. owner = {
  3573. primitive: model,
  3574. id: model.id,
  3575. node: runtimeNode.publicNode,
  3576. mesh: runtimeMeshesByName[mesh.name],
  3577. };
  3578. }
  3579. const castShadows = ShadowMode.castShadows(model._shadows);
  3580. const receiveShadows = ShadowMode.receiveShadows(model._shadows);
  3581. let pickId;
  3582. if (allowPicking && !defined(model._uniformMapLoaded)) {
  3583. pickId = context.createPickId(owner);
  3584. pickIds.push(pickId);
  3585. const pickUniforms = {
  3586. czm_pickColor: createPickColorFunction(pickId.color),
  3587. };
  3588. uniformMap = combine(uniformMap, pickUniforms);
  3589. }
  3590. if (allowPicking) {
  3591. if (defined(model._pickIdLoaded) && defined(model._uniformMapLoaded)) {
  3592. pickId = model._pickIdLoaded();
  3593. } else {
  3594. pickId = "czm_pickColor";
  3595. }
  3596. }
  3597. const command = new DrawCommand({
  3598. boundingVolume: new BoundingSphere(), // updated in update()
  3599. cull: model.cull,
  3600. modelMatrix: new Matrix4(), // computed in update()
  3601. primitiveType: primitive.mode,
  3602. vertexArray: vertexArray,
  3603. count: count,
  3604. offset: offset,
  3605. shaderProgram: rendererPrograms[programId],
  3606. castShadows: castShadows,
  3607. receiveShadows: receiveShadows,
  3608. uniformMap: uniformMap,
  3609. renderState: rs,
  3610. owner: owner,
  3611. pass: isTranslucent ? Pass.TRANSLUCENT : model.opaquePass,
  3612. pickId: pickId,
  3613. });
  3614. let command2D;
  3615. if (!scene3DOnly) {
  3616. command2D = DrawCommand.shallowClone(command);
  3617. command2D.boundingVolume = new BoundingSphere(); // updated in update()
  3618. command2D.modelMatrix = new Matrix4(); // updated in update()
  3619. }
  3620. const nodeCommand = {
  3621. show: true,
  3622. boundingSphere: boundingSphere,
  3623. command: command,
  3624. command2D: command2D,
  3625. // Generated on demand when silhouette size is greater than 0.0 and silhouette alpha is greater than 0.0
  3626. silhouetteModelCommand: undefined,
  3627. silhouetteModelCommand2D: undefined,
  3628. silhouetteColorCommand: undefined,
  3629. silhouetteColorCommand2D: undefined,
  3630. // Generated on demand when color alpha is less than 1.0
  3631. translucentCommand: undefined,
  3632. translucentCommand2D: undefined,
  3633. // Generated on demand when back face culling is false
  3634. disableCullingCommand: undefined,
  3635. disableCullingCommand2D: undefined,
  3636. // For updating node commands on shader reconstruction
  3637. programId: programId,
  3638. };
  3639. runtimeNode.commands.push(nodeCommand);
  3640. nodeCommands.push(nodeCommand);
  3641. }
  3642. }
  3643. function createRuntimeNodes(model, context, scene3DOnly) {
  3644. const loadResources = model._loadResources;
  3645. if (!loadResources.finishedEverythingButTextureCreation()) {
  3646. return;
  3647. }
  3648. if (!loadResources.createRuntimeNodes) {
  3649. return;
  3650. }
  3651. loadResources.createRuntimeNodes = false;
  3652. const rootNodes = [];
  3653. const runtimeNodes = model._runtime.nodes;
  3654. const gltf = model.gltf;
  3655. const nodes = gltf.nodes;
  3656. const scene = gltf.scenes[gltf.scene];
  3657. const sceneNodes = scene.nodes;
  3658. const length = sceneNodes.length;
  3659. const stack = [];
  3660. const seen = {};
  3661. for (let i = 0; i < length; ++i) {
  3662. stack.push({
  3663. parentRuntimeNode: undefined,
  3664. gltfNode: nodes[sceneNodes[i]],
  3665. id: sceneNodes[i],
  3666. });
  3667. while (stack.length > 0) {
  3668. const n = stack.pop();
  3669. seen[n.id] = true;
  3670. const parentRuntimeNode = n.parentRuntimeNode;
  3671. const gltfNode = n.gltfNode;
  3672. // Node hierarchy is a DAG so a node can have more than one parent so it may already exist
  3673. const runtimeNode = runtimeNodes[n.id];
  3674. if (runtimeNode.parents.length === 0) {
  3675. if (defined(gltfNode.matrix)) {
  3676. runtimeNode.matrix = Matrix4.fromColumnMajorArray(gltfNode.matrix);
  3677. } else {
  3678. // TRS converted to Cesium types
  3679. const rotation = gltfNode.rotation;
  3680. runtimeNode.translation = Cartesian3.fromArray(gltfNode.translation);
  3681. runtimeNode.rotation = Quaternion.unpack(rotation);
  3682. runtimeNode.scale = Cartesian3.fromArray(gltfNode.scale);
  3683. }
  3684. }
  3685. if (defined(parentRuntimeNode)) {
  3686. parentRuntimeNode.children.push(runtimeNode);
  3687. runtimeNode.parents.push(parentRuntimeNode);
  3688. } else {
  3689. rootNodes.push(runtimeNode);
  3690. }
  3691. if (defined(gltfNode.mesh)) {
  3692. createCommand(model, gltfNode, runtimeNode, context, scene3DOnly);
  3693. }
  3694. const children = gltfNode.children;
  3695. if (defined(children)) {
  3696. const childrenLength = children.length;
  3697. for (let j = 0; j < childrenLength; j++) {
  3698. const childId = children[j];
  3699. if (!seen[childId]) {
  3700. stack.push({
  3701. parentRuntimeNode: runtimeNode,
  3702. gltfNode: nodes[childId],
  3703. id: children[j],
  3704. });
  3705. }
  3706. }
  3707. }
  3708. }
  3709. }
  3710. model._runtime.rootNodes = rootNodes;
  3711. model._runtime.nodes = runtimeNodes;
  3712. }
  3713. function getGeometryByteLength(buffers) {
  3714. let memory = 0;
  3715. for (const id in buffers) {
  3716. if (buffers.hasOwnProperty(id)) {
  3717. memory += buffers[id].sizeInBytes;
  3718. }
  3719. }
  3720. return memory;
  3721. }
  3722. function getTexturesByteLength(textures) {
  3723. let memory = 0;
  3724. for (const id in textures) {
  3725. if (textures.hasOwnProperty(id)) {
  3726. memory += textures[id].sizeInBytes;
  3727. }
  3728. }
  3729. return memory;
  3730. }
  3731. function createResources(model, frameState) {
  3732. const context = frameState.context;
  3733. const scene3DOnly = frameState.scene3DOnly;
  3734. const quantizedVertexShaders = model._quantizedVertexShaders;
  3735. const techniques = model._sourceTechniques;
  3736. const programs = model._sourcePrograms;
  3737. const resources = model._rendererResources;
  3738. let shaders = resources.sourceShaders;
  3739. if (model._loadRendererResourcesFromCache) {
  3740. shaders = resources.sourceShaders =
  3741. model._cachedRendererResources.sourceShaders;
  3742. }
  3743. for (const techniqueId in techniques) {
  3744. if (techniques.hasOwnProperty(techniqueId)) {
  3745. const programId = techniques[techniqueId].program;
  3746. const program = programs[programId];
  3747. let shader = shaders[program.vertexShader];
  3748. ModelUtility.checkSupportedGlExtensions(program.glExtensions, context);
  3749. if (
  3750. model.extensionsUsed.WEB3D_quantized_attributes ||
  3751. model._dequantizeInShader
  3752. ) {
  3753. let quantizedVS = quantizedVertexShaders[programId];
  3754. if (!defined(quantizedVS)) {
  3755. quantizedVS = modifyShaderForQuantizedAttributes(
  3756. shader,
  3757. programId,
  3758. model
  3759. );
  3760. quantizedVertexShaders[programId] = quantizedVS;
  3761. }
  3762. shader = quantizedVS;
  3763. }
  3764. shader = modifyShader(shader, programId, model._vertexShaderLoaded);
  3765. }
  3766. }
  3767. if (model._loadRendererResourcesFromCache) {
  3768. const cachedResources = model._cachedRendererResources;
  3769. resources.buffers = cachedResources.buffers;
  3770. resources.vertexArrays = cachedResources.vertexArrays;
  3771. resources.programs = cachedResources.programs;
  3772. resources.silhouettePrograms = cachedResources.silhouettePrograms;
  3773. resources.textures = cachedResources.textures;
  3774. resources.samplers = cachedResources.samplers;
  3775. resources.renderStates = cachedResources.renderStates;
  3776. // Vertex arrays are unique to this model, create instead of using the cache.
  3777. if (defined(model._precreatedAttributes)) {
  3778. createVertexArrays(model, context);
  3779. }
  3780. model._cachedGeometryByteLength += getGeometryByteLength(
  3781. cachedResources.buffers
  3782. );
  3783. model._cachedTexturesByteLength += getTexturesByteLength(
  3784. cachedResources.textures
  3785. );
  3786. } else {
  3787. createBuffers(model, frameState); // using glTF bufferViews
  3788. createPrograms(model, frameState);
  3789. createSamplers(model, context);
  3790. loadTexturesFromBufferViews(model);
  3791. createTextures(model, frameState);
  3792. }
  3793. createSkins(model);
  3794. createRuntimeAnimations(model);
  3795. if (!model._loadRendererResourcesFromCache) {
  3796. createVertexArrays(model, context); // using glTF meshes
  3797. createRenderStates(model); // using glTF materials/techniques/states
  3798. // Long-term, we might not cache render states if they could change
  3799. // due to an animation, e.g., a uniform going from opaque to transparent.
  3800. // Could use copy-on-write if it is worth it. Probably overkill.
  3801. }
  3802. createUniformMaps(model, context); // using glTF materials/techniques
  3803. createRuntimeNodes(model, context, scene3DOnly); // using glTF scene
  3804. }
  3805. ///////////////////////////////////////////////////////////////////////////
  3806. function getNodeMatrix(node, result) {
  3807. const publicNode = node.publicNode;
  3808. const publicMatrix = publicNode.matrix;
  3809. if (publicNode.useMatrix && defined(publicMatrix)) {
  3810. // Public matrix overrides original glTF matrix and glTF animations
  3811. Matrix4.clone(publicMatrix, result);
  3812. } else if (defined(node.matrix)) {
  3813. Matrix4.clone(node.matrix, result);
  3814. } else {
  3815. Matrix4.fromTranslationQuaternionRotationScale(
  3816. node.translation,
  3817. node.rotation,
  3818. node.scale,
  3819. result
  3820. );
  3821. // Keep matrix returned by the node in-sync if the node is targeted by an animation. Only TRS nodes can be targeted.
  3822. publicNode.setMatrix(result);
  3823. }
  3824. }
  3825. const scratchNodeStack = [];
  3826. const scratchComputedTranslation = new Cartesian4();
  3827. const scratchComputedMatrixIn2D = new Matrix4();
  3828. function updateNodeHierarchyModelMatrix(
  3829. model,
  3830. modelTransformChanged,
  3831. justLoaded,
  3832. projection
  3833. ) {
  3834. const maxDirtyNumber = model._maxDirtyNumber;
  3835. const rootNodes = model._runtime.rootNodes;
  3836. const length = rootNodes.length;
  3837. const nodeStack = scratchNodeStack;
  3838. let computedModelMatrix = model._computedModelMatrix;
  3839. if (model._mode !== SceneMode.SCENE3D && !model._ignoreCommands) {
  3840. const translation = Matrix4.getColumn(
  3841. computedModelMatrix,
  3842. 3,
  3843. scratchComputedTranslation
  3844. );
  3845. if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {
  3846. computedModelMatrix = Transforms.basisTo2D(
  3847. projection,
  3848. computedModelMatrix,
  3849. scratchComputedMatrixIn2D
  3850. );
  3851. model._rtcCenter = model._rtcCenter3D;
  3852. } else {
  3853. const center = model.boundingSphere.center;
  3854. const to2D = Transforms.wgs84To2DModelMatrix(
  3855. projection,
  3856. center,
  3857. scratchComputedMatrixIn2D
  3858. );
  3859. computedModelMatrix = Matrix4.multiply(
  3860. to2D,
  3861. computedModelMatrix,
  3862. scratchComputedMatrixIn2D
  3863. );
  3864. if (defined(model._rtcCenter)) {
  3865. Matrix4.setTranslation(
  3866. computedModelMatrix,
  3867. Cartesian4.UNIT_W,
  3868. computedModelMatrix
  3869. );
  3870. model._rtcCenter = model._rtcCenter2D;
  3871. }
  3872. }
  3873. }
  3874. for (let i = 0; i < length; ++i) {
  3875. let n = rootNodes[i];
  3876. getNodeMatrix(n, n.transformToRoot);
  3877. nodeStack.push(n);
  3878. while (nodeStack.length > 0) {
  3879. n = nodeStack.pop();
  3880. const transformToRoot = n.transformToRoot;
  3881. const commands = n.commands;
  3882. if (
  3883. n.dirtyNumber === maxDirtyNumber ||
  3884. modelTransformChanged ||
  3885. justLoaded
  3886. ) {
  3887. const nodeMatrix = Matrix4.multiplyTransformation(
  3888. computedModelMatrix,
  3889. transformToRoot,
  3890. n.computedMatrix
  3891. );
  3892. const commandsLength = commands.length;
  3893. if (commandsLength > 0) {
  3894. // Node has meshes, which has primitives. Update their commands.
  3895. for (let j = 0; j < commandsLength; ++j) {
  3896. const primitiveCommand = commands[j];
  3897. let command = primitiveCommand.command;
  3898. Matrix4.clone(nodeMatrix, command.modelMatrix);
  3899. // PERFORMANCE_IDEA: Can use transformWithoutScale if no node up to the root has scale (including animation)
  3900. BoundingSphere.transform(
  3901. primitiveCommand.boundingSphere,
  3902. command.modelMatrix,
  3903. command.boundingVolume
  3904. );
  3905. if (defined(model._rtcCenter)) {
  3906. Cartesian3.add(
  3907. model._rtcCenter,
  3908. command.boundingVolume.center,
  3909. command.boundingVolume.center
  3910. );
  3911. }
  3912. // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it
  3913. // will be clipped by the viewport. We create a second command that translates the model
  3914. // model matrix to the opposite side of the map so the part that was clipped in one viewport
  3915. // is drawn in the other.
  3916. command = primitiveCommand.command2D;
  3917. if (defined(command) && model._mode === SceneMode.SCENE2D) {
  3918. Matrix4.clone(nodeMatrix, command.modelMatrix);
  3919. command.modelMatrix[13] -=
  3920. CesiumMath.sign(command.modelMatrix[13]) *
  3921. 2.0 *
  3922. CesiumMath.PI *
  3923. projection.ellipsoid.maximumRadius;
  3924. BoundingSphere.transform(
  3925. primitiveCommand.boundingSphere,
  3926. command.modelMatrix,
  3927. command.boundingVolume
  3928. );
  3929. }
  3930. }
  3931. }
  3932. }
  3933. const children = n.children;
  3934. if (defined(children)) {
  3935. const childrenLength = children.length;
  3936. for (let k = 0; k < childrenLength; ++k) {
  3937. const child = children[k];
  3938. // A node's transform needs to be updated if
  3939. // - It was targeted for animation this frame, or
  3940. // - Any of its ancestors were targeted for animation this frame
  3941. // PERFORMANCE_IDEA: if a child has multiple parents and only one of the parents
  3942. // is dirty, all the subtrees for each child instance will be dirty; we probably
  3943. // won't see this in the wild often.
  3944. child.dirtyNumber = Math.max(child.dirtyNumber, n.dirtyNumber);
  3945. if (child.dirtyNumber === maxDirtyNumber || justLoaded) {
  3946. // Don't check for modelTransformChanged since if only the model's model matrix changed,
  3947. // we do not need to rebuild the local transform-to-root, only the final
  3948. // [model's-model-matrix][transform-to-root] above.
  3949. getNodeMatrix(child, child.transformToRoot);
  3950. Matrix4.multiplyTransformation(
  3951. transformToRoot,
  3952. child.transformToRoot,
  3953. child.transformToRoot
  3954. );
  3955. }
  3956. nodeStack.push(child);
  3957. }
  3958. }
  3959. }
  3960. }
  3961. ++model._maxDirtyNumber;
  3962. }
  3963. let scratchObjectSpace = new Matrix4();
  3964. function applySkins(model) {
  3965. const skinnedNodes = model._runtime.skinnedNodes;
  3966. const length = skinnedNodes.length;
  3967. for (let i = 0; i < length; ++i) {
  3968. const node = skinnedNodes[i];
  3969. scratchObjectSpace = Matrix4.inverseTransformation(
  3970. node.transformToRoot,
  3971. scratchObjectSpace
  3972. );
  3973. const computedJointMatrices = node.computedJointMatrices;
  3974. const joints = node.joints;
  3975. const bindShapeMatrix = node.bindShapeMatrix;
  3976. const inverseBindMatrices = node.inverseBindMatrices;
  3977. const inverseBindMatricesLength = inverseBindMatrices.length;
  3978. for (let m = 0; m < inverseBindMatricesLength; ++m) {
  3979. // [joint-matrix] = [node-to-root^-1][joint-to-root][inverse-bind][bind-shape]
  3980. if (!defined(computedJointMatrices[m])) {
  3981. computedJointMatrices[m] = new Matrix4();
  3982. }
  3983. computedJointMatrices[m] = Matrix4.multiplyTransformation(
  3984. scratchObjectSpace,
  3985. joints[m].transformToRoot,
  3986. computedJointMatrices[m]
  3987. );
  3988. computedJointMatrices[m] = Matrix4.multiplyTransformation(
  3989. computedJointMatrices[m],
  3990. inverseBindMatrices[m],
  3991. computedJointMatrices[m]
  3992. );
  3993. if (defined(bindShapeMatrix)) {
  3994. // NOTE: bindShapeMatrix is glTF 1.0 only, removed in glTF 2.0.
  3995. computedJointMatrices[m] = Matrix4.multiplyTransformation(
  3996. computedJointMatrices[m],
  3997. bindShapeMatrix,
  3998. computedJointMatrices[m]
  3999. );
  4000. }
  4001. }
  4002. }
  4003. }
  4004. function updatePerNodeShow(model) {
  4005. // Totally not worth it, but we could optimize this:
  4006. // http://help.agi.com/AGIComponents/html/BlogDeletionInBoundingVolumeHierarchies.htm
  4007. const rootNodes = model._runtime.rootNodes;
  4008. const length = rootNodes.length;
  4009. const nodeStack = scratchNodeStack;
  4010. for (let i = 0; i < length; ++i) {
  4011. let n = rootNodes[i];
  4012. n.computedShow = n.publicNode.show;
  4013. nodeStack.push(n);
  4014. while (nodeStack.length > 0) {
  4015. n = nodeStack.pop();
  4016. const show = n.computedShow;
  4017. const nodeCommands = n.commands;
  4018. const nodeCommandsLength = nodeCommands.length;
  4019. for (let j = 0; j < nodeCommandsLength; ++j) {
  4020. nodeCommands[j].show = show;
  4021. }
  4022. // if commandsLength is zero, the node has a light or camera
  4023. const children = n.children;
  4024. if (defined(children)) {
  4025. const childrenLength = children.length;
  4026. for (let k = 0; k < childrenLength; ++k) {
  4027. const child = children[k];
  4028. // Parent needs to be shown for child to be shown.
  4029. child.computedShow = show && child.publicNode.show;
  4030. nodeStack.push(child);
  4031. }
  4032. }
  4033. }
  4034. }
  4035. }
  4036. function updatePickIds(model, context) {
  4037. const id = model.id;
  4038. if (model._id !== id) {
  4039. model._id = id;
  4040. const pickIds = model._pickIds;
  4041. const length = pickIds.length;
  4042. for (let i = 0; i < length; ++i) {
  4043. pickIds[i].object.id = id;
  4044. }
  4045. }
  4046. }
  4047. function updateWireframe(model) {
  4048. if (model._debugWireframe !== model.debugWireframe) {
  4049. model._debugWireframe = model.debugWireframe;
  4050. // This assumes the original primitive was TRIANGLES and that the triangles
  4051. // are connected for the wireframe to look perfect.
  4052. const primitiveType = model.debugWireframe
  4053. ? PrimitiveType.LINES
  4054. : PrimitiveType.TRIANGLES;
  4055. const nodeCommands = model._nodeCommands;
  4056. const length = nodeCommands.length;
  4057. for (let i = 0; i < length; ++i) {
  4058. nodeCommands[i].command.primitiveType = primitiveType;
  4059. }
  4060. }
  4061. }
  4062. function updateShowBoundingVolume(model) {
  4063. if (model.debugShowBoundingVolume !== model._debugShowBoundingVolume) {
  4064. model._debugShowBoundingVolume = model.debugShowBoundingVolume;
  4065. const debugShowBoundingVolume = model.debugShowBoundingVolume;
  4066. const nodeCommands = model._nodeCommands;
  4067. const length = nodeCommands.length;
  4068. for (let i = 0; i < length; ++i) {
  4069. nodeCommands[i].command.debugShowBoundingVolume = debugShowBoundingVolume;
  4070. }
  4071. }
  4072. }
  4073. function updateShadows(model) {
  4074. if (model.shadows !== model._shadows) {
  4075. model._shadows = model.shadows;
  4076. const castShadows = ShadowMode.castShadows(model.shadows);
  4077. const receiveShadows = ShadowMode.receiveShadows(model.shadows);
  4078. const nodeCommands = model._nodeCommands;
  4079. const length = nodeCommands.length;
  4080. for (let i = 0; i < length; i++) {
  4081. const nodeCommand = nodeCommands[i];
  4082. nodeCommand.command.castShadows = castShadows;
  4083. nodeCommand.command.receiveShadows = receiveShadows;
  4084. }
  4085. }
  4086. }
  4087. function getTranslucentRenderState(model, renderState) {
  4088. const rs = clone(renderState, true);
  4089. rs.cull.enabled = false;
  4090. rs.depthTest.enabled = true;
  4091. rs.depthMask = false;
  4092. rs.blending = BlendingState.ALPHA_BLEND;
  4093. if (model.opaquePass === Pass.CESIUM_3D_TILE) {
  4094. rs.stencilTest = StencilConstants.setCesium3DTileBit();
  4095. rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
  4096. }
  4097. return RenderState.fromCache(rs);
  4098. }
  4099. function deriveTranslucentCommand(model, command) {
  4100. const translucentCommand = DrawCommand.shallowClone(command);
  4101. translucentCommand.pass = Pass.TRANSLUCENT;
  4102. translucentCommand.renderState = getTranslucentRenderState(
  4103. model,
  4104. command.renderState
  4105. );
  4106. return translucentCommand;
  4107. }
  4108. function updateColor(model, frameState, forceDerive) {
  4109. // Generate translucent commands when the blend color has an alpha in the range (0.0, 1.0) exclusive
  4110. const scene3DOnly = frameState.scene3DOnly;
  4111. const alpha = model.color.alpha;
  4112. if (alpha > 0.0 && alpha < 1.0) {
  4113. const nodeCommands = model._nodeCommands;
  4114. const length = nodeCommands.length;
  4115. if (
  4116. length > 0 &&
  4117. (!defined(nodeCommands[0].translucentCommand) || forceDerive)
  4118. ) {
  4119. for (let i = 0; i < length; ++i) {
  4120. const nodeCommand = nodeCommands[i];
  4121. const command = nodeCommand.command;
  4122. nodeCommand.translucentCommand = deriveTranslucentCommand(
  4123. model,
  4124. command
  4125. );
  4126. if (!scene3DOnly) {
  4127. const command2D = nodeCommand.command2D;
  4128. nodeCommand.translucentCommand2D = deriveTranslucentCommand(
  4129. model,
  4130. command2D
  4131. );
  4132. }
  4133. }
  4134. }
  4135. }
  4136. }
  4137. function getDisableCullingRenderState(renderState) {
  4138. const rs = clone(renderState, true);
  4139. rs.cull.enabled = false;
  4140. return RenderState.fromCache(rs);
  4141. }
  4142. function deriveDisableCullingCommand(command) {
  4143. const disableCullingCommand = DrawCommand.shallowClone(command);
  4144. disableCullingCommand.renderState = getDisableCullingRenderState(
  4145. command.renderState
  4146. );
  4147. return disableCullingCommand;
  4148. }
  4149. function updateBackFaceCulling(model, frameState, forceDerive) {
  4150. const scene3DOnly = frameState.scene3DOnly;
  4151. const backFaceCulling = model.backFaceCulling;
  4152. if (!backFaceCulling) {
  4153. const nodeCommands = model._nodeCommands;
  4154. const length = nodeCommands.length;
  4155. if (
  4156. length > 0 &&
  4157. (!defined(nodeCommands[0].disableCullingCommand) || forceDerive)
  4158. ) {
  4159. for (let i = 0; i < length; ++i) {
  4160. const nodeCommand = nodeCommands[i];
  4161. const command = nodeCommand.command;
  4162. nodeCommand.disableCullingCommand = deriveDisableCullingCommand(
  4163. command
  4164. );
  4165. if (!scene3DOnly) {
  4166. const command2D = nodeCommand.command2D;
  4167. nodeCommand.disableCullingCommand2D = deriveDisableCullingCommand(
  4168. command2D
  4169. );
  4170. }
  4171. }
  4172. }
  4173. }
  4174. }
  4175. function getProgramId(model, program) {
  4176. const programs = model._rendererResources.programs;
  4177. for (const id in programs) {
  4178. if (programs.hasOwnProperty(id)) {
  4179. if (programs[id] === program) {
  4180. return id;
  4181. }
  4182. }
  4183. }
  4184. }
  4185. function createSilhouetteProgram(model, program, frameState) {
  4186. let vs = program.vertexShaderSource.sources[0];
  4187. const attributeLocations = program._attributeLocations;
  4188. const normalAttributeName = model._normalAttributeName;
  4189. // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/
  4190. vs = ShaderSource.replaceMain(vs, "gltf_silhouette_main");
  4191. vs +=
  4192. `${
  4193. "uniform float gltf_silhouetteSize; \n" +
  4194. "void main() \n" +
  4195. "{ \n" +
  4196. " gltf_silhouette_main(); \n" +
  4197. " vec3 n = normalize(czm_normal3D * "
  4198. }${normalAttributeName}); \n` +
  4199. ` n.x *= czm_projection[0][0]; \n` +
  4200. ` n.y *= czm_projection[1][1]; \n` +
  4201. ` vec4 clip = gl_Position; \n` +
  4202. ` clip.xy += n.xy * clip.w * gltf_silhouetteSize * czm_pixelRatio / czm_viewport.z; \n` +
  4203. ` gl_Position = clip; \n` +
  4204. `}`;
  4205. const fs =
  4206. "uniform vec4 gltf_silhouetteColor; \n" +
  4207. "void main() \n" +
  4208. "{ \n" +
  4209. " gl_FragColor = czm_gammaCorrect(gltf_silhouetteColor); \n" +
  4210. "}";
  4211. return ShaderProgram.fromCache({
  4212. context: frameState.context,
  4213. vertexShaderSource: vs,
  4214. fragmentShaderSource: fs,
  4215. attributeLocations: attributeLocations,
  4216. });
  4217. }
  4218. function hasSilhouette(model, frameState) {
  4219. return (
  4220. silhouetteSupported(frameState.context) &&
  4221. model.silhouetteSize > 0.0 &&
  4222. model.silhouetteColor.alpha > 0.0 &&
  4223. defined(model._normalAttributeName)
  4224. );
  4225. }
  4226. function hasTranslucentCommands(model) {
  4227. const nodeCommands = model._nodeCommands;
  4228. const length = nodeCommands.length;
  4229. for (let i = 0; i < length; ++i) {
  4230. const nodeCommand = nodeCommands[i];
  4231. const command = nodeCommand.command;
  4232. if (command.pass === Pass.TRANSLUCENT) {
  4233. return true;
  4234. }
  4235. }
  4236. return false;
  4237. }
  4238. function isTranslucent(model) {
  4239. return model.color.alpha > 0.0 && model.color.alpha < 1.0;
  4240. }
  4241. function isInvisible(model) {
  4242. return model.color.alpha === 0.0;
  4243. }
  4244. function alphaDirty(currAlpha, prevAlpha) {
  4245. // Returns whether the alpha state has changed between invisible, translucent, or opaque
  4246. return (
  4247. Math.floor(currAlpha) !== Math.floor(prevAlpha) ||
  4248. Math.ceil(currAlpha) !== Math.ceil(prevAlpha)
  4249. );
  4250. }
  4251. let silhouettesLength = 0;
  4252. function createSilhouetteCommands(model, frameState) {
  4253. // Wrap around after exceeding the 8-bit stencil limit.
  4254. // The reference is unique to each model until this point.
  4255. const stencilReference = ++silhouettesLength % 255;
  4256. // If the model is translucent the silhouette needs to be in the translucent pass.
  4257. // Otherwise the silhouette would be rendered before the model.
  4258. const silhouetteTranslucent =
  4259. hasTranslucentCommands(model) ||
  4260. isTranslucent(model) ||
  4261. model.silhouetteColor.alpha < 1.0;
  4262. const silhouettePrograms = model._rendererResources.silhouettePrograms;
  4263. const scene3DOnly = frameState.scene3DOnly;
  4264. const nodeCommands = model._nodeCommands;
  4265. const length = nodeCommands.length;
  4266. for (let i = 0; i < length; ++i) {
  4267. const nodeCommand = nodeCommands[i];
  4268. const command = nodeCommand.command;
  4269. // Create model command
  4270. const modelCommand = isTranslucent(model)
  4271. ? nodeCommand.translucentCommand
  4272. : command;
  4273. const silhouetteModelCommand = DrawCommand.shallowClone(modelCommand);
  4274. let renderState = clone(modelCommand.renderState);
  4275. // Write the reference value into the stencil buffer.
  4276. renderState.stencilTest = {
  4277. enabled: true,
  4278. frontFunction: WebGLConstants.ALWAYS,
  4279. backFunction: WebGLConstants.ALWAYS,
  4280. reference: stencilReference,
  4281. mask: ~0,
  4282. frontOperation: {
  4283. fail: WebGLConstants.KEEP,
  4284. zFail: WebGLConstants.KEEP,
  4285. zPass: WebGLConstants.REPLACE,
  4286. },
  4287. backOperation: {
  4288. fail: WebGLConstants.KEEP,
  4289. zFail: WebGLConstants.KEEP,
  4290. zPass: WebGLConstants.REPLACE,
  4291. },
  4292. };
  4293. if (isInvisible(model)) {
  4294. // When the model is invisible disable color and depth writes but still write into the stencil buffer
  4295. renderState.colorMask = {
  4296. red: false,
  4297. green: false,
  4298. blue: false,
  4299. alpha: false,
  4300. };
  4301. renderState.depthMask = false;
  4302. }
  4303. renderState = RenderState.fromCache(renderState);
  4304. silhouetteModelCommand.renderState = renderState;
  4305. nodeCommand.silhouetteModelCommand = silhouetteModelCommand;
  4306. // Create color command
  4307. const silhouetteColorCommand = DrawCommand.shallowClone(command);
  4308. renderState = clone(command.renderState, true);
  4309. renderState.depthTest.enabled = true;
  4310. renderState.cull.enabled = false;
  4311. if (silhouetteTranslucent) {
  4312. silhouetteColorCommand.pass = Pass.TRANSLUCENT;
  4313. renderState.depthMask = false;
  4314. renderState.blending = BlendingState.ALPHA_BLEND;
  4315. }
  4316. // Only render silhouette if the value in the stencil buffer equals the reference
  4317. renderState.stencilTest = {
  4318. enabled: true,
  4319. frontFunction: WebGLConstants.NOTEQUAL,
  4320. backFunction: WebGLConstants.NOTEQUAL,
  4321. reference: stencilReference,
  4322. mask: ~0,
  4323. frontOperation: {
  4324. fail: WebGLConstants.KEEP,
  4325. zFail: WebGLConstants.KEEP,
  4326. zPass: WebGLConstants.KEEP,
  4327. },
  4328. backOperation: {
  4329. fail: WebGLConstants.KEEP,
  4330. zFail: WebGLConstants.KEEP,
  4331. zPass: WebGLConstants.KEEP,
  4332. },
  4333. };
  4334. renderState = RenderState.fromCache(renderState);
  4335. // If the silhouette program has already been cached use it
  4336. const program = command.shaderProgram;
  4337. const id = getProgramId(model, program);
  4338. let silhouetteProgram = silhouettePrograms[id];
  4339. if (!defined(silhouetteProgram)) {
  4340. silhouetteProgram = createSilhouetteProgram(model, program, frameState);
  4341. silhouettePrograms[id] = silhouetteProgram;
  4342. }
  4343. const silhouetteUniformMap = combine(command.uniformMap, {
  4344. gltf_silhouetteColor: createSilhouetteColorFunction(model),
  4345. gltf_silhouetteSize: createSilhouetteSizeFunction(model),
  4346. });
  4347. silhouetteColorCommand.renderState = renderState;
  4348. silhouetteColorCommand.shaderProgram = silhouetteProgram;
  4349. silhouetteColorCommand.uniformMap = silhouetteUniformMap;
  4350. silhouetteColorCommand.castShadows = false;
  4351. silhouetteColorCommand.receiveShadows = false;
  4352. nodeCommand.silhouetteColorCommand = silhouetteColorCommand;
  4353. if (!scene3DOnly) {
  4354. const command2D = nodeCommand.command2D;
  4355. const silhouetteModelCommand2D = DrawCommand.shallowClone(
  4356. silhouetteModelCommand
  4357. );
  4358. silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume;
  4359. silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix;
  4360. nodeCommand.silhouetteModelCommand2D = silhouetteModelCommand2D;
  4361. const silhouetteColorCommand2D = DrawCommand.shallowClone(
  4362. silhouetteColorCommand
  4363. );
  4364. silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume;
  4365. silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix;
  4366. nodeCommand.silhouetteColorCommand2D = silhouetteColorCommand2D;
  4367. }
  4368. }
  4369. }
  4370. function modifyShaderForClippingPlanes(
  4371. shader,
  4372. clippingPlaneCollection,
  4373. context
  4374. ) {
  4375. shader = ShaderSource.replaceMain(shader, "gltf_clip_main");
  4376. shader += `${Model._getClippingFunction(clippingPlaneCollection, context)}\n`;
  4377. shader += `${
  4378. "uniform highp sampler2D gltf_clippingPlanes; \n" +
  4379. "uniform mat4 gltf_clippingPlanesMatrix; \n" +
  4380. "uniform vec4 gltf_clippingPlanesEdgeStyle; \n" +
  4381. "void main() \n" +
  4382. "{ \n" +
  4383. " gltf_clip_main(); \n"
  4384. }${getClipAndStyleCode(
  4385. "gltf_clippingPlanes",
  4386. "gltf_clippingPlanesMatrix",
  4387. "gltf_clippingPlanesEdgeStyle"
  4388. )}} \n`;
  4389. return shader;
  4390. }
  4391. function updateSilhouette(model, frameState, force) {
  4392. // Generate silhouette commands when the silhouette size is greater than 0.0 and the alpha is greater than 0.0
  4393. // There are two silhouette commands:
  4394. // 1. silhouetteModelCommand : render model normally while enabling stencil mask
  4395. // 2. silhouetteColorCommand : render enlarged model with a solid color while enabling stencil tests
  4396. if (!hasSilhouette(model, frameState)) {
  4397. return;
  4398. }
  4399. const nodeCommands = model._nodeCommands;
  4400. const dirty =
  4401. nodeCommands.length > 0 &&
  4402. (alphaDirty(model.color.alpha, model._colorPreviousAlpha) ||
  4403. alphaDirty(
  4404. model.silhouetteColor.alpha,
  4405. model._silhouetteColorPreviousAlpha
  4406. ) ||
  4407. !defined(nodeCommands[0].silhouetteModelCommand));
  4408. model._colorPreviousAlpha = model.color.alpha;
  4409. model._silhouetteColorPreviousAlpha = model.silhouetteColor.alpha;
  4410. if (dirty || force) {
  4411. createSilhouetteCommands(model, frameState);
  4412. }
  4413. }
  4414. function updateClippingPlanes(model, frameState) {
  4415. const clippingPlanes = model._clippingPlanes;
  4416. if (defined(clippingPlanes) && clippingPlanes.owner === model) {
  4417. if (clippingPlanes.enabled) {
  4418. clippingPlanes.update(frameState);
  4419. }
  4420. }
  4421. }
  4422. const scratchBoundingSphere = new BoundingSphere();
  4423. function scaleInPixels(positionWC, radius, frameState) {
  4424. scratchBoundingSphere.center = positionWC;
  4425. scratchBoundingSphere.radius = radius;
  4426. return frameState.camera.getPixelSize(
  4427. scratchBoundingSphere,
  4428. frameState.context.drawingBufferWidth,
  4429. frameState.context.drawingBufferHeight
  4430. );
  4431. }
  4432. const scratchPosition = new Cartesian3();
  4433. const scratchCartographic = new Cartographic();
  4434. function getScale(model, frameState) {
  4435. let scale = model.scale;
  4436. if (model.minimumPixelSize !== 0.0) {
  4437. // Compute size of bounding sphere in pixels
  4438. const context = frameState.context;
  4439. const maxPixelSize = Math.max(
  4440. context.drawingBufferWidth,
  4441. context.drawingBufferHeight
  4442. );
  4443. const m = defined(model._clampedModelMatrix)
  4444. ? model._clampedModelMatrix
  4445. : model.modelMatrix;
  4446. scratchPosition.x = m[12];
  4447. scratchPosition.y = m[13];
  4448. scratchPosition.z = m[14];
  4449. if (defined(model._rtcCenter)) {
  4450. Cartesian3.add(model._rtcCenter, scratchPosition, scratchPosition);
  4451. }
  4452. if (model._mode !== SceneMode.SCENE3D) {
  4453. const projection = frameState.mapProjection;
  4454. const cartographic = projection.ellipsoid.cartesianToCartographic(
  4455. scratchPosition,
  4456. scratchCartographic
  4457. );
  4458. projection.project(cartographic, scratchPosition);
  4459. Cartesian3.fromElements(
  4460. scratchPosition.z,
  4461. scratchPosition.x,
  4462. scratchPosition.y,
  4463. scratchPosition
  4464. );
  4465. }
  4466. const radius = model.boundingSphere.radius;
  4467. const metersPerPixel = scaleInPixels(scratchPosition, radius, frameState);
  4468. // metersPerPixel is always > 0.0
  4469. const pixelsPerMeter = 1.0 / metersPerPixel;
  4470. const diameterInPixels = Math.min(
  4471. pixelsPerMeter * (2.0 * radius),
  4472. maxPixelSize
  4473. );
  4474. // Maintain model's minimum pixel size
  4475. if (diameterInPixels < model.minimumPixelSize) {
  4476. scale =
  4477. (model.minimumPixelSize * metersPerPixel) /
  4478. (2.0 * model._initialRadius);
  4479. }
  4480. }
  4481. return defined(model.maximumScale)
  4482. ? Math.min(model.maximumScale, scale)
  4483. : scale;
  4484. }
  4485. function releaseCachedGltf(model) {
  4486. if (
  4487. defined(model._cacheKey) &&
  4488. defined(model._cachedGltf) &&
  4489. --model._cachedGltf.count === 0
  4490. ) {
  4491. delete gltfCache[model._cacheKey];
  4492. }
  4493. model._cachedGltf = undefined;
  4494. }
  4495. ///////////////////////////////////////////////////////////////////////////
  4496. function CachedRendererResources(context, cacheKey) {
  4497. this.buffers = undefined;
  4498. this.vertexArrays = undefined;
  4499. this.programs = undefined;
  4500. this.sourceShaders = undefined;
  4501. this.silhouettePrograms = undefined;
  4502. this.textures = undefined;
  4503. this.samplers = undefined;
  4504. this.renderStates = undefined;
  4505. this.ready = false;
  4506. this.context = context;
  4507. this.cacheKey = cacheKey;
  4508. this.count = 0;
  4509. }
  4510. function destroy(property) {
  4511. for (const name in property) {
  4512. if (property.hasOwnProperty(name)) {
  4513. property[name].destroy();
  4514. }
  4515. }
  4516. }
  4517. function destroyCachedRendererResources(resources) {
  4518. destroy(resources.buffers);
  4519. destroy(resources.vertexArrays);
  4520. destroy(resources.programs);
  4521. destroy(resources.silhouettePrograms);
  4522. destroy(resources.textures);
  4523. }
  4524. CachedRendererResources.prototype.release = function () {
  4525. if (--this.count === 0) {
  4526. if (defined(this.cacheKey)) {
  4527. // Remove if this was cached
  4528. delete this.context.cache.modelRendererResourceCache[this.cacheKey];
  4529. }
  4530. destroyCachedRendererResources(this);
  4531. return destroyObject(this);
  4532. }
  4533. return undefined;
  4534. };
  4535. ///////////////////////////////////////////////////////////////////////////
  4536. function getUpdateHeightCallback(model, ellipsoid, cartoPosition) {
  4537. return function (clampedPosition) {
  4538. if (model.heightReference === HeightReference.RELATIVE_TO_GROUND) {
  4539. const clampedCart = ellipsoid.cartesianToCartographic(
  4540. clampedPosition,
  4541. scratchCartographic
  4542. );
  4543. clampedCart.height += cartoPosition.height;
  4544. ellipsoid.cartographicToCartesian(clampedCart, clampedPosition);
  4545. }
  4546. const clampedModelMatrix = model._clampedModelMatrix;
  4547. // Modify clamped model matrix to use new height
  4548. Matrix4.clone(model.modelMatrix, clampedModelMatrix);
  4549. clampedModelMatrix[12] = clampedPosition.x;
  4550. clampedModelMatrix[13] = clampedPosition.y;
  4551. clampedModelMatrix[14] = clampedPosition.z;
  4552. model._heightChanged = true;
  4553. };
  4554. }
  4555. function updateClamping(model) {
  4556. if (defined(model._removeUpdateHeightCallback)) {
  4557. model._removeUpdateHeightCallback();
  4558. model._removeUpdateHeightCallback = undefined;
  4559. }
  4560. const scene = model._scene;
  4561. if (
  4562. !defined(scene) ||
  4563. !defined(scene.globe) ||
  4564. model.heightReference === HeightReference.NONE
  4565. ) {
  4566. //>>includeStart('debug', pragmas.debug);
  4567. if (model.heightReference !== HeightReference.NONE) {
  4568. throw new DeveloperError(
  4569. "Height reference is not supported without a scene and globe."
  4570. );
  4571. }
  4572. //>>includeEnd('debug');
  4573. model._clampedModelMatrix = undefined;
  4574. return;
  4575. }
  4576. const globe = scene.globe;
  4577. const ellipsoid = globe.ellipsoid;
  4578. // Compute cartographic position so we don't recompute every update
  4579. const modelMatrix = model.modelMatrix;
  4580. scratchPosition.x = modelMatrix[12];
  4581. scratchPosition.y = modelMatrix[13];
  4582. scratchPosition.z = modelMatrix[14];
  4583. const cartoPosition = ellipsoid.cartesianToCartographic(scratchPosition);
  4584. if (!defined(model._clampedModelMatrix)) {
  4585. model._clampedModelMatrix = Matrix4.clone(modelMatrix, new Matrix4());
  4586. }
  4587. // Install callback to handle updating of terrain tiles
  4588. const surface = globe._surface;
  4589. model._removeUpdateHeightCallback = surface.updateHeight(
  4590. cartoPosition,
  4591. getUpdateHeightCallback(model, ellipsoid, cartoPosition)
  4592. );
  4593. // Set the correct height now
  4594. const height = globe.getHeight(cartoPosition);
  4595. if (defined(height)) {
  4596. // Get callback with cartoPosition being the non-clamped position
  4597. const cb = getUpdateHeightCallback(model, ellipsoid, cartoPosition);
  4598. // Compute the clamped cartesian and call updateHeight callback
  4599. Cartographic.clone(cartoPosition, scratchCartographic);
  4600. scratchCartographic.height = height;
  4601. ellipsoid.cartographicToCartesian(scratchCartographic, scratchPosition);
  4602. cb(scratchPosition);
  4603. }
  4604. }
  4605. const scratchDisplayConditionCartesian = new Cartesian3();
  4606. const scratchDistanceDisplayConditionCartographic = new Cartographic();
  4607. function distanceDisplayConditionVisible(model, frameState) {
  4608. let distance2;
  4609. const ddc = model.distanceDisplayCondition;
  4610. const nearSquared = ddc.near * ddc.near;
  4611. const farSquared = ddc.far * ddc.far;
  4612. if (frameState.mode === SceneMode.SCENE2D) {
  4613. const frustum2DWidth =
  4614. frameState.camera.frustum.right - frameState.camera.frustum.left;
  4615. distance2 = frustum2DWidth * 0.5;
  4616. distance2 = distance2 * distance2;
  4617. } else {
  4618. // Distance to center of primitive's reference frame
  4619. let position = Matrix4.getTranslation(
  4620. model.modelMatrix,
  4621. scratchDisplayConditionCartesian
  4622. );
  4623. if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  4624. const projection = frameState.mapProjection;
  4625. const ellipsoid = projection.ellipsoid;
  4626. const cartographic = ellipsoid.cartesianToCartographic(
  4627. position,
  4628. scratchDistanceDisplayConditionCartographic
  4629. );
  4630. position = projection.project(cartographic, position);
  4631. Cartesian3.fromElements(position.z, position.x, position.y, position);
  4632. }
  4633. distance2 = Cartesian3.distanceSquared(
  4634. position,
  4635. frameState.camera.positionWC
  4636. );
  4637. }
  4638. return distance2 >= nearSquared && distance2 <= farSquared;
  4639. }
  4640. const scratchIBLReferenceFrameMatrix4 = new Matrix4();
  4641. const scratchIBLReferenceFrameMatrix3 = new Matrix3();
  4642. const scratchClippingPlanesMatrix = new Matrix4();
  4643. /**
  4644. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  4645. * get the draw commands needed to render this primitive.
  4646. * <p>
  4647. * Do not call this function directly. This is documented just to
  4648. * list the exceptions that may be propagated when the scene is rendered:
  4649. * </p>
  4650. *
  4651. * @exception {RuntimeError} Failed to load external reference.
  4652. */
  4653. Model.prototype.update = function (frameState) {
  4654. if (frameState.mode === SceneMode.MORPHING) {
  4655. return;
  4656. }
  4657. if (!FeatureDetection.supportsWebP.initialized) {
  4658. FeatureDetection.supportsWebP.initialize();
  4659. return;
  4660. }
  4661. const context = frameState.context;
  4662. this._defaultTexture = context.defaultTexture;
  4663. const supportsWebP = FeatureDetection.supportsWebP();
  4664. if (this._state === ModelState.NEEDS_LOAD && defined(this.gltf)) {
  4665. // Use renderer resources from cache instead of loading/creating them?
  4666. let cachedRendererResources;
  4667. const cacheKey = this.cacheKey;
  4668. if (defined(cacheKey)) {
  4669. // cache key given? this model will pull from or contribute to context level cache
  4670. context.cache.modelRendererResourceCache = defaultValue(
  4671. context.cache.modelRendererResourceCache,
  4672. {}
  4673. );
  4674. const modelCaches = context.cache.modelRendererResourceCache;
  4675. cachedRendererResources = modelCaches[this.cacheKey];
  4676. if (defined(cachedRendererResources)) {
  4677. if (!cachedRendererResources.ready) {
  4678. // Cached resources for the model are not loaded yet. We'll
  4679. // try again every frame until they are.
  4680. return;
  4681. }
  4682. ++cachedRendererResources.count;
  4683. this._loadRendererResourcesFromCache = true;
  4684. } else {
  4685. cachedRendererResources = new CachedRendererResources(
  4686. context,
  4687. cacheKey
  4688. );
  4689. cachedRendererResources.count = 1;
  4690. modelCaches[this.cacheKey] = cachedRendererResources;
  4691. }
  4692. this._cachedRendererResources = cachedRendererResources;
  4693. } else {
  4694. // cache key not given? this model doesn't care about context level cache at all. Cache is here to simplify freeing on destroy.
  4695. cachedRendererResources = new CachedRendererResources(context);
  4696. cachedRendererResources.count = 1;
  4697. this._cachedRendererResources = cachedRendererResources;
  4698. }
  4699. this._state = ModelState.LOADING;
  4700. if (this._state !== ModelState.FAILED) {
  4701. const extensions = this.gltf.extensions;
  4702. if (defined(extensions) && defined(extensions.CESIUM_RTC)) {
  4703. const center = Cartesian3.fromArray(extensions.CESIUM_RTC.center);
  4704. if (!Cartesian3.equals(center, Cartesian3.ZERO)) {
  4705. this._rtcCenter3D = center;
  4706. const projection = frameState.mapProjection;
  4707. const ellipsoid = projection.ellipsoid;
  4708. const cartographic = ellipsoid.cartesianToCartographic(
  4709. this._rtcCenter3D
  4710. );
  4711. const projectedCart = projection.project(cartographic);
  4712. Cartesian3.fromElements(
  4713. projectedCart.z,
  4714. projectedCart.x,
  4715. projectedCart.y,
  4716. projectedCart
  4717. );
  4718. this._rtcCenter2D = projectedCart;
  4719. this._rtcCenterEye = new Cartesian3();
  4720. this._rtcCenter = this._rtcCenter3D;
  4721. }
  4722. }
  4723. addPipelineExtras(this.gltf);
  4724. this._loadResources = new ModelLoadResources();
  4725. if (!this._loadRendererResourcesFromCache) {
  4726. // Buffers are required to updateVersion
  4727. ModelUtility.parseBuffers(this, bufferLoad);
  4728. }
  4729. }
  4730. }
  4731. const loadResources = this._loadResources;
  4732. const incrementallyLoadTextures = this._incrementallyLoadTextures;
  4733. let justLoaded = false;
  4734. if (this._state === ModelState.LOADING) {
  4735. // Transition from LOADING -> LOADED once resources are downloaded and created.
  4736. // Textures may continue to stream in while in the LOADED state.
  4737. if (loadResources.pendingBufferLoads === 0) {
  4738. if (!loadResources.initialized) {
  4739. frameState.brdfLutGenerator.update(frameState);
  4740. ModelUtility.checkSupportedExtensions(
  4741. this.extensionsRequired,
  4742. supportsWebP
  4743. );
  4744. ModelUtility.updateForwardAxis(this);
  4745. // glTF pipeline updates, not needed if loading from cache
  4746. if (!defined(this.gltf.extras.sourceVersion)) {
  4747. const gltf = this.gltf;
  4748. // Add the original version so it remains cached
  4749. gltf.extras.sourceVersion = ModelUtility.getAssetVersion(gltf);
  4750. gltf.extras.sourceKHRTechniquesWebGL = defined(
  4751. ModelUtility.getUsedExtensions(gltf).KHR_techniques_webgl
  4752. );
  4753. this._sourceVersion = gltf.extras.sourceVersion;
  4754. this._sourceKHRTechniquesWebGL = gltf.extras.sourceKHRTechniquesWebGL;
  4755. updateVersion(gltf);
  4756. addDefaults(gltf);
  4757. const options = {
  4758. addBatchIdToGeneratedShaders: this._addBatchIdToGeneratedShaders,
  4759. };
  4760. processModelMaterialsCommon(gltf, options);
  4761. processPbrMaterials(gltf, options);
  4762. }
  4763. this._sourceVersion = this.gltf.extras.sourceVersion;
  4764. this._sourceKHRTechniquesWebGL = this.gltf.extras.sourceKHRTechniquesWebGL;
  4765. // Skip dequantizing in the shader if not encoded
  4766. this._dequantizeInShader =
  4767. this._dequantizeInShader && DracoLoader.hasExtension(this);
  4768. // We do this after to make sure that the ids don't change
  4769. addBuffersToLoadResources(this);
  4770. parseArticulations(this);
  4771. parseTechniques(this);
  4772. if (!this._loadRendererResourcesFromCache) {
  4773. parseBufferViews(this);
  4774. parseShaders(this);
  4775. parsePrograms(this);
  4776. parseTextures(this, context, supportsWebP);
  4777. }
  4778. parseMaterials(this);
  4779. parseMeshes(this);
  4780. parseNodes(this);
  4781. parseCredits(this);
  4782. // Start draco decoding
  4783. DracoLoader.parse(this, context);
  4784. loadResources.initialized = true;
  4785. }
  4786. if (!loadResources.finishedDecoding()) {
  4787. DracoLoader.decodeModel(this, context).catch(
  4788. ModelUtility.getFailedLoadFunction(this, "model", this.basePath)
  4789. );
  4790. }
  4791. if (loadResources.finishedDecoding() && !loadResources.resourcesParsed) {
  4792. this._boundingSphere = ModelUtility.computeBoundingSphere(this);
  4793. this._initialRadius = this._boundingSphere.radius;
  4794. DracoLoader.cacheDataForModel(this);
  4795. loadResources.resourcesParsed = true;
  4796. }
  4797. if (
  4798. loadResources.resourcesParsed &&
  4799. loadResources.pendingShaderLoads === 0
  4800. ) {
  4801. if (this.showOutline) {
  4802. ModelOutlineLoader.outlinePrimitives(this);
  4803. }
  4804. createResources(this, frameState);
  4805. }
  4806. }
  4807. if (
  4808. loadResources.finished() ||
  4809. (incrementallyLoadTextures &&
  4810. loadResources.finishedEverythingButTextureCreation())
  4811. ) {
  4812. this._state = ModelState.LOADED;
  4813. justLoaded = true;
  4814. }
  4815. }
  4816. // Incrementally stream textures.
  4817. if (defined(loadResources) && this._state === ModelState.LOADED) {
  4818. if (incrementallyLoadTextures && !justLoaded) {
  4819. createResources(this, frameState);
  4820. }
  4821. if (loadResources.finished()) {
  4822. this._loadResources = undefined; // Clear CPU memory since WebGL resources were created.
  4823. const resources = this._rendererResources;
  4824. const cachedResources = this._cachedRendererResources;
  4825. cachedResources.buffers = resources.buffers;
  4826. cachedResources.vertexArrays = resources.vertexArrays;
  4827. cachedResources.programs = resources.programs;
  4828. cachedResources.sourceShaders = resources.sourceShaders;
  4829. cachedResources.silhouettePrograms = resources.silhouettePrograms;
  4830. cachedResources.textures = resources.textures;
  4831. cachedResources.samplers = resources.samplers;
  4832. cachedResources.renderStates = resources.renderStates;
  4833. cachedResources.ready = true;
  4834. // The normal attribute name is required for silhouettes, so get it before the gltf JSON is released
  4835. this._normalAttributeName = ModelUtility.getAttributeOrUniformBySemantic(
  4836. this.gltf,
  4837. "NORMAL"
  4838. );
  4839. // Vertex arrays are unique to this model, do not store in cache.
  4840. if (defined(this._precreatedAttributes)) {
  4841. cachedResources.vertexArrays = {};
  4842. }
  4843. if (this.releaseGltfJson) {
  4844. releaseCachedGltf(this);
  4845. }
  4846. }
  4847. }
  4848. const silhouette = hasSilhouette(this, frameState);
  4849. const translucent = isTranslucent(this);
  4850. const invisible = isInvisible(this);
  4851. const backFaceCulling = this.backFaceCulling;
  4852. const displayConditionPassed = defined(this.distanceDisplayCondition)
  4853. ? distanceDisplayConditionVisible(this, frameState)
  4854. : true;
  4855. const show =
  4856. this.show &&
  4857. displayConditionPassed &&
  4858. this.scale !== 0.0 &&
  4859. (!invisible || silhouette);
  4860. this._imageBasedLighting.update(frameState);
  4861. if ((show && this._state === ModelState.LOADED) || justLoaded) {
  4862. const animated =
  4863. this.activeAnimations.update(frameState) || this._cesiumAnimationsDirty;
  4864. this._cesiumAnimationsDirty = false;
  4865. this._dirty = false;
  4866. let modelMatrix = this.modelMatrix;
  4867. const modeChanged = frameState.mode !== this._mode;
  4868. this._mode = frameState.mode;
  4869. // Model's model matrix needs to be updated
  4870. const modelTransformChanged =
  4871. !Matrix4.equals(this._modelMatrix, modelMatrix) ||
  4872. this._scale !== this.scale ||
  4873. this._minimumPixelSize !== this.minimumPixelSize ||
  4874. this.minimumPixelSize !== 0.0 || // Minimum pixel size changed or is enabled
  4875. this._maximumScale !== this.maximumScale ||
  4876. this._heightReference !== this.heightReference ||
  4877. this._heightChanged ||
  4878. modeChanged;
  4879. if (modelTransformChanged || justLoaded) {
  4880. Matrix4.clone(modelMatrix, this._modelMatrix);
  4881. updateClamping(this);
  4882. if (defined(this._clampedModelMatrix)) {
  4883. modelMatrix = this._clampedModelMatrix;
  4884. }
  4885. this._scale = this.scale;
  4886. this._minimumPixelSize = this.minimumPixelSize;
  4887. this._maximumScale = this.maximumScale;
  4888. this._heightReference = this.heightReference;
  4889. this._heightChanged = false;
  4890. const scale = getScale(this, frameState);
  4891. const computedModelMatrix = this._computedModelMatrix;
  4892. Matrix4.multiplyByUniformScale(modelMatrix, scale, computedModelMatrix);
  4893. if (this._upAxis === Axis.Y) {
  4894. Matrix4.multiplyTransformation(
  4895. computedModelMatrix,
  4896. Axis.Y_UP_TO_Z_UP,
  4897. computedModelMatrix
  4898. );
  4899. } else if (this._upAxis === Axis.X) {
  4900. Matrix4.multiplyTransformation(
  4901. computedModelMatrix,
  4902. Axis.X_UP_TO_Z_UP,
  4903. computedModelMatrix
  4904. );
  4905. }
  4906. if (this.forwardAxis === Axis.Z) {
  4907. // glTF 2.0 has a Z-forward convention that must be adapted here to X-forward.
  4908. Matrix4.multiplyTransformation(
  4909. computedModelMatrix,
  4910. Axis.Z_UP_TO_X_UP,
  4911. computedModelMatrix
  4912. );
  4913. }
  4914. }
  4915. // Update modelMatrix throughout the graph as needed
  4916. if (animated || modelTransformChanged || justLoaded) {
  4917. updateNodeHierarchyModelMatrix(
  4918. this,
  4919. modelTransformChanged,
  4920. justLoaded,
  4921. frameState.mapProjection
  4922. );
  4923. this._dirty = true;
  4924. if (animated || justLoaded) {
  4925. // Apply skins if animation changed any node transforms
  4926. applySkins(this);
  4927. }
  4928. }
  4929. if (this._perNodeShowDirty) {
  4930. this._perNodeShowDirty = false;
  4931. updatePerNodeShow(this);
  4932. }
  4933. updatePickIds(this, context);
  4934. updateWireframe(this);
  4935. updateShowBoundingVolume(this);
  4936. updateShadows(this);
  4937. updateClippingPlanes(this, frameState);
  4938. // Regenerate shaders if ClippingPlaneCollection state changed or it was removed
  4939. const clippingPlanes = this._clippingPlanes;
  4940. let currentClippingPlanesState = 0;
  4941. // If defined, use the reference matrix to transform miscellaneous properties like
  4942. // clipping planes and IBL instead of the modelMatrix. This is so that when
  4943. // models are part of a tileset these properties get transformed relative to
  4944. // a common reference (such as the root).
  4945. const referenceMatrix = defaultValue(this.referenceMatrix, modelMatrix);
  4946. if (
  4947. this._imageBasedLighting.useSphericalHarmonicCoefficients ||
  4948. this._imageBasedLighting.useSpecularEnvironmentMaps
  4949. ) {
  4950. let iblReferenceFrameMatrix3 = scratchIBLReferenceFrameMatrix3;
  4951. let iblReferenceFrameMatrix4 = scratchIBLReferenceFrameMatrix4;
  4952. iblReferenceFrameMatrix4 = Matrix4.multiply(
  4953. context.uniformState.view3D,
  4954. referenceMatrix,
  4955. iblReferenceFrameMatrix4
  4956. );
  4957. iblReferenceFrameMatrix3 = Matrix4.getMatrix3(
  4958. iblReferenceFrameMatrix4,
  4959. iblReferenceFrameMatrix3
  4960. );
  4961. iblReferenceFrameMatrix3 = Matrix3.getRotation(
  4962. iblReferenceFrameMatrix3,
  4963. iblReferenceFrameMatrix3
  4964. );
  4965. this._iblReferenceFrameMatrix = Matrix3.transpose(
  4966. iblReferenceFrameMatrix3,
  4967. this._iblReferenceFrameMatrix
  4968. );
  4969. }
  4970. this._shouldRegenerateShaders =
  4971. this._shouldRegenerateShaders ||
  4972. this._imageBasedLighting.shouldRegenerateShaders;
  4973. if (isClippingEnabled(this)) {
  4974. let clippingPlanesMatrix = scratchClippingPlanesMatrix;
  4975. clippingPlanesMatrix = Matrix4.multiply(
  4976. context.uniformState.view3D,
  4977. referenceMatrix,
  4978. clippingPlanesMatrix
  4979. );
  4980. clippingPlanesMatrix = Matrix4.multiply(
  4981. clippingPlanesMatrix,
  4982. clippingPlanes.modelMatrix,
  4983. clippingPlanesMatrix
  4984. );
  4985. this._clippingPlanesMatrix = Matrix4.inverseTranspose(
  4986. clippingPlanesMatrix,
  4987. this._clippingPlanesMatrix
  4988. );
  4989. currentClippingPlanesState = clippingPlanes.clippingPlanesState;
  4990. }
  4991. this._shouldRegenerateShaders =
  4992. this._shouldRegenerateShaders ||
  4993. this._clippingPlanesState !== currentClippingPlanesState;
  4994. this._clippingPlanesState = currentClippingPlanesState;
  4995. // Regenerate shaders if color shading changed from last update
  4996. const currentlyColorShadingEnabled = isColorShadingEnabled(this);
  4997. if (currentlyColorShadingEnabled !== this._colorShadingEnabled) {
  4998. this._colorShadingEnabled = currentlyColorShadingEnabled;
  4999. this._shouldRegenerateShaders = true;
  5000. }
  5001. // Regenerate shaders if splitting was enabled/disabled from last update
  5002. const splittingEnabled = this.splitDirection !== SplitDirection.NONE;
  5003. if (this._splittingEnabled !== splittingEnabled) {
  5004. this._splittingEnabled = splittingEnabled;
  5005. this._shouldRegenerateShaders = true;
  5006. }
  5007. if (this._shouldRegenerateShaders) {
  5008. regenerateShaders(this, frameState);
  5009. } else {
  5010. updateColor(this, frameState, false);
  5011. updateBackFaceCulling(this, frameState, false);
  5012. updateSilhouette(this, frameState, false);
  5013. }
  5014. }
  5015. if (justLoaded) {
  5016. // Called after modelMatrix update.
  5017. const model = this;
  5018. frameState.afterRender.push(function () {
  5019. model._ready = true;
  5020. model._readyPromise.resolve(model);
  5021. });
  5022. return;
  5023. }
  5024. // We don't check show at the top of the function since we
  5025. // want to be able to progressively load models when they are not shown,
  5026. // and then have them visible immediately when show is set to true.
  5027. if (show && !this._ignoreCommands) {
  5028. // PERFORMANCE_IDEA: This is terrible
  5029. const commandList = frameState.commandList;
  5030. const passes = frameState.passes;
  5031. const nodeCommands = this._nodeCommands;
  5032. const length = nodeCommands.length;
  5033. let i;
  5034. let nc;
  5035. const idl2D =
  5036. frameState.mapProjection.ellipsoid.maximumRadius * CesiumMath.PI;
  5037. let boundingVolume;
  5038. if (passes.render || (passes.pick && this.allowPicking)) {
  5039. for (i = 0; i < length; ++i) {
  5040. nc = nodeCommands[i];
  5041. if (nc.show) {
  5042. let command = nc.command;
  5043. if (silhouette) {
  5044. command = nc.silhouetteModelCommand;
  5045. } else if (translucent) {
  5046. command = nc.translucentCommand;
  5047. } else if (!backFaceCulling) {
  5048. command = nc.disableCullingCommand;
  5049. }
  5050. commandList.push(command);
  5051. boundingVolume = nc.command.boundingVolume;
  5052. if (
  5053. frameState.mode === SceneMode.SCENE2D &&
  5054. (boundingVolume.center.y + boundingVolume.radius > idl2D ||
  5055. boundingVolume.center.y - boundingVolume.radius < idl2D)
  5056. ) {
  5057. let command2D = nc.command2D;
  5058. if (silhouette) {
  5059. command2D = nc.silhouetteModelCommand2D;
  5060. } else if (translucent) {
  5061. command2D = nc.translucentCommand2D;
  5062. } else if (!backFaceCulling) {
  5063. command2D = nc.disableCullingCommand2D;
  5064. }
  5065. commandList.push(command2D);
  5066. }
  5067. }
  5068. }
  5069. if (silhouette && !passes.pick) {
  5070. // Render second silhouette pass
  5071. for (i = 0; i < length; ++i) {
  5072. nc = nodeCommands[i];
  5073. if (nc.show) {
  5074. commandList.push(nc.silhouetteColorCommand);
  5075. boundingVolume = nc.command.boundingVolume;
  5076. if (
  5077. frameState.mode === SceneMode.SCENE2D &&
  5078. (boundingVolume.center.y + boundingVolume.radius > idl2D ||
  5079. boundingVolume.center.y - boundingVolume.radius < idl2D)
  5080. ) {
  5081. commandList.push(nc.silhouetteColorCommand2D);
  5082. }
  5083. }
  5084. }
  5085. }
  5086. }
  5087. }
  5088. const credit = this._credit;
  5089. if (defined(credit)) {
  5090. frameState.creditDisplay.addCredit(credit);
  5091. }
  5092. const resourceCredits = this._resourceCredits;
  5093. const resourceCreditsLength = resourceCredits.length;
  5094. for (let c = 0; c < resourceCreditsLength; c++) {
  5095. frameState.creditDisplay.addCredit(resourceCredits[c]);
  5096. }
  5097. const gltfCredits = this._gltfCredits;
  5098. const gltfCreditsLength = gltfCredits.length;
  5099. for (let c = 0; c < gltfCreditsLength; c++) {
  5100. frameState.creditDisplay.addCredit(gltfCredits[c]);
  5101. }
  5102. };
  5103. function destroyIfNotCached(rendererResources, cachedRendererResources) {
  5104. if (rendererResources.programs !== cachedRendererResources.programs) {
  5105. destroy(rendererResources.programs);
  5106. }
  5107. if (
  5108. rendererResources.silhouettePrograms !==
  5109. cachedRendererResources.silhouettePrograms
  5110. ) {
  5111. destroy(rendererResources.silhouettePrograms);
  5112. }
  5113. }
  5114. // Run from update iff:
  5115. // - everything is loaded
  5116. // - clipping planes state change OR color state set
  5117. // Run this from destructor after removing color state and clipping plane state
  5118. function regenerateShaders(model, frameState) {
  5119. // In regards to _cachedRendererResources:
  5120. // Fair to assume that this is data that should just never get modified due to clipping planes, model color, or splitting.
  5121. // So if clipping planes, model color, or splitting are active:
  5122. // - delink _rendererResources.*programs and create new dictionaries.
  5123. // - 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
  5124. // If clipping planes, model color, and splitting inactive:
  5125. // - destroy _rendererResources.*programs
  5126. // - relink _rendererResources.*programs to _cachedRendererResources
  5127. // In both cases, need to mark commands as dirty, re-run derived commands (elsewhere)
  5128. const rendererResources = model._rendererResources;
  5129. const cachedRendererResources = model._cachedRendererResources;
  5130. destroyIfNotCached(rendererResources, cachedRendererResources);
  5131. let programId;
  5132. if (
  5133. isClippingEnabled(model) ||
  5134. isColorShadingEnabled(model) ||
  5135. model.splitDirection !== SplitDirection.NONE ||
  5136. model._shouldRegenerateShaders
  5137. ) {
  5138. model._shouldRegenerateShaders = false;
  5139. rendererResources.programs = {};
  5140. rendererResources.silhouettePrograms = {};
  5141. const visitedPrograms = {};
  5142. const techniques = model._sourceTechniques;
  5143. let technique;
  5144. for (const techniqueId in techniques) {
  5145. if (techniques.hasOwnProperty(techniqueId)) {
  5146. technique = techniques[techniqueId];
  5147. programId = technique.program;
  5148. if (!visitedPrograms[programId]) {
  5149. visitedPrograms[programId] = true;
  5150. recreateProgram(
  5151. {
  5152. programId: programId,
  5153. techniqueId: techniqueId,
  5154. },
  5155. model,
  5156. frameState.context
  5157. );
  5158. }
  5159. }
  5160. }
  5161. } else {
  5162. rendererResources.programs = cachedRendererResources.programs;
  5163. rendererResources.silhouettePrograms =
  5164. cachedRendererResources.silhouettePrograms;
  5165. }
  5166. // Fix all the commands, marking them as dirty so everything that derives will re-derive
  5167. const rendererPrograms = rendererResources.programs;
  5168. const nodeCommands = model._nodeCommands;
  5169. const commandCount = nodeCommands.length;
  5170. for (let i = 0; i < commandCount; ++i) {
  5171. const nodeCommand = nodeCommands[i];
  5172. programId = nodeCommand.programId;
  5173. const renderProgram = rendererPrograms[programId];
  5174. nodeCommand.command.shaderProgram = renderProgram;
  5175. if (defined(nodeCommand.command2D)) {
  5176. nodeCommand.command2D.shaderProgram = renderProgram;
  5177. }
  5178. }
  5179. // Force update silhouette commands/shaders
  5180. updateColor(model, frameState, true);
  5181. updateBackFaceCulling(model, frameState, true);
  5182. updateSilhouette(model, frameState, true);
  5183. }
  5184. /**
  5185. * Returns true if this object was destroyed; otherwise, false.
  5186. * <br /><br />
  5187. * If this object was destroyed, it should not be used; calling any function other than
  5188. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  5189. *
  5190. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  5191. *
  5192. * @see Model#destroy
  5193. */
  5194. Model.prototype.isDestroyed = function () {
  5195. return false;
  5196. };
  5197. /**
  5198. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  5199. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  5200. * <br /><br />
  5201. * Once an object is destroyed, it should not be used; calling any function other than
  5202. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  5203. * assign the return value (<code>undefined</code>) to the object as done in the example.
  5204. *
  5205. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  5206. *
  5207. *
  5208. * @example
  5209. * model = model && model.destroy();
  5210. *
  5211. * @see Model#isDestroyed
  5212. */
  5213. Model.prototype.destroy = function () {
  5214. // Vertex arrays are unique to this model, destroy here.
  5215. if (defined(this._precreatedAttributes)) {
  5216. destroy(this._rendererResources.vertexArrays);
  5217. }
  5218. if (defined(this._removeUpdateHeightCallback)) {
  5219. this._removeUpdateHeightCallback();
  5220. this._removeUpdateHeightCallback = undefined;
  5221. }
  5222. if (defined(this._terrainProviderChangedCallback)) {
  5223. this._terrainProviderChangedCallback();
  5224. this._terrainProviderChangedCallback = undefined;
  5225. }
  5226. // Shaders modified for clipping and for color don't get cached, so destroy these manually
  5227. if (defined(this._cachedRendererResources)) {
  5228. destroyIfNotCached(this._rendererResources, this._cachedRendererResources);
  5229. }
  5230. this._rendererResources = undefined;
  5231. this._cachedRendererResources =
  5232. this._cachedRendererResources && this._cachedRendererResources.release();
  5233. DracoLoader.destroyCachedDataForModel(this);
  5234. const pickIds = this._pickIds;
  5235. const length = pickIds.length;
  5236. for (let i = 0; i < length; ++i) {
  5237. pickIds[i].destroy();
  5238. }
  5239. releaseCachedGltf(this);
  5240. this._quantizedVertexShaders = undefined;
  5241. // Only destroy the ClippingPlaneCollection if this is the owner - if this model is part of a Cesium3DTileset,
  5242. // _clippingPlanes references a ClippingPlaneCollection that this model does not own.
  5243. const clippingPlaneCollection = this._clippingPlanes;
  5244. if (
  5245. defined(clippingPlaneCollection) &&
  5246. !clippingPlaneCollection.isDestroyed() &&
  5247. clippingPlaneCollection.owner === this
  5248. ) {
  5249. clippingPlaneCollection.destroy();
  5250. }
  5251. this._clippingPlanes = undefined;
  5252. if (
  5253. this._shouldDestroyImageBasedLighting &&
  5254. !this._imageBasedLighting.isDestroyed()
  5255. ) {
  5256. this._imageBasedLighting.destroy();
  5257. }
  5258. this._imageBasedLighting = undefined;
  5259. return destroyObject(this);
  5260. };
  5261. // exposed for testing
  5262. Model._getClippingFunction = getClippingFunction;
  5263. Model._modifyShaderForColor = modifyShaderForColor;
  5264. export default Model;