Model.js 212 KB

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