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