Model.js 185 KB

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