gaussian-splats-3d.module.js 474 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793
  1. import * as THREE from '../three.js/build/three.module.js';
  2. import { Ray as Ray$1, Plane, MathUtils, EventDispatcher, Vector3, MOUSE, TOUCH, Quaternion, Spherical, Vector2 } from '../three.js/build/three.module.js';
  3. /*
  4. let Ray$1 = THREE.Ray, Plane = THREE.Plane, MathUtils = THREE.MathUtils, EventDispatcher = THREE.EventDispatcher,
  5. Vector3 = THREE.Vector3, MOUSE = THREE.MOUSE, TOUCH = THREE.TOUCH, Quaternion = THREE.Quaternion,
  6. Spherical = THREE.Spherical, Vector2 = THREE.Vector2
  7. */
  8. //加
  9. {
  10. const _tables = /*@__PURE__*/ _generateTables();
  11. function _generateTables() {
  12. // float32 to float16 helpers
  13. const buffer = new ArrayBuffer( 4 );
  14. const floatView = new Float32Array( buffer );
  15. const uint32View = new Uint32Array( buffer );
  16. const baseTable = new Uint32Array( 512 );
  17. const shiftTable = new Uint32Array( 512 );
  18. for ( let i = 0; i < 256; ++ i ) {
  19. const e = i - 127;
  20. // very small number (0, -0)
  21. if ( e < - 27 ) {
  22. baseTable[ i ] = 0x0000;
  23. baseTable[ i | 0x100 ] = 0x8000;
  24. shiftTable[ i ] = 24;
  25. shiftTable[ i | 0x100 ] = 24;
  26. // small number (denorm)
  27. } else if ( e < - 14 ) {
  28. baseTable[ i ] = 0x0400 >> ( - e - 14 );
  29. baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000;
  30. shiftTable[ i ] = - e - 1;
  31. shiftTable[ i | 0x100 ] = - e - 1;
  32. // normal number
  33. } else if ( e <= 15 ) {
  34. baseTable[ i ] = ( e + 15 ) << 10;
  35. baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000;
  36. shiftTable[ i ] = 13;
  37. shiftTable[ i | 0x100 ] = 13;
  38. // large number (Infinity, -Infinity)
  39. } else if ( e < 128 ) {
  40. baseTable[ i ] = 0x7c00;
  41. baseTable[ i | 0x100 ] = 0xfc00;
  42. shiftTable[ i ] = 24;
  43. shiftTable[ i | 0x100 ] = 24;
  44. // stay (NaN, Infinity, -Infinity)
  45. } else {
  46. baseTable[ i ] = 0x7c00;
  47. baseTable[ i | 0x100 ] = 0xfc00;
  48. shiftTable[ i ] = 13;
  49. shiftTable[ i | 0x100 ] = 13;
  50. }
  51. }
  52. // float16 to float32 helpers
  53. const mantissaTable = new Uint32Array( 2048 );
  54. const exponentTable = new Uint32Array( 64 );
  55. const offsetTable = new Uint32Array( 64 );
  56. for ( let i = 1; i < 1024; ++ i ) {
  57. let m = i << 13; // zero pad mantissa bits
  58. let e = 0; // zero exponent
  59. // normalized
  60. while ( ( m & 0x00800000 ) === 0 ) {
  61. m <<= 1;
  62. e -= 0x00800000; // decrement exponent
  63. }
  64. m &= ~ 0x00800000; // clear leading 1 bit
  65. e += 0x38800000; // adjust bias
  66. mantissaTable[ i ] = m | e;
  67. }
  68. for ( let i = 1024; i < 2048; ++ i ) {
  69. mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 );
  70. }
  71. for ( let i = 1; i < 31; ++ i ) {
  72. exponentTable[ i ] = i << 23;
  73. }
  74. exponentTable[ 31 ] = 0x47800000;
  75. exponentTable[ 32 ] = 0x80000000;
  76. for ( let i = 33; i < 63; ++ i ) {
  77. exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 );
  78. }
  79. exponentTable[ 63 ] = 0xc7800000;
  80. for ( let i = 1; i < 64; ++ i ) {
  81. if ( i !== 32 ) {
  82. offsetTable[ i ] = 1024;
  83. }
  84. }
  85. return {
  86. floatView: floatView,
  87. uint32View: uint32View,
  88. baseTable: baseTable,
  89. shiftTable: shiftTable,
  90. mantissaTable: mantissaTable,
  91. exponentTable: exponentTable,
  92. offsetTable: offsetTable
  93. };
  94. }
  95. // float32 to float16
  96. /* function toHalfFloat( val ) {
  97. if ( Math.abs( val ) > 65504 ) console.warn( 'THREE.DataUtils.toHalfFloat(): Value out of range.' );
  98. val = clamp( val, - 65504, 65504 );
  99. _tables.floatView[ 0 ] = val;
  100. const f = _tables.uint32View[ 0 ];
  101. const e = ( f >> 23 ) & 0x1ff;
  102. return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] );
  103. } */
  104. // float16 to float32
  105. function fromHalfFloat( val ) {
  106. const m = val >> 10;
  107. _tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ];
  108. return _tables.floatView[ 0 ];
  109. }
  110. /*
  111. THREE.DataUtils = {
  112. toHalfFloat: toHalfFloat,
  113. fromHalfFloat: fromHalfFloat,
  114. };
  115. */
  116. THREE.DataUtils.fromHalfFloat = fromHalfFloat
  117. }
  118. //---------------------------------------------------
  119. /**
  120. * AbortablePromise: A quick & dirty wrapper for JavaScript's Promise class that allows the underlying
  121. * asynchronous operation to be cancelled. It is only meant for simple situations where no complex promise
  122. * chaining or merging occurs. It needs a significant amount of work to truly replicate the full
  123. * functionality of JavaScript's Promise class. Look at Util.fetchWithProgress() for example usage.
  124. *
  125. * This class was primarily added to allow splat scene downloads to be cancelled. It has not been tested
  126. * very thoroughly and the implementation is kinda janky. If you can at all help it, please avoid using it :)
  127. */
  128. class AbortablePromise {
  129. static idGen = 0;
  130. constructor(promiseFunc, abortHandler) {
  131. let promiseResolve;
  132. let promiseReject;
  133. this.promise = new Promise((resolve, reject) => {
  134. promiseResolve = resolve.bind(this);
  135. promiseReject = reject.bind(this);
  136. });
  137. const resolve = (...args) => {
  138. promiseResolve(...args);
  139. };
  140. const reject = (error) => {
  141. promiseReject(error);
  142. };
  143. promiseFunc(resolve.bind(this), reject.bind(this));
  144. this.abortHandler = abortHandler;
  145. this.id = AbortablePromise.idGen++;
  146. }
  147. then(onResolve) {
  148. return new AbortablePromise((resolve, reject) => {
  149. this.promise = this.promise
  150. .then((...args) => {
  151. const onResolveResult = onResolve(...args);
  152. if (onResolveResult instanceof Promise || onResolveResult instanceof AbortablePromise) {
  153. onResolveResult.then((...args2) => {
  154. resolve(...args2);
  155. });
  156. } else {
  157. resolve(onResolveResult);
  158. }
  159. })
  160. .catch((error) => {
  161. reject(error);
  162. });
  163. }, this.abortHandler);
  164. }
  165. catch(onFail) {
  166. return new AbortablePromise((resolve) => {
  167. this.promise = this.promise.then((...args) => {
  168. resolve(...args);
  169. })
  170. .catch(onFail);
  171. }, this.abortHandler);
  172. }
  173. abort() {
  174. if (this.abortHandler) this.abortHandler();
  175. }
  176. }
  177. class AbortedPromiseError extends Error {
  178. constructor(msg) {
  179. super(msg);
  180. }
  181. }
  182. const floatToHalf = function() {
  183. const floatView = new Float32Array(1);
  184. const int32View = new Int32Array(floatView.buffer);
  185. return function(val) {
  186. floatView[0] = val;
  187. const x = int32View[0];
  188. let bits = (x >> 16) & 0x8000;
  189. let m = (x >> 12) & 0x07ff;
  190. const e = (x >> 23) & 0xff;
  191. if (e < 103) return bits;
  192. if (e > 142) {
  193. bits |= 0x7c00;
  194. bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
  195. return bits;
  196. }
  197. if (e < 113) {
  198. m |= 0x0800;
  199. bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
  200. return bits;
  201. }
  202. bits |= (( e - 112) << 10) | (m >> 1);
  203. bits += m & 1;
  204. return bits;
  205. };
  206. }();
  207. const uintEncodedFloat = function() {
  208. const floatView = new Float32Array(1);
  209. const int32View = new Int32Array(floatView.buffer);
  210. return function(f) {
  211. floatView[0] = f;
  212. return int32View[0];
  213. };
  214. }();
  215. const rgbaToInteger = function(r, g, b, a) {
  216. return r + (g << 8) + (b << 16) + (a << 24);
  217. };
  218. const rgbaArrayToInteger = function(arr, offset) {
  219. return arr[offset] + (arr[offset + 1] << 8) + (arr[offset + 2] << 16) + (arr[offset + 3] << 24);
  220. };
  221. const fetchWithProgress = function(path, onProgress, saveChunks = true) {
  222. const abortController = new AbortController();
  223. const signal = abortController.signal;
  224. let aborted = false;
  225. let rejectFunc = null;
  226. const abortHandler = () => {
  227. abortController.abort();
  228. rejectFunc(new AbortedPromiseError('Fetch aborted.'));
  229. aborted = true;
  230. };
  231. return new AbortablePromise((resolve, reject) => {
  232. rejectFunc = reject;
  233. fetch(path, { signal })
  234. .then(async (data) => {
  235. const reader = data.body.getReader();
  236. let bytesDownloaded = 0;
  237. let _fileSize = data.headers.get('Content-Length');
  238. let fileSize = _fileSize ? parseInt(_fileSize) : undefined;
  239. const chunks = [];
  240. while (!aborted) {
  241. try {
  242. const { value: chunk, done } = await reader.read();
  243. if (done) {
  244. if (onProgress) {
  245. onProgress(100, '100%', chunk, fileSize);
  246. }
  247. if (saveChunks) {
  248. const buffer = new Blob(chunks).arrayBuffer();
  249. resolve(buffer);
  250. } else {
  251. resolve();
  252. }
  253. break;
  254. }
  255. bytesDownloaded += chunk.length;
  256. let percent;
  257. let percentLabel;
  258. if (fileSize !== undefined) {
  259. percent = bytesDownloaded / fileSize * 100;
  260. percentLabel = `${percent.toFixed(2)}%`;
  261. }
  262. if (saveChunks) chunks.push(chunk);
  263. if (onProgress) {
  264. const cancelSaveChucnks = onProgress(percent, percentLabel, chunk, fileSize);
  265. if (cancelSaveChucnks) saveChunks = false;
  266. }
  267. } catch (error) {
  268. reject(error);
  269. break;
  270. }
  271. }
  272. });
  273. }, abortHandler);
  274. };
  275. const clamp = function(val, min, max) {
  276. return Math.max(Math.min(val, max), min);
  277. };
  278. const getCurrentTime = function() {
  279. return performance.now() / 1000;
  280. };
  281. const disposeAllMeshes = (object3D) => {
  282. if (object3D.geometry) {
  283. object3D.geometry.dispose();
  284. object3D.geometry = null;
  285. }
  286. if (object3D.material) {
  287. object3D.material.dispose();
  288. object3D.material = null;
  289. }
  290. if (object3D.children) {
  291. for (let child of object3D.children) {
  292. disposeAllMeshes(child);
  293. }
  294. }
  295. };
  296. const delayedExecute = (func, fast) => {
  297. return new Promise((resolve) => {
  298. window.setTimeout(() => {
  299. resolve(func());
  300. }, fast ? 1 : 50);
  301. });
  302. };
  303. const getSphericalHarmonicsComponentCountForDegree = (sphericalHarmonicsDegree = 0) => {
  304. switch (sphericalHarmonicsDegree) {
  305. case 1:
  306. return 9;
  307. case 2:
  308. return 24;
  309. }
  310. return 0;
  311. };
  312. const BASE_COMPONENT_COUNT = 14;
  313. class UncompressedSplatArray {
  314. static OFFSET = {
  315. X: 0,
  316. Y: 1,
  317. Z: 2,
  318. SCALE0: 3,
  319. SCALE1: 4,
  320. SCALE2: 5,
  321. ROTATION0: 6,
  322. ROTATION1: 7,
  323. ROTATION2: 8,
  324. ROTATION3: 9,
  325. FDC0: 10,
  326. FDC1: 11,
  327. FDC2: 12,
  328. OPACITY: 13,
  329. FRC0: 14,
  330. FRC1: 15,
  331. FRC2: 16,
  332. FRC3: 17,
  333. FRC4: 18,
  334. FRC5: 19,
  335. FRC6: 20,
  336. FRC7: 21,
  337. FRC8: 22,
  338. FRC9: 23,
  339. FRC10: 24,
  340. FRC11: 25,
  341. FRC12: 26,
  342. FRC13: 27,
  343. FRC14: 28,
  344. FRC15: 29,
  345. FRC16: 30,
  346. FRC17: 31,
  347. FRC18: 32,
  348. FRC19: 33,
  349. FRC20: 34,
  350. FRC21: 35,
  351. FRC22: 36,
  352. FRC23: 37,
  353. };
  354. constructor(sphericalHarmonicsDegree = 0) {
  355. this.sphericalHarmonicsDegree = sphericalHarmonicsDegree;
  356. this.sphericalHarmonicsCount = getSphericalHarmonicsComponentCountForDegree(this.sphericalHarmonicsDegree);
  357. this.componentCount = this.sphericalHarmonicsCount + BASE_COMPONENT_COUNT;
  358. this.defaultSphericalHarmonics = new Array(this.sphericalHarmonicsCount).fill(0);
  359. this.splats = [];
  360. this.splatCount = 0;
  361. }
  362. static createSplat(sphericalHarmonicsDegree = 0) {
  363. const baseSplat = [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
  364. let shEntries = getSphericalHarmonicsComponentCountForDegree(sphericalHarmonicsDegree);
  365. for (let i = 0; i < shEntries; i++) baseSplat.push(0);
  366. return baseSplat;
  367. }
  368. addSplat(splat) {
  369. this.splats.push(splat);
  370. this.splatCount++;
  371. }
  372. getSplat(index) {
  373. return this.splats[index];
  374. }
  375. addDefaultSplat() {
  376. const newSplat = UncompressedSplatArray.createSplat(this.sphericalHarmonicsDegree);
  377. this.addSplat(newSplat);
  378. return newSplat;
  379. }
  380. addSplatFromComonents(x, y, z, scale0, scale1, scale2, rot0, rot1, rot2, rot3, r, g, b, opacity, ...rest) {
  381. const newSplat = [x, y, z, scale0, scale1, scale2, rot0, rot1, rot2, rot3, r, g, b, opacity, ...this.defaultSphericalHarmonics];
  382. for (let i = 0; i < rest.length && i < this.sphericalHarmonicsCount; i++) {
  383. newSplat[i] = rest[i];
  384. }
  385. this.addSplat(newSplat);
  386. return newSplat;
  387. }
  388. addSplatFromArray(src, srcIndex) {
  389. const srcSplat = src.splats[srcIndex];
  390. const newSplat = UncompressedSplatArray.createSplat(this.sphericalHarmonicsDegree);
  391. for (let i = 0; i < this.componentCount && i < srcSplat.length; i++) {
  392. newSplat[i] = srcSplat[i];
  393. }
  394. this.addSplat(newSplat);
  395. }
  396. }
  397. const toHalfFloat = THREE.DataUtils.toHalfFloat.bind(THREE.DataUtils);
  398. const toUint8 = (v) => {
  399. return Math.floor(v * 128) + 128;
  400. };
  401. const fromUint8 = (v) => {
  402. return (v / 255) * 2.0 - 1.0;
  403. };
  404. //xzw 改 暂时没用到 需要更新版本
  405. const fromHalfFloat = THREE.DataUtils.fromHalfFloat.bind(THREE.DataUtils);
  406. const fromHalfFloatToUint8 = (v) => {
  407. return Math.floor(fromHalfFloat(v) * 128) + 128;
  408. };
  409. const toUncompressedFloat = (f, compressionLevel, isSH = false) => {
  410. if (compressionLevel === 0) {
  411. return f;
  412. } else if (compressionLevel === 1 || compressionLevel === 2 && !isSH) {
  413. return THREE.DataUtils.fromHalfFloat(f);
  414. } else if (compressionLevel === 2) {
  415. return fromUint8(f);
  416. }
  417. };
  418. const dataViewFloatForCompressionLevel = (dataView, floatIndex, compressionLevel, isSH = false) => {
  419. if (compressionLevel === 0) {
  420. return dataView.getFloat32(floatIndex * 4, true);
  421. } else if (compressionLevel === 1 || compressionLevel === 2 && !isSH) {
  422. return dataView.getUint16(floatIndex * 2, true);
  423. } else {
  424. return dataView.getUint8(floatIndex, true);
  425. }
  426. };
  427. /**
  428. * SplatBuffer: Container for splat data from a single scene/file and capable of (mediocre) compression.
  429. */
  430. class SplatBuffer {
  431. static CurrentMajorVersion = 0;
  432. static CurrentMinorVersion = 1;
  433. static CenterComponentCount = 3;
  434. static ScaleComponentCount = 3;
  435. static RotationComponentCount = 4;
  436. static ColorComponentCount = 4;
  437. static CovarianceComponentCount = 6;
  438. static SplatScaleOffsetFloat = 3;
  439. static SplatRotationOffsetFloat = 6;
  440. static CompressionLevels = {
  441. 0: {
  442. BytesPerCenter: 12,
  443. BytesPerScale: 12,
  444. BytesPerRotation: 16,
  445. BytesPerColor: 4,
  446. ScaleOffsetBytes: 12,
  447. RotationffsetBytes: 24,
  448. ColorOffsetBytes: 40,
  449. SphericalHarmonicsOffsetBytes: 44,
  450. ScaleRange: 1,
  451. BytesPerSphericalHarmonicsComponent: 4,
  452. SphericalHarmonicsOffsetFloat: 11,
  453. SphericalHarmonicsDegrees: {
  454. 0: { BytesPerSplat: 44 },
  455. 1: { BytesPerSplat: 80 },
  456. 2: { BytesPerSplat: 140 }
  457. },
  458. },
  459. 1: {
  460. BytesPerCenter: 6,
  461. BytesPerScale: 6,
  462. BytesPerRotation: 8,
  463. BytesPerColor: 4,
  464. ScaleOffsetBytes: 6,
  465. RotationffsetBytes: 12,
  466. ColorOffsetBytes: 20,
  467. SphericalHarmonicsOffsetBytes: 24,
  468. ScaleRange: 32767,
  469. BytesPerSphericalHarmonicsComponent: 2,
  470. SphericalHarmonicsOffsetFloat: 12,
  471. SphericalHarmonicsDegrees: {
  472. 0: { BytesPerSplat: 24 },
  473. 1: { BytesPerSplat: 42 },
  474. 2: { BytesPerSplat: 72 }
  475. },
  476. },
  477. 2: {
  478. BytesPerCenter: 6,
  479. BytesPerScale: 6,
  480. BytesPerRotation: 8,
  481. BytesPerColor: 4,
  482. ScaleOffsetBytes: 6,
  483. RotationffsetBytes: 12,
  484. ColorOffsetBytes: 20,
  485. SphericalHarmonicsOffsetBytes: 24,
  486. ScaleRange: 32767,
  487. BytesPerSphericalHarmonicsComponent: 1,
  488. SphericalHarmonicsOffsetFloat: 12,
  489. SphericalHarmonicsDegrees: {
  490. 0: { BytesPerSplat: 24 },
  491. 1: { BytesPerSplat: 33 },
  492. 2: { BytesPerSplat: 48 }
  493. },
  494. }
  495. };
  496. static CovarianceSizeFloats = 6;
  497. static HeaderSizeBytes = 4096;
  498. static SectionHeaderSizeBytes = 1024;
  499. static BucketStorageSizeBytes = 12;
  500. static BucketStorageSizeFloats = 3;
  501. static BucketBlockSize = 5.0;
  502. static BucketSize = 256;
  503. constructor(bufferData, secLoadedCountsToMax = true) {
  504. this.constructFromBuffer(bufferData, secLoadedCountsToMax);
  505. }
  506. getSplatCount() {
  507. return this.splatCount;
  508. }
  509. getMaxSplatCount() {
  510. return this.maxSplatCount;
  511. }
  512. getMinSphericalHarmonicsDegree() {
  513. let minSphericalHarmonicsDegree = 0;
  514. for (let i = 0; i < this.sections.length; i++) {
  515. const section = this.sections[i];
  516. if (i === 0 || section.sphericalHarmonicsDegree < minSphericalHarmonicsDegree) {
  517. minSphericalHarmonicsDegree = section.sphericalHarmonicsDegree;
  518. }
  519. }
  520. return minSphericalHarmonicsDegree;
  521. }
  522. getBucketIndex(section, localSplatIndex) {
  523. let bucketIndex;
  524. const maxSplatIndexInFullBuckets = section.fullBucketCount * section.bucketSize;
  525. if (localSplatIndex < maxSplatIndexInFullBuckets) {
  526. bucketIndex = Math.floor(localSplatIndex / section.bucketSize);
  527. } else {
  528. let bucketSplatIndex = maxSplatIndexInFullBuckets;
  529. bucketIndex = section.fullBucketCount;
  530. let partiallyFullBucketIndex = 0;
  531. while (bucketSplatIndex < section.splatCount) {
  532. let currentPartiallyFilledBucketSize = section.partiallyFilledBucketLengths[partiallyFullBucketIndex];
  533. if (localSplatIndex >= bucketSplatIndex && localSplatIndex < bucketSplatIndex + currentPartiallyFilledBucketSize) {
  534. break;
  535. }
  536. bucketSplatIndex += currentPartiallyFilledBucketSize;
  537. bucketIndex++;
  538. partiallyFullBucketIndex++;
  539. }
  540. }
  541. return bucketIndex;
  542. }
  543. getSplatCenter(globalSplatIndex, outCenter, transform) {
  544. const sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];
  545. const section = this.sections[sectionIndex];
  546. const localSplatIndex = globalSplatIndex - section.splatCountOffset;
  547. const srcSplatCentersBase = section.bytesPerSplat * localSplatIndex;
  548. const dataView = new DataView(this.bufferData, section.dataBase + srcSplatCentersBase);
  549. const x = dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);
  550. const y = dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);
  551. const z = dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);
  552. if (this.compressionLevel >= 1) {
  553. const bucketIndex = this.getBucketIndex(section, localSplatIndex);
  554. const bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;
  555. const sf = section.compressionScaleFactor;
  556. const sr = section.compressionScaleRange;
  557. outCenter.x = (x - sr) * sf + section.bucketArray[bucketBase];
  558. outCenter.y = (y - sr) * sf + section.bucketArray[bucketBase + 1];
  559. outCenter.z = (z - sr) * sf + section.bucketArray[bucketBase + 2];
  560. } else {
  561. outCenter.x = x;
  562. outCenter.y = y;
  563. outCenter.z = z;
  564. }
  565. if (transform) outCenter.applyMatrix4(transform);
  566. }
  567. getSplatScaleAndRotation = function() {
  568. const scaleMatrix = new THREE.Matrix4();
  569. const rotationMatrix = new THREE.Matrix4();
  570. const tempMatrix = new THREE.Matrix4();
  571. const tempPosition = new THREE.Vector3();
  572. const scale = new THREE.Vector3();
  573. const rotation = new THREE.Quaternion();
  574. return function(index, outScale, outRotation, transform) {
  575. const sectionIndex = this.globalSplatIndexToSectionMap[index];
  576. const section = this.sections[sectionIndex];
  577. const localSplatIndex = index - section.splatCountOffset;
  578. const srcSplatScalesBase = section.bytesPerSplat * localSplatIndex +
  579. SplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;
  580. const dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);
  581. scale.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel), this.compressionLevel),
  582. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel), this.compressionLevel),
  583. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel), this.compressionLevel));
  584. rotation.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel), this.compressionLevel),
  585. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel), this.compressionLevel),
  586. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel), this.compressionLevel),
  587. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel), this.compressionLevel));
  588. if (transform) {
  589. scaleMatrix.makeScale(scale.x, scale.y, scale.z);
  590. rotationMatrix.makeRotationFromQuaternion(rotation);
  591. tempMatrix.copy(scaleMatrix).multiply(rotationMatrix).multiply(transform);
  592. tempMatrix.decompose(tempPosition, outRotation, outScale);
  593. } else {
  594. outScale.copy(scale);
  595. outRotation.copy(rotation);
  596. }
  597. };
  598. }();
  599. getSplatColor(globalSplatIndex, outColor) {
  600. const sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];
  601. const section = this.sections[sectionIndex];
  602. const localSplatIndex = globalSplatIndex - section.splatCountOffset;
  603. const srcSplatColorsBase = section.bytesPerSplat * localSplatIndex +
  604. SplatBuffer.CompressionLevels[this.compressionLevel].ColorOffsetBytes;
  605. const splatColorsArray = new Uint8Array(this.bufferData, section.dataBase + srcSplatColorsBase, 4);
  606. outColor.set(splatColorsArray[0], splatColorsArray[1],
  607. splatColorsArray[2], splatColorsArray[3]);
  608. }
  609. fillSplatCenterArray(outCenterArray, transform, srcFrom, srcTo, destFrom) {
  610. const splatCount = this.splatCount;
  611. srcFrom = srcFrom || 0;
  612. srcTo = srcTo || splatCount - 1;
  613. if (destFrom === undefined) destFrom = srcFrom;
  614. const center = new THREE.Vector3();
  615. for (let i = srcFrom; i <= srcTo; i++) {
  616. const sectionIndex = this.globalSplatIndexToSectionMap[i];
  617. const section = this.sections[sectionIndex];
  618. const localSplatIndex = i - section.splatCountOffset;
  619. const centerDestBase = (i - srcFrom + destFrom) * SplatBuffer.CenterComponentCount;
  620. const srcSplatCentersBase = section.bytesPerSplat * localSplatIndex;
  621. const dataView = new DataView(this.bufferData, section.dataBase + srcSplatCentersBase);
  622. const x = dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);
  623. const y = dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);
  624. const z = dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);
  625. if (this.compressionLevel >= 1) {
  626. const bucketIndex = this.getBucketIndex(section, localSplatIndex);
  627. const bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;
  628. const sf = section.compressionScaleFactor;
  629. const sr = section.compressionScaleRange;
  630. center.x = (x - sr) * sf + section.bucketArray[bucketBase];
  631. center.y = (y - sr) * sf + section.bucketArray[bucketBase + 1];
  632. center.z = (z - sr) * sf + section.bucketArray[bucketBase + 2];
  633. } else {
  634. center.x = x;
  635. center.y = y;
  636. center.z = z;
  637. }
  638. if (transform) {
  639. center.applyMatrix4(transform);
  640. }
  641. outCenterArray[centerDestBase] = center.x;
  642. outCenterArray[centerDestBase + 1] = center.y;
  643. outCenterArray[centerDestBase + 2] = center.z;
  644. }
  645. }
  646. static computeCovariance = function() {//通过旋转和缩放计算协方差(决定姿态)得到6个数
  647. const tempMatrix4 = new THREE.Matrix4();
  648. const scaleMatrix = new THREE.Matrix3();
  649. const rotationMatrix = new THREE.Matrix3();
  650. const covarianceMatrix = new THREE.Matrix3();
  651. const transformedCovariance = new THREE.Matrix3();
  652. const transform3x3 = new THREE.Matrix3();
  653. const transform3x3Transpose = new THREE.Matrix3();
  654. return function(scale, rotation, transform, outCovariance, outOffset = 0, desiredOutputCompressionLevel) {
  655. tempMatrix4.makeScale(scale.x, scale.y, scale.z);
  656. scaleMatrix.setFromMatrix4(tempMatrix4);
  657. tempMatrix4.makeRotationFromQuaternion(rotation);
  658. rotationMatrix.setFromMatrix4(tempMatrix4);
  659. covarianceMatrix.copy(rotationMatrix).multiply(scaleMatrix);
  660. transformedCovariance.copy(covarianceMatrix).transpose().premultiply(covarianceMatrix); //为什么要乘以自己的转置呀?
  661. if (transform) {//场景的整体transform
  662. transform3x3.setFromMatrix4(transform);
  663. transform3x3Transpose.copy(transform3x3).transpose();
  664. transformedCovariance.multiply(transform3x3Transpose);
  665. transformedCovariance.premultiply(transform3x3);
  666. }
  667. if (desiredOutputCompressionLevel >= 1) {//压缩
  668. outCovariance[outOffset] = toHalfFloat(transformedCovariance.elements[0]);
  669. outCovariance[outOffset + 1] = toHalfFloat(transformedCovariance.elements[3]);
  670. outCovariance[outOffset + 2] = toHalfFloat(transformedCovariance.elements[6]);
  671. outCovariance[outOffset + 3] = toHalfFloat(transformedCovariance.elements[4]);
  672. outCovariance[outOffset + 4] = toHalfFloat(transformedCovariance.elements[7]);
  673. outCovariance[outOffset + 5] = toHalfFloat(transformedCovariance.elements[8]);
  674. } else {
  675. outCovariance[outOffset] = transformedCovariance.elements[0];
  676. outCovariance[outOffset + 1] = transformedCovariance.elements[3];
  677. outCovariance[outOffset + 2] = transformedCovariance.elements[6];
  678. outCovariance[outOffset + 3] = transformedCovariance.elements[4];
  679. outCovariance[outOffset + 4] = transformedCovariance.elements[7];
  680. outCovariance[outOffset + 5] = transformedCovariance.elements[8];
  681. }
  682. };
  683. }();
  684. fillSplatCovarianceArray(covarianceArray, transform, srcFrom, srcTo, destFrom, desiredOutputCompressionLevel) {
  685. const splatCount = this.splatCount;
  686. const scale = new THREE.Vector3();
  687. const rotation = new THREE.Quaternion();
  688. srcFrom = srcFrom || 0;
  689. srcTo = srcTo || splatCount - 1;
  690. if (destFrom === undefined) destFrom = srcFrom;
  691. for (let i = srcFrom; i <= srcTo; i++) {
  692. const sectionIndex = this.globalSplatIndexToSectionMap[i];
  693. const section = this.sections[sectionIndex];
  694. const localSplatIndex = i - section.splatCountOffset;
  695. const covarianceDestBase = (i - srcFrom + destFrom) * SplatBuffer.CovarianceComponentCount;
  696. const srcSplatScalesBase = section.bytesPerSplat * localSplatIndex +
  697. SplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;
  698. const dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);
  699. scale.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel), this.compressionLevel),
  700. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel), this.compressionLevel),
  701. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel), this.compressionLevel));
  702. rotation.set(toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel), this.compressionLevel),
  703. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel), this.compressionLevel),
  704. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel), this.compressionLevel),
  705. toUncompressedFloat(dataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel), this.compressionLevel));
  706. SplatBuffer.computeCovariance(scale, rotation, transform, covarianceArray, covarianceDestBase, desiredOutputCompressionLevel);
  707. }
  708. }
  709. fillSplatColorArray(outColorArray, minimumAlpha, srcFrom, srcTo, destFrom) {
  710. const splatCount = this.splatCount;
  711. srcFrom = srcFrom || 0;
  712. srcTo = srcTo || splatCount - 1;
  713. if (destFrom === undefined) destFrom = srcFrom;
  714. for (let i = srcFrom; i <= srcTo; i++) {
  715. const sectionIndex = this.globalSplatIndexToSectionMap[i];
  716. const section = this.sections[sectionIndex];
  717. const localSplatIndex = i - section.splatCountOffset;
  718. const colorDestBase = (i - srcFrom + destFrom) * SplatBuffer.ColorComponentCount;
  719. const srcSplatColorsBase = section.bytesPerSplat * localSplatIndex +
  720. SplatBuffer.CompressionLevels[this.compressionLevel].ColorOffsetBytes;
  721. const dataView = new Uint8Array(this.bufferData, section.dataBase + srcSplatColorsBase);
  722. let alpha = dataView[3];
  723. alpha = (alpha >= minimumAlpha) ? alpha : 0;
  724. outColorArray[colorDestBase] = dataView[0];
  725. outColorArray[colorDestBase + 1] = dataView[1];
  726. outColorArray[colorDestBase + 2] = dataView[2];
  727. outColorArray[colorDestBase + 3] = alpha;
  728. }
  729. }
  730. fillSphericalHarmonicsArray = function() {
  731. const sphericalHarmonicVectors = [];
  732. for (let i = 0; i < 15; i++) {
  733. sphericalHarmonicVectors[i] = new THREE.Vector3();
  734. }
  735. const tempMatrix3 = new THREE.Matrix3();
  736. const sh11 = [];
  737. const sh12 = [];
  738. const sh13 = [];
  739. const sh21 = [];
  740. const sh22 = [];
  741. const sh23 = [];
  742. const sh24 = [];
  743. const sh25 = [];
  744. const shIn1 = [];
  745. const shIn2 = [];
  746. const shIn3 = [];
  747. const shIn4 = [];
  748. const shIn5 = [];
  749. const shOut1 = [];
  750. const shOut2 = [];
  751. const shOut3 = [];
  752. const shOut4 = [];
  753. const shOut5 = [];
  754. const noop = (v) => v;
  755. const set3 = (array, val1, val2, val3) => {
  756. array[0] = val1;
  757. array[1] = val2;
  758. array[2] = val3;
  759. };
  760. const set3FromArray = (array, srcDestView, stride, srcBase, compressionLevel) => {
  761. array[0] = dataViewFloatForCompressionLevel(srcDestView, srcBase, compressionLevel, true);
  762. array[1] = dataViewFloatForCompressionLevel(srcDestView, srcBase + stride, compressionLevel, true);
  763. array[2] = dataViewFloatForCompressionLevel(srcDestView, srcBase + stride + stride, compressionLevel, true);
  764. };
  765. const copy3 = (srcArray, destArray) => {
  766. destArray[0] = srcArray[0];
  767. destArray[1] = srcArray[1];
  768. destArray[2] = srcArray[2];
  769. };
  770. const setOutput3 = (srcArray, destArray, destBase, conversionFunc) => {
  771. destArray[destBase] = conversionFunc(srcArray[0]);
  772. destArray[destBase + 1] = conversionFunc(srcArray[1]);
  773. destArray[destBase + 2] = conversionFunc(srcArray[2]);
  774. };
  775. const toUncompressedFloatArray3 = (src, dest, compressionLevel) => {
  776. dest[0] = toUncompressedFloat(src[0], compressionLevel, true);
  777. dest[1] = toUncompressedFloat(src[1], compressionLevel, true);
  778. dest[2] = toUncompressedFloat(src[2], compressionLevel, true);
  779. return dest;
  780. };
  781. return function(outSphericalHarmonicsArray, outSphericalHarmonicsDegree, transform,
  782. srcFrom, srcTo, destFrom, desiredOutputCompressionLevel) {
  783. const splatCount = this.splatCount;
  784. srcFrom = srcFrom || 0;
  785. srcTo = srcTo || splatCount - 1;
  786. if (destFrom === undefined) destFrom = srcFrom;
  787. if (transform && outSphericalHarmonicsDegree >= 1) {
  788. tempMatrix3.setFromMatrix4(transform);
  789. set3(sh11, tempMatrix3.elements[4], -tempMatrix3.elements[7], tempMatrix3.elements[1]);
  790. set3(sh12, -tempMatrix3.elements[5], tempMatrix3.elements[8], -tempMatrix3.elements[2]);
  791. set3(sh13, tempMatrix3.elements[3], -tempMatrix3.elements[6], tempMatrix3.elements[0]);
  792. }
  793. for (let i = srcFrom; i <= srcTo; i++) {
  794. const sectionIndex = this.globalSplatIndexToSectionMap[i];
  795. const section = this.sections[sectionIndex];
  796. outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, section.sphericalHarmonicsDegree);
  797. const outSphericalHarmonicsComponentsCount = getSphericalHarmonicsComponentCountForDegree(outSphericalHarmonicsDegree);
  798. const localSplatIndex = i - section.splatCountOffset;
  799. const srcSplatSHBase = section.bytesPerSplat * localSplatIndex +
  800. SplatBuffer.CompressionLevels[this.compressionLevel].SphericalHarmonicsOffsetBytes;
  801. const dataView = new DataView(this.bufferData, section.dataBase + srcSplatSHBase);
  802. const shDestBase = (i - srcFrom + destFrom) * outSphericalHarmonicsComponentsCount;
  803. let compressionLevelForOutputConversion = transform ? 0 : this.compressionLevel;
  804. let outputConversionFunc = noop;
  805. if (compressionLevelForOutputConversion !== desiredOutputCompressionLevel) {
  806. if (compressionLevelForOutputConversion === 1) {
  807. if (desiredOutputCompressionLevel === 0) outputConversionFunc = fromHalfFloat;
  808. else if (desiredOutputCompressionLevel == 2) outputConversionFunc = fromHalfFloatToUint8;
  809. } else if (compressionLevelForOutputConversion === 0) {
  810. if (desiredOutputCompressionLevel === 1) outputConversionFunc = toHalfFloat;
  811. else if (desiredOutputCompressionLevel == 2) outputConversionFunc = toUint8;
  812. }
  813. }
  814. if (outSphericalHarmonicsDegree >= 1) {
  815. set3FromArray(shIn1, dataView, 3, 0, this.compressionLevel);
  816. set3FromArray(shIn2, dataView, 3, 1, this.compressionLevel);
  817. set3FromArray(shIn3, dataView, 3, 2, this.compressionLevel);
  818. if (transform) {
  819. toUncompressedFloatArray3(shIn1, shIn1, this.compressionLevel);
  820. toUncompressedFloatArray3(shIn2, shIn2, this.compressionLevel);
  821. toUncompressedFloatArray3(shIn3, shIn3, this.compressionLevel);
  822. SplatBuffer.rotateSphericalHarmonics3(shIn1, shIn2, shIn3, sh11, sh12, sh13, shOut1, shOut2, shOut3);
  823. } else {
  824. copy3(shIn1, shOut1);
  825. copy3(shIn2, shOut2);
  826. copy3(shIn3, shOut3);
  827. }
  828. setOutput3(shOut1, outSphericalHarmonicsArray, shDestBase, outputConversionFunc);
  829. setOutput3(shOut2, outSphericalHarmonicsArray, shDestBase + 3, outputConversionFunc);
  830. setOutput3(shOut3, outSphericalHarmonicsArray, shDestBase + 6, outputConversionFunc);
  831. if (outSphericalHarmonicsDegree >= 2) {
  832. set3FromArray(shIn1, dataView, 5, 9, this.compressionLevel);
  833. set3FromArray(shIn2, dataView, 5, 10, this.compressionLevel);
  834. set3FromArray(shIn3, dataView, 5, 11, this.compressionLevel);
  835. set3FromArray(shIn4, dataView, 5, 12, this.compressionLevel);
  836. set3FromArray(shIn5, dataView, 5, 13, this.compressionLevel);
  837. if (transform) {
  838. toUncompressedFloatArray3(shIn1, shIn1, this.compressionLevel);
  839. toUncompressedFloatArray3(shIn2, shIn2, this.compressionLevel);
  840. toUncompressedFloatArray3(shIn3, shIn3, this.compressionLevel);
  841. toUncompressedFloatArray3(shIn4, shIn4, this.compressionLevel);
  842. toUncompressedFloatArray3(shIn5, shIn5, this.compressionLevel);
  843. SplatBuffer.rotateSphericalHarmonics5(shIn1, shIn2, shIn3, shIn4, shIn5,
  844. sh11, sh12, sh13, sh21, sh22, sh23, sh24, sh25,
  845. shOut1, shOut2, shOut3, shOut4, shOut5);
  846. } else {
  847. copy3(shIn1, shOut1);
  848. copy3(shIn2, shOut2);
  849. copy3(shIn3, shOut3);
  850. copy3(shIn4, shOut4);
  851. copy3(shIn5, shOut5);
  852. }
  853. setOutput3(shOut1, outSphericalHarmonicsArray, shDestBase + 9, outputConversionFunc);
  854. setOutput3(shOut2, outSphericalHarmonicsArray, shDestBase + 12, outputConversionFunc);
  855. setOutput3(shOut3, outSphericalHarmonicsArray, shDestBase + 15, outputConversionFunc);
  856. setOutput3(shOut4, outSphericalHarmonicsArray, shDestBase + 18, outputConversionFunc);
  857. setOutput3(shOut5, outSphericalHarmonicsArray, shDestBase + 21, outputConversionFunc);
  858. }
  859. }
  860. }
  861. };
  862. }();
  863. static dot3 = (v1, v2, v3, transformRow, outArray) => {
  864. outArray[0] = outArray[1] = outArray[2] = 0;
  865. const t0 = transformRow[0];
  866. const t1 = transformRow[1];
  867. const t2 = transformRow[2];
  868. SplatBuffer.addInto3(v1[0] * t0, v1[1] * t0, v1[2] * t0, outArray);
  869. SplatBuffer.addInto3(v2[0] * t1, v2[1] * t1, v2[2] * t1, outArray);
  870. SplatBuffer.addInto3(v3[0] * t2, v3[1] * t2, v3[2] * t2, outArray);
  871. };
  872. static addInto3 = (val1, val2, val3, destArray) => {
  873. destArray[0] = destArray[0] + val1;
  874. destArray[1] = destArray[1] + val2;
  875. destArray[2] = destArray[2] + val3;
  876. };
  877. static dot5 = (v1, v2, v3, v4, v5, transformRow, outArray) => {
  878. outArray[0] = outArray[1] = outArray[2] = 0;
  879. const t0 = transformRow[0];
  880. const t1 = transformRow[1];
  881. const t2 = transformRow[2];
  882. const t3 = transformRow[3];
  883. const t4 = transformRow[4];
  884. SplatBuffer.addInto3(v1[0] * t0, v1[1] * t0, v1[2] * t0, outArray);
  885. SplatBuffer.addInto3(v2[0] * t1, v2[1] * t1, v2[2] * t1, outArray);
  886. SplatBuffer.addInto3(v3[0] * t2, v3[1] * t2, v3[2] * t2, outArray);
  887. SplatBuffer.addInto3(v4[0] * t3, v4[1] * t3, v4[2] * t3, outArray);
  888. SplatBuffer.addInto3(v5[0] * t4, v5[1] * t4, v5[2] * t4, outArray);
  889. };
  890. static rotateSphericalHarmonics3 = (in1, in2, in3, tsh11, tsh12, tsh13, out1, out2, out3) => {
  891. SplatBuffer.dot3(in1, in2, in3, tsh11, out1);
  892. SplatBuffer.dot3(in1, in2, in3, tsh12, out2);
  893. SplatBuffer.dot3(in1, in2, in3, tsh13, out3);
  894. };
  895. static rotateSphericalHarmonics5 = (in1, in2, in3, in4, in5, tsh11, tsh12, tsh13,
  896. tsh21, tsh22, tsh23, tsh24, tsh25, out1, out2, out3, out4, out5) => {
  897. const kSqrt0104 = Math.sqrt(1.0 / 4.0);
  898. const kSqrt0304 = Math.sqrt(3.0 / 4.0);
  899. const kSqrt0103 = Math.sqrt(1.0 / 3.0);
  900. const kSqrt0403 = Math.sqrt(4.0 / 3.0);
  901. const kSqrt0112 = Math.sqrt(1.0 / 12.0);
  902. tsh21[0] = kSqrt0104 * ((tsh13[2] * tsh11[0] + tsh13[0] * tsh11[2]) + (tsh11[2] * tsh13[0] + tsh11[0] * tsh13[2]));
  903. tsh21[1] = (tsh13[1] * tsh11[0] + tsh11[1] * tsh13[0]);
  904. tsh21[2] = kSqrt0304 * (tsh13[1] * tsh11[1] + tsh11[1] * tsh13[1]);
  905. tsh21[3] = (tsh13[1] * tsh11[2] + tsh11[1] * tsh13[2]);
  906. tsh21[4] = kSqrt0104 * ((tsh13[2] * tsh11[2] - tsh13[0] * tsh11[0]) + (tsh11[2] * tsh13[2] - tsh11[0] * tsh13[0]));
  907. SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh21, out1);
  908. tsh22[0] = kSqrt0104 * ((tsh12[2] * tsh11[0] + tsh12[0] * tsh11[2]) + (tsh11[2] * tsh12[0] + tsh11[0] * tsh12[2]));
  909. tsh22[1] = tsh12[1] * tsh11[0] + tsh11[1] * tsh12[0];
  910. tsh22[2] = kSqrt0304 * (tsh12[1] * tsh11[1] + tsh11[1] * tsh12[1]);
  911. tsh22[3] = tsh12[1] * tsh11[2] + tsh11[1] * tsh12[2];
  912. tsh22[4] = kSqrt0104 * ((tsh12[2] * tsh11[2] - tsh12[0] * tsh11[0]) + (tsh11[2] * tsh12[2] - tsh11[0] * tsh12[0]));
  913. SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh22, out2);
  914. tsh23[0] = kSqrt0103 * (tsh12[2] * tsh12[0] + tsh12[0] * tsh12[2]) + -kSqrt0112 *
  915. ((tsh13[2] * tsh13[0] + tsh13[0] * tsh13[2]) + (tsh11[2] * tsh11[0] + tsh11[0] * tsh11[2]));
  916. tsh23[1] = kSqrt0403 * tsh12[1] * tsh12[0] + -kSqrt0103 * (tsh13[1] * tsh13[0] + tsh11[1] * tsh11[0]);
  917. tsh23[2] = tsh12[1] * tsh12[1] + -kSqrt0104 * (tsh13[1] * tsh13[1] + tsh11[1] * tsh11[1]);
  918. tsh23[3] = kSqrt0403 * tsh12[1] * tsh12[2] + -kSqrt0103 * (tsh13[1] * tsh13[2] + tsh11[1] * tsh11[2]);
  919. tsh23[4] = kSqrt0103 * (tsh12[2] * tsh12[2] - tsh12[0] * tsh12[0]) + -kSqrt0112 *
  920. ((tsh13[2] * tsh13[2] - tsh13[0] * tsh13[0]) + (tsh11[2] * tsh11[2] - tsh11[0] * tsh11[0]));
  921. SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh23, out3);
  922. tsh24[0] = kSqrt0104 * ((tsh12[2] * tsh13[0] + tsh12[0] * tsh13[2]) + (tsh13[2] * tsh12[0] + tsh13[0] * tsh12[2]));
  923. tsh24[1] = tsh12[1] * tsh13[0] + tsh13[1] * tsh12[0];
  924. tsh24[2] = kSqrt0304 * (tsh12[1] * tsh13[1] + tsh13[1] * tsh12[1]);
  925. tsh24[3] = tsh12[1] * tsh13[2] + tsh13[1] * tsh12[2];
  926. tsh24[4] = kSqrt0104 * ((tsh12[2] * tsh13[2] - tsh12[0] * tsh13[0]) + (tsh13[2] * tsh12[2] - tsh13[0] * tsh12[0]));
  927. SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh24, out4);
  928. tsh25[0] = kSqrt0104 * ((tsh13[2] * tsh13[0] + tsh13[0] * tsh13[2]) - (tsh11[2] * tsh11[0] + tsh11[0] * tsh11[2]));
  929. tsh25[1] = (tsh13[1] * tsh13[0] - tsh11[1] * tsh11[0]);
  930. tsh25[2] = kSqrt0304 * (tsh13[1] * tsh13[1] - tsh11[1] * tsh11[1]);
  931. tsh25[3] = (tsh13[1] * tsh13[2] - tsh11[1] * tsh11[2]);
  932. tsh25[4] = kSqrt0104 * ((tsh13[2] * tsh13[2] - tsh13[0] * tsh13[0]) - (tsh11[2] * tsh11[2] - tsh11[0] * tsh11[0]));
  933. SplatBuffer.dot5(in1, in2, in3, in4, in5, tsh25, out5);
  934. };
  935. static parseHeader(buffer) {
  936. const headerArrayUint8 = new Uint8Array(buffer, 0, SplatBuffer.HeaderSizeBytes);
  937. const headerArrayUint16 = new Uint16Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 2);
  938. const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
  939. const headerArrayFloat32 = new Float32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
  940. const versionMajor = headerArrayUint8[0];
  941. const versionMinor = headerArrayUint8[1];
  942. const maxSectionCount = headerArrayUint32[1];
  943. const sectionCount = headerArrayUint32[2];
  944. const maxSplatCount = headerArrayUint32[3];
  945. const splatCount = headerArrayUint32[4];
  946. const compressionLevel = headerArrayUint16[10];
  947. const sceneCenter = new THREE.Vector3(headerArrayFloat32[6], headerArrayFloat32[7], headerArrayFloat32[8]);
  948. return {
  949. versionMajor,
  950. versionMinor,
  951. maxSectionCount,
  952. sectionCount,
  953. maxSplatCount,
  954. splatCount,
  955. compressionLevel,
  956. sceneCenter
  957. };
  958. }
  959. static writeHeaderCountsToBuffer(sectionCount, splatCount, buffer) {
  960. const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
  961. headerArrayUint32[2] = sectionCount;
  962. headerArrayUint32[4] = splatCount;
  963. }
  964. static writeHeaderToBuffer(header, buffer) {
  965. const headerArrayUint8 = new Uint8Array(buffer, 0, SplatBuffer.HeaderSizeBytes);
  966. const headerArrayUint16 = new Uint16Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 2);
  967. const headerArrayUint32 = new Uint32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
  968. const headerArrayFloat32 = new Float32Array(buffer, 0, SplatBuffer.HeaderSizeBytes / 4);
  969. headerArrayUint8[0] = header.versionMajor;
  970. headerArrayUint8[1] = header.versionMinor;
  971. headerArrayUint8[2] = 0; // unused for now
  972. headerArrayUint8[3] = 0; // unused for now
  973. headerArrayUint32[1] = header.maxSectionCount;
  974. headerArrayUint32[2] = header.sectionCount;
  975. headerArrayUint32[3] = header.maxSplatCount;
  976. headerArrayUint32[4] = header.splatCount;
  977. headerArrayUint16[10] = header.compressionLevel;
  978. headerArrayFloat32[6] = header.sceneCenter.x;
  979. headerArrayFloat32[7] = header.sceneCenter.y;
  980. headerArrayFloat32[8] = header.sceneCenter.z;
  981. }
  982. static parseSectionHeaders(header, buffer, offset = 0, secLoadedCountsToMax) {
  983. const compressionLevel = header.compressionLevel;
  984. const maxSectionCount = header.maxSectionCount;
  985. const sectionHeaderArrayUint16 = new Uint16Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 2);
  986. const sectionHeaderArrayUint32 = new Uint32Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 4);
  987. const sectionHeaderArrayFloat32 = new Float32Array(buffer, offset, maxSectionCount * SplatBuffer.SectionHeaderSizeBytes / 4);
  988. const sectionHeaders = [];
  989. let sectionHeaderBase = 0;
  990. let sectionHeaderBaseUint16 = sectionHeaderBase / 2;
  991. let sectionHeaderBaseUint32 = sectionHeaderBase / 4;
  992. let sectionBase = SplatBuffer.HeaderSizeBytes + header.maxSectionCount * SplatBuffer.SectionHeaderSizeBytes;
  993. let splatCountOffset = 0;
  994. for (let i = 0; i < maxSectionCount; i++) {
  995. const maxSplatCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 1];
  996. const bucketSize = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 2];
  997. const bucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 3];
  998. const bucketBlockSize = sectionHeaderArrayFloat32[sectionHeaderBaseUint32 + 4];
  999. const halfBucketBlockSize = bucketBlockSize / 2.0;
  1000. const bucketStorageSizeBytes = sectionHeaderArrayUint16[sectionHeaderBaseUint16 + 10];
  1001. const compressionScaleRange = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 6] ||
  1002. SplatBuffer.CompressionLevels[compressionLevel].ScaleRange;
  1003. const fullBucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 8];
  1004. const partiallyFilledBucketCount = sectionHeaderArrayUint32[sectionHeaderBaseUint32 + 9];
  1005. const bucketsMetaDataSizeBytes = partiallyFilledBucketCount * 4;
  1006. const bucketsStorageSizeBytes = bucketStorageSizeBytes * bucketCount + bucketsMetaDataSizeBytes;
  1007. const sphericalHarmonicsDegree = sectionHeaderArrayUint16[sectionHeaderBaseUint16 + 20];
  1008. const { bytesPerSplat } = SplatBuffer.calculateComponentStorage(compressionLevel, sphericalHarmonicsDegree);
  1009. const splatDataStorageSizeBytes = bytesPerSplat * maxSplatCount;
  1010. const storageSizeBytes = splatDataStorageSizeBytes + bucketsStorageSizeBytes;
  1011. const sectionHeader = {
  1012. bytesPerSplat: bytesPerSplat,
  1013. splatCountOffset: splatCountOffset,
  1014. splatCount: secLoadedCountsToMax ? maxSplatCount : 0,
  1015. maxSplatCount: maxSplatCount,
  1016. bucketSize: bucketSize,
  1017. bucketCount: bucketCount,
  1018. bucketBlockSize: bucketBlockSize,
  1019. halfBucketBlockSize: halfBucketBlockSize,
  1020. bucketStorageSizeBytes: bucketStorageSizeBytes,
  1021. bucketsStorageSizeBytes: bucketsStorageSizeBytes,
  1022. splatDataStorageSizeBytes: splatDataStorageSizeBytes,
  1023. storageSizeBytes: storageSizeBytes,
  1024. compressionScaleRange: compressionScaleRange,
  1025. compressionScaleFactor: halfBucketBlockSize / compressionScaleRange,
  1026. base: sectionBase,
  1027. bucketsBase: sectionBase + bucketsMetaDataSizeBytes,
  1028. dataBase: sectionBase + bucketsStorageSizeBytes,
  1029. fullBucketCount: fullBucketCount,
  1030. partiallyFilledBucketCount: partiallyFilledBucketCount,
  1031. sphericalHarmonicsDegree: sphericalHarmonicsDegree
  1032. };
  1033. sectionHeaders[i] = sectionHeader;
  1034. sectionBase += storageSizeBytes;
  1035. sectionHeaderBase += SplatBuffer.SectionHeaderSizeBytes;
  1036. sectionHeaderBaseUint16 = sectionHeaderBase / 2;
  1037. sectionHeaderBaseUint32 = sectionHeaderBase / 4;
  1038. splatCountOffset += maxSplatCount;
  1039. }
  1040. return sectionHeaders;
  1041. }
  1042. static writeSectionHeaderToBuffer(sectionHeader, compressionLevel, buffer, offset = 0) {
  1043. const sectionHeadeArrayUint16 = new Uint16Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 2);
  1044. const sectionHeadeArrayUint32 = new Uint32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
  1045. const sectionHeadeArrayFloat32 = new Float32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
  1046. sectionHeadeArrayUint32[0] = sectionHeader.splatCount;
  1047. sectionHeadeArrayUint32[1] = sectionHeader.maxSplatCount;
  1048. sectionHeadeArrayUint32[2] = compressionLevel >= 1 ? sectionHeader.bucketSize : 0;
  1049. sectionHeadeArrayUint32[3] = compressionLevel >= 1 ? sectionHeader.bucketCount : 0;
  1050. sectionHeadeArrayFloat32[4] = compressionLevel >= 1 ? sectionHeader.bucketBlockSize : 0.0;
  1051. sectionHeadeArrayUint16[10] = compressionLevel >= 1 ? SplatBuffer.BucketStorageSizeBytes : 0;
  1052. sectionHeadeArrayUint32[6] = compressionLevel >= 1 ? sectionHeader.compressionScaleRange : 0;
  1053. sectionHeadeArrayUint32[7] = sectionHeader.storageSizeBytes;
  1054. sectionHeadeArrayUint32[8] = compressionLevel >= 1 ? sectionHeader.fullBucketCount : 0;
  1055. sectionHeadeArrayUint32[9] = compressionLevel >= 1 ? sectionHeader.partiallyFilledBucketCount : 0;
  1056. sectionHeadeArrayUint16[20] = sectionHeader.sphericalHarmonicsDegree;
  1057. }
  1058. static writeSectionHeaderSplatCountToBuffer(splatCount, buffer, offset = 0) {
  1059. const sectionHeadeArrayUint32 = new Uint32Array(buffer, offset, SplatBuffer.SectionHeaderSizeBytes / 4);
  1060. sectionHeadeArrayUint32[0] = splatCount;
  1061. }
  1062. constructFromBuffer(bufferData, secLoadedCountsToMax) {
  1063. this.bufferData = bufferData;
  1064. this.globalSplatIndexToLocalSplatIndexMap = [];
  1065. this.globalSplatIndexToSectionMap = [];
  1066. const header = SplatBuffer.parseHeader(this.bufferData);
  1067. this.versionMajor = header.versionMajor;
  1068. this.versionMinor = header.versionMinor;
  1069. this.maxSectionCount = header.maxSectionCount;
  1070. this.sectionCount = secLoadedCountsToMax ? header.maxSectionCount : 0;
  1071. this.maxSplatCount = header.maxSplatCount;
  1072. this.splatCount = secLoadedCountsToMax ? header.maxSplatCount : 0;
  1073. this.compressionLevel = header.compressionLevel;
  1074. this.sceneCenter = new THREE.Vector3().copy(header.sceneCenter);
  1075. this.sections = SplatBuffer.parseSectionHeaders(header, this.bufferData, SplatBuffer.HeaderSizeBytes, secLoadedCountsToMax);
  1076. this.linkBufferArrays();
  1077. this.buildMaps();
  1078. }
  1079. static calculateComponentStorage(compressionLevel, sphericalHarmonicsDegree) {
  1080. const bytesPerCenter = SplatBuffer.CompressionLevels[compressionLevel].BytesPerCenter;
  1081. const bytesPerScale = SplatBuffer.CompressionLevels[compressionLevel].BytesPerScale;
  1082. const bytesPerRotation = SplatBuffer.CompressionLevels[compressionLevel].BytesPerRotation;
  1083. const bytesPerColor = SplatBuffer.CompressionLevels[compressionLevel].BytesPerColor;
  1084. const sphericalHarmonicsComponentsPerSplat = getSphericalHarmonicsComponentCountForDegree(sphericalHarmonicsDegree);
  1085. const sphericalHarmonicsBytesPerSplat = SplatBuffer.CompressionLevels[compressionLevel].BytesPerSphericalHarmonicsComponent *
  1086. sphericalHarmonicsComponentsPerSplat;
  1087. const bytesPerSplat = bytesPerCenter + bytesPerScale + bytesPerRotation +
  1088. bytesPerColor + sphericalHarmonicsBytesPerSplat;
  1089. return {
  1090. bytesPerCenter,
  1091. bytesPerScale,
  1092. bytesPerRotation,
  1093. bytesPerColor,
  1094. sphericalHarmonicsComponentsPerSplat,
  1095. sphericalHarmonicsBytesPerSplat,
  1096. bytesPerSplat
  1097. };
  1098. }
  1099. linkBufferArrays() {
  1100. for (let i = 0; i < this.maxSectionCount; i++) {
  1101. const section = this.sections[i];
  1102. section.bucketArray = new Float32Array(this.bufferData, section.bucketsBase,
  1103. section.bucketCount * SplatBuffer.BucketStorageSizeFloats);
  1104. if (section.partiallyFilledBucketCount > 0) {
  1105. section.partiallyFilledBucketLengths = new Uint32Array(this.bufferData, section.base,
  1106. section.partiallyFilledBucketCount);
  1107. }
  1108. }
  1109. }
  1110. buildMaps() {
  1111. let cumulativeSplatCount = 0;
  1112. for (let i = 0; i < this.maxSectionCount; i++) {
  1113. const section = this.sections[i];
  1114. for (let j = 0; j < section.maxSplatCount; j++) {
  1115. const globalSplatIndex = cumulativeSplatCount + j;
  1116. this.globalSplatIndexToLocalSplatIndexMap[globalSplatIndex] = j;
  1117. this.globalSplatIndexToSectionMap[globalSplatIndex] = i;
  1118. }
  1119. cumulativeSplatCount += section.maxSplatCount;
  1120. }
  1121. }
  1122. updateLoadedCounts(newSectionCount, newSplatCount) {
  1123. SplatBuffer.writeHeaderCountsToBuffer(newSectionCount, newSplatCount, this.bufferData);
  1124. this.sectionCount = newSectionCount;
  1125. this.splatCount = newSplatCount;
  1126. }
  1127. updateSectionLoadedCounts(sectionIndex, newSplatCount) {
  1128. const sectionHeaderOffset = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes * sectionIndex;
  1129. SplatBuffer.writeSectionHeaderSplatCountToBuffer(newSplatCount, this.bufferData, sectionHeaderOffset);
  1130. this.sections[sectionIndex].splatCount = newSplatCount;
  1131. }
  1132. static generateFromUncompressedSplatArrays(splatArrays, minimumAlpha, compressionLevel,
  1133. sceneCenter, blockSize, bucketSize, options = []) {
  1134. const copyBetweenBuffers = (srcBuffer, srcOffset, destBuffer, destOffset, byteCount = 0) => {
  1135. const src = new Uint8Array(srcBuffer, srcOffset);
  1136. const dest = new Uint8Array(destBuffer, destOffset);
  1137. for (let i = 0; i < byteCount; i++) {
  1138. dest[i] = src[i];
  1139. }
  1140. };
  1141. let sphericalHarmonicsDegree = 0;
  1142. for (let sa = 0; sa < splatArrays.length; sa ++) {
  1143. const splatArray = splatArrays[sa];
  1144. if (sa === 0 || splatArray.sphericalHarmonicsDegree < sphericalHarmonicsDegree) {
  1145. if (sa > 0 && splatArray.sphericalHarmonicsDegree !== sphericalHarmonicsDegree) {
  1146. const msg = 'SplatBuffer::generateFromUncompressedSplatArrays() -> ' +
  1147. 'all splat arrays must have the same spherical harmonics degree.';
  1148. throw new Error(msg);
  1149. }
  1150. sphericalHarmonicsDegree = splatArray.sphericalHarmonicsDegree;
  1151. }
  1152. }
  1153. const {bytesPerCenter, bytesPerScale, bytesPerRotation, bytesPerColor, sphericalHarmonicsComponentsPerSplat,
  1154. sphericalHarmonicsBytesPerSplat, bytesPerSplat} =
  1155. SplatBuffer.calculateComponentStorage(compressionLevel, sphericalHarmonicsDegree);
  1156. const compressionScaleRange = SplatBuffer.CompressionLevels[compressionLevel].ScaleRange;
  1157. const sectionBuffers = [];
  1158. const sectionHeaderBuffers = [];
  1159. let totalSplatCount = 0;
  1160. const tempRotation = new THREE.Quaternion();
  1161. for (let sa = 0; sa < splatArrays.length; sa ++) {
  1162. const splatArray = splatArrays[sa];
  1163. const sectionOptions = options[sa] || {};
  1164. const sectionBlockSize = (sectionOptions.blockSizeFactor || 1) * (blockSize || SplatBuffer.BucketBlockSize);
  1165. const sectionBucketSize = Math.ceil((sectionOptions.bucketSizeFactor || 1) * (bucketSize || SplatBuffer.BucketSize));
  1166. const validSplats = new UncompressedSplatArray(sphericalHarmonicsDegree);
  1167. for (let i = 0; i < splatArray.splatCount; i++) {
  1168. const targetSplat = splatArray.splats[i];
  1169. let alpha;
  1170. if (targetSplat[UncompressedSplatArray.OFFSET.OPACITY]) {
  1171. alpha = targetSplat[UncompressedSplatArray.OFFSET.OPACITY];
  1172. } else {
  1173. alpha = 255;
  1174. }
  1175. if (alpha >= minimumAlpha) {
  1176. validSplats.addSplat(targetSplat);
  1177. }
  1178. }
  1179. const bucketInfo = SplatBuffer.computeBucketsForUncompressedSplatArray(validSplats, sectionBlockSize, sectionBucketSize);
  1180. const fullBucketCount = bucketInfo.fullBuckets.length;
  1181. const partiallyFullBucketLengths = bucketInfo.partiallyFullBuckets.map((bucket) => bucket.splats.length);
  1182. const partiallyFilledBucketCount = partiallyFullBucketLengths.length;
  1183. const buckets = [...bucketInfo.fullBuckets, ...bucketInfo.partiallyFullBuckets];
  1184. const sectionDataSizeBytes = validSplats.splats.length * bytesPerSplat;
  1185. const bucketMetaDataSizeBytes = partiallyFilledBucketCount * 4;
  1186. const bucketDataBytes = compressionLevel >= 1 ? buckets.length *
  1187. SplatBuffer.BucketStorageSizeBytes + bucketMetaDataSizeBytes : 0;
  1188. const sectionSizeBytes = sectionDataSizeBytes + bucketDataBytes;
  1189. const sectionBuffer = new ArrayBuffer(sectionSizeBytes);
  1190. const blockHalfSize = sectionBlockSize / 2.0;
  1191. const compressionScaleFactor = compressionScaleRange / blockHalfSize;
  1192. const doubleCompressionScaleRange = compressionScaleRange * 2 + 1;
  1193. const tempCenterBuffer = new ArrayBuffer(bytesPerCenter);
  1194. const tempScaleBuffer = new ArrayBuffer(bytesPerScale);
  1195. const tempRotationBuffer = new ArrayBuffer(bytesPerRotation);
  1196. const tempColorBuffer = new ArrayBuffer(bytesPerColor);
  1197. const tempSHBuffer = new ArrayBuffer(sphericalHarmonicsBytesPerSplat);
  1198. const bucketCenter = new THREE.Vector3();
  1199. const bucketCenterDelta = new THREE.Vector3();
  1200. let outSplatCount = 0;
  1201. for (let b = 0; b < buckets.length; b++) {
  1202. const bucket = buckets[b];
  1203. bucketCenter.fromArray(bucket.center);
  1204. for (let i = 0; i < bucket.splats.length; i++) {
  1205. let row = bucket.splats[i];
  1206. const targetSplat = validSplats.splats[row];
  1207. const centerBase = bucketDataBytes + outSplatCount * bytesPerSplat;
  1208. const scaleBase = centerBase + bytesPerCenter;
  1209. const rotationBase = scaleBase + bytesPerScale;
  1210. const colorBase = rotationBase + bytesPerRotation;
  1211. const sphericalHarmonicsBase = colorBase + bytesPerColor;
  1212. if (compressionLevel === 0) {
  1213. const center = new Float32Array(sectionBuffer, centerBase, SplatBuffer.CenterComponentCount);
  1214. const rot = new Float32Array(sectionBuffer, rotationBase, SplatBuffer.RotationComponentCount);
  1215. const scale = new Float32Array(sectionBuffer, scaleBase, SplatBuffer.ScaleComponentCount);
  1216. if (targetSplat[UncompressedSplatArray.OFFSET.SCALE0] !== undefined) {
  1217. tempRotation.set(targetSplat[UncompressedSplatArray.OFFSET.ROTATION0],
  1218. targetSplat[UncompressedSplatArray.OFFSET.ROTATION1],
  1219. targetSplat[UncompressedSplatArray.OFFSET.ROTATION2],
  1220. targetSplat[UncompressedSplatArray.OFFSET.ROTATION3]);
  1221. tempRotation.normalize();
  1222. rot.set([tempRotation.x, tempRotation.y, tempRotation.z, tempRotation.w]);
  1223. scale.set([targetSplat[UncompressedSplatArray.OFFSET.SCALE0],
  1224. targetSplat[UncompressedSplatArray.OFFSET.SCALE1],
  1225. targetSplat[UncompressedSplatArray.OFFSET.SCALE2]]);
  1226. } else {
  1227. rot.set([1.0, 0.0, 0.0, 0.0]);
  1228. scale.set([0.01, 0.01, 0.01]);
  1229. }
  1230. center.set([targetSplat[UncompressedSplatArray.OFFSET.X],
  1231. targetSplat[UncompressedSplatArray.OFFSET.Y],
  1232. targetSplat[UncompressedSplatArray.OFFSET.Z]]);
  1233. if (sphericalHarmonicsDegree > 0) {
  1234. const shOut = new Float32Array(sectionBuffer, sphericalHarmonicsBase, sphericalHarmonicsComponentsPerSplat);
  1235. if (sphericalHarmonicsDegree >= 1) {
  1236. for (let s = 0; s < 9; s++) shOut[s] = targetSplat[UncompressedSplatArray.OFFSET.FRC0 + s];
  1237. if (sphericalHarmonicsDegree >= 2) {
  1238. for (let s = 0; s < 15; s++) shOut[s + 9] = targetSplat[UncompressedSplatArray.OFFSET.FRC9 + s];
  1239. }
  1240. }
  1241. }
  1242. } else {
  1243. const center = new Uint16Array(tempCenterBuffer, 0, SplatBuffer.CenterComponentCount);
  1244. const rot = new Uint16Array(tempRotationBuffer, 0, SplatBuffer.RotationComponentCount);
  1245. const scale = new Uint16Array(tempScaleBuffer, 0, SplatBuffer.ScaleComponentCount);
  1246. if (targetSplat[UncompressedSplatArray.OFFSET.SCALE0] !== undefined) {
  1247. tempRotation.set(targetSplat[UncompressedSplatArray.OFFSET.ROTATION0],
  1248. targetSplat[UncompressedSplatArray.OFFSET.ROTATION1],
  1249. targetSplat[UncompressedSplatArray.OFFSET.ROTATION2],
  1250. targetSplat[UncompressedSplatArray.OFFSET.ROTATION3]);
  1251. tempRotation.normalize();
  1252. rot.set([toHalfFloat(tempRotation.x), toHalfFloat(tempRotation.y),
  1253. toHalfFloat(tempRotation.z), toHalfFloat(tempRotation.w)]);
  1254. scale.set([toHalfFloat(targetSplat[UncompressedSplatArray.OFFSET.SCALE0]),
  1255. toHalfFloat(targetSplat[UncompressedSplatArray.OFFSET.SCALE1]),
  1256. toHalfFloat(targetSplat[UncompressedSplatArray.OFFSET.SCALE2])]);
  1257. } else {
  1258. rot.set([toHalfFloat(1.), 0, 0, 0]);
  1259. scale.set([toHalfFloat(0.01), toHalfFloat(0.01), toHalfFloat(0.01)]);
  1260. }
  1261. bucketCenterDelta.set(targetSplat[UncompressedSplatArray.OFFSET.X],
  1262. targetSplat[UncompressedSplatArray.OFFSET.Y],
  1263. targetSplat[UncompressedSplatArray.OFFSET.Z]).sub(bucketCenter);
  1264. bucketCenterDelta.x = Math.round(bucketCenterDelta.x * compressionScaleFactor) + compressionScaleRange;
  1265. bucketCenterDelta.x = clamp(bucketCenterDelta.x, 0, doubleCompressionScaleRange);
  1266. bucketCenterDelta.y = Math.round(bucketCenterDelta.y * compressionScaleFactor) + compressionScaleRange;
  1267. bucketCenterDelta.y = clamp(bucketCenterDelta.y, 0, doubleCompressionScaleRange);
  1268. bucketCenterDelta.z = Math.round(bucketCenterDelta.z * compressionScaleFactor) + compressionScaleRange;
  1269. bucketCenterDelta.z = clamp(bucketCenterDelta.z, 0, doubleCompressionScaleRange);
  1270. center.set([bucketCenterDelta.x, bucketCenterDelta.y, bucketCenterDelta.z]);
  1271. if (sphericalHarmonicsDegree > 0) {
  1272. const SHArrayType = compressionLevel === 1 ? Uint16Array : Uint8Array;
  1273. const bytesPerSHComponent = compressionLevel === 1 ? 2 : 1;
  1274. const shOut = new SHArrayType(tempSHBuffer, 0, sphericalHarmonicsComponentsPerSplat);
  1275. if (sphericalHarmonicsDegree >= 1) {
  1276. for (let s = 0; s < 9; s++) {
  1277. const srcVal = targetSplat[UncompressedSplatArray.OFFSET.FRC0 + s];
  1278. shOut[s] = compressionLevel === 1 ? toHalfFloat(srcVal) : toUint8(srcVal);
  1279. }
  1280. const degree1ByteCount = 9 * bytesPerSHComponent;
  1281. copyBetweenBuffers(shOut.buffer, 0, sectionBuffer, sphericalHarmonicsBase, degree1ByteCount);
  1282. if (sphericalHarmonicsDegree >= 2) {
  1283. for (let s = 0; s < 15; s++) {
  1284. const srcVal = targetSplat[UncompressedSplatArray.OFFSET.FRC9 + s];
  1285. shOut[s + 9] = compressionLevel === 1 ? toHalfFloat(srcVal) : toUint8(srcVal);
  1286. }
  1287. const degree2ByteCount = 15 * bytesPerSHComponent;
  1288. copyBetweenBuffers(shOut.buffer, degree1ByteCount, sectionBuffer,
  1289. sphericalHarmonicsBase + degree1ByteCount, degree2ByteCount);
  1290. }
  1291. }
  1292. }
  1293. copyBetweenBuffers(center.buffer, 0, sectionBuffer, centerBase, 6);
  1294. copyBetweenBuffers(scale.buffer, 0, sectionBuffer, scaleBase, 6);
  1295. copyBetweenBuffers(rot.buffer, 0, sectionBuffer, rotationBase, 8);
  1296. }
  1297. const rgba = new Uint8ClampedArray(tempColorBuffer, 0, 4);
  1298. if (targetSplat[UncompressedSplatArray.OFFSET.FDC0] !== undefined) {
  1299. rgba.set([targetSplat[UncompressedSplatArray.OFFSET.FDC0],
  1300. targetSplat[UncompressedSplatArray.OFFSET.FDC1],
  1301. targetSplat[UncompressedSplatArray.OFFSET.FDC2]]);
  1302. } else {
  1303. rgba.set([255, 0, 0]);
  1304. }
  1305. if (targetSplat[UncompressedSplatArray.OFFSET.OPACITY] !== undefined) {
  1306. rgba[3] = targetSplat[UncompressedSplatArray.OFFSET.OPACITY];
  1307. } else {
  1308. rgba[3] = 255;
  1309. }
  1310. copyBetweenBuffers(rgba.buffer, 0, sectionBuffer, colorBase, 4);
  1311. outSplatCount++;
  1312. }
  1313. }
  1314. totalSplatCount += outSplatCount;
  1315. if (compressionLevel >= 1) {
  1316. const bucketMetaDataArray = new Uint32Array(sectionBuffer, 0, partiallyFullBucketLengths.length * 4);
  1317. for (let pfb = 0; pfb < partiallyFullBucketLengths.length; pfb ++) {
  1318. bucketMetaDataArray[pfb] = partiallyFullBucketLengths[pfb];
  1319. }
  1320. const bucketArray = new Float32Array(sectionBuffer, bucketMetaDataSizeBytes,
  1321. buckets.length * SplatBuffer.BucketStorageSizeFloats);
  1322. for (let b = 0; b < buckets.length; b++) {
  1323. const bucket = buckets[b];
  1324. const base = b * 3;
  1325. bucketArray[base] = bucket.center[0];
  1326. bucketArray[base + 1] = bucket.center[1];
  1327. bucketArray[base + 2] = bucket.center[2];
  1328. }
  1329. }
  1330. sectionBuffers.push(sectionBuffer);
  1331. const sectionHeaderBuffer = new ArrayBuffer(SplatBuffer.SectionHeaderSizeBytes);
  1332. SplatBuffer.writeSectionHeaderToBuffer({
  1333. maxSplatCount: outSplatCount,
  1334. splatCount: outSplatCount,
  1335. bucketSize: sectionBucketSize,
  1336. bucketCount: buckets.length,
  1337. bucketBlockSize: sectionBlockSize,
  1338. compressionScaleRange: compressionScaleRange,
  1339. storageSizeBytes: sectionSizeBytes,
  1340. fullBucketCount: fullBucketCount,
  1341. partiallyFilledBucketCount: partiallyFilledBucketCount,
  1342. sphericalHarmonicsDegree: sphericalHarmonicsDegree
  1343. }, compressionLevel, sectionHeaderBuffer, 0);
  1344. sectionHeaderBuffers.push(sectionHeaderBuffer);
  1345. }
  1346. let sectionsCumulativeSizeBytes = 0;
  1347. for (let sectionBuffer of sectionBuffers) sectionsCumulativeSizeBytes += sectionBuffer.byteLength;
  1348. const unifiedBufferSize = SplatBuffer.HeaderSizeBytes +
  1349. SplatBuffer.SectionHeaderSizeBytes * sectionBuffers.length + sectionsCumulativeSizeBytes;
  1350. const unifiedBuffer = new ArrayBuffer(unifiedBufferSize);
  1351. SplatBuffer.writeHeaderToBuffer({
  1352. versionMajor: 0,
  1353. versionMinor: 1,
  1354. maxSectionCount: sectionBuffers.length,
  1355. sectionCount: sectionBuffers.length,
  1356. maxSplatCount: totalSplatCount,
  1357. splatCount: totalSplatCount,
  1358. compressionLevel: compressionLevel,
  1359. sceneCenter: sceneCenter
  1360. }, unifiedBuffer);
  1361. let currentUnifiedBase = SplatBuffer.HeaderSizeBytes;
  1362. for (let sectionHeaderBuffer of sectionHeaderBuffers) {
  1363. new Uint8Array(unifiedBuffer, currentUnifiedBase, SplatBuffer.SectionHeaderSizeBytes).set(new Uint8Array(sectionHeaderBuffer));
  1364. currentUnifiedBase += SplatBuffer.SectionHeaderSizeBytes;
  1365. }
  1366. for (let sectionBuffer of sectionBuffers) {
  1367. new Uint8Array(unifiedBuffer, currentUnifiedBase, sectionBuffer.byteLength).set(new Uint8Array(sectionBuffer));
  1368. currentUnifiedBase += sectionBuffer.byteLength;
  1369. }
  1370. const splatBuffer = new SplatBuffer(unifiedBuffer);
  1371. return splatBuffer;
  1372. }
  1373. static computeBucketsForUncompressedSplatArray(splatArray, blockSize, bucketSize) {
  1374. let splatCount = splatArray.splatCount;
  1375. const halfBlockSize = blockSize / 2.0;
  1376. const min = new THREE.Vector3();
  1377. const max = new THREE.Vector3();
  1378. for (let i = 0; i < splatCount; i++) {
  1379. const targetSplat = splatArray.splats[i];
  1380. const center = [targetSplat[UncompressedSplatArray.OFFSET.X],
  1381. targetSplat[UncompressedSplatArray.OFFSET.Y],
  1382. targetSplat[UncompressedSplatArray.OFFSET.Z]];
  1383. if (i === 0 || center[0] < min.x) min.x = center[0];
  1384. if (i === 0 || center[0] > max.x) max.x = center[0];
  1385. if (i === 0 || center[1] < min.y) min.y = center[1];
  1386. if (i === 0 || center[1] > max.y) max.y = center[1];
  1387. if (i === 0 || center[2] < min.z) min.z = center[2];
  1388. if (i === 0 || center[2] > max.z) max.z = center[2];
  1389. }
  1390. const dimensions = new THREE.Vector3().copy(max).sub(min);
  1391. const yBlocks = Math.ceil(dimensions.y / blockSize);
  1392. const zBlocks = Math.ceil(dimensions.z / blockSize);
  1393. const blockCenter = new THREE.Vector3();
  1394. const fullBuckets = [];
  1395. const partiallyFullBuckets = {};
  1396. for (let i = 0; i < splatCount; i++) {
  1397. const targetSplat = splatArray.splats[i];
  1398. const center = [targetSplat[UncompressedSplatArray.OFFSET.X],
  1399. targetSplat[UncompressedSplatArray.OFFSET.Y],
  1400. targetSplat[UncompressedSplatArray.OFFSET.Z]];
  1401. const xBlock = Math.floor((center[0] - min.x) / blockSize);
  1402. const yBlock = Math.floor((center[1] - min.y) / blockSize);
  1403. const zBlock = Math.floor((center[2] - min.z) / blockSize);
  1404. blockCenter.x = xBlock * blockSize + min.x + halfBlockSize;
  1405. blockCenter.y = yBlock * blockSize + min.y + halfBlockSize;
  1406. blockCenter.z = zBlock * blockSize + min.z + halfBlockSize;
  1407. const bucketId = xBlock * (yBlocks * zBlocks) + yBlock * zBlocks + zBlock;
  1408. let bucket = partiallyFullBuckets[bucketId];
  1409. if (!bucket) {
  1410. partiallyFullBuckets[bucketId] = bucket = {
  1411. 'splats': [],
  1412. 'center': blockCenter.toArray()
  1413. };
  1414. }
  1415. bucket.splats.push(i);
  1416. if (bucket.splats.length >= bucketSize) {
  1417. fullBuckets.push(bucket);
  1418. partiallyFullBuckets[bucketId] = null;
  1419. }
  1420. }
  1421. const partiallyFullBucketArray = [];
  1422. for (let bucketId in partiallyFullBuckets) {
  1423. if (partiallyFullBuckets.hasOwnProperty(bucketId)) {
  1424. const bucket = partiallyFullBuckets[bucketId];
  1425. if (bucket) {
  1426. partiallyFullBucketArray.push(bucket);
  1427. }
  1428. }
  1429. }
  1430. return {
  1431. 'fullBuckets': fullBuckets,
  1432. 'partiallyFullBuckets': partiallyFullBucketArray,
  1433. };
  1434. }
  1435. }
  1436. const HeaderMagicBytes = new Uint8Array([112, 108, 121, 10]);
  1437. const HeaderEndTokenBytes = new Uint8Array([10, 101, 110, 100, 95, 104, 101, 97, 100, 101, 114, 10]);
  1438. const HeaderEndToken = 'end_header';
  1439. const DataTypeMap = new Map([
  1440. ['char', Int8Array],
  1441. ['uchar', Uint8Array],
  1442. ['short', Int16Array],
  1443. ['ushort', Uint16Array],
  1444. ['int', Int32Array],
  1445. ['uint', Uint32Array],
  1446. ['float', Float32Array],
  1447. ['double', Float64Array],
  1448. ]);
  1449. const unpackUnorm = (value, bits) => {
  1450. const t = (1 << bits) - 1;
  1451. return (value & t) / t;
  1452. };
  1453. const unpack111011 = (result, value) => {
  1454. result.x = unpackUnorm(value >>> 21, 11);
  1455. result.y = unpackUnorm(value >>> 11, 10);
  1456. result.z = unpackUnorm(value, 11);
  1457. };
  1458. const unpack8888 = (result, value) => {
  1459. result.x = unpackUnorm(value >>> 24, 8);
  1460. result.y = unpackUnorm(value >>> 16, 8);
  1461. result.z = unpackUnorm(value >>> 8, 8);
  1462. result.w = unpackUnorm(value, 8);
  1463. };
  1464. // unpack quaternion with 2,10,10,10 format (largest element, 3x10bit element)
  1465. const unpackRot = (result, value) => {
  1466. const norm = 1.0 / (Math.sqrt(2) * 0.5);
  1467. const a = (unpackUnorm(value >>> 20, 10) - 0.5) * norm;
  1468. const b = (unpackUnorm(value >>> 10, 10) - 0.5) * norm;
  1469. const c = (unpackUnorm(value, 10) - 0.5) * norm;
  1470. const m = Math.sqrt(1.0 - (a * a + b * b + c * c));
  1471. switch (value >>> 30) {
  1472. case 0:
  1473. result.set(m, a, b, c);
  1474. break;
  1475. case 1:
  1476. result.set(a, m, b, c);
  1477. break;
  1478. case 2:
  1479. result.set(a, b, m, c);
  1480. break;
  1481. case 3:
  1482. result.set(a, b, c, m);
  1483. break;
  1484. }
  1485. };
  1486. const lerp = (a, b, t) => {
  1487. return a * (1 - t) + b * t;
  1488. };
  1489. const getElementPropStorage = (element, name) => {
  1490. return element.properties.find((p) => p.name === name && p.storage)
  1491. ?.storage;
  1492. };
  1493. class CompressedPlyParser {
  1494. static decodeHeaderText(headerText) {
  1495. let element;
  1496. let chunkElement;
  1497. let vertexElement;
  1498. const headerLines = headerText.split('\n').filter((line) => !line.startsWith('comment '));
  1499. let bytesPerSplat = 0;
  1500. let done = false;
  1501. for (let i = 1; i < headerLines.length; ++i) {
  1502. const words = headerLines[i].split(' ');
  1503. switch (words[0]) {
  1504. case 'format':
  1505. if (words[1] !== 'binary_little_endian') {
  1506. throw new Error('Unsupported ply format');
  1507. }
  1508. break;
  1509. case 'element':
  1510. element = {
  1511. name: words[1],
  1512. count: parseInt(words[2], 10),
  1513. properties: [],
  1514. storageSizeBytes: 0
  1515. };
  1516. if (element.name === 'chunk') chunkElement = element;
  1517. else if (element.name === 'vertex') vertexElement = element;
  1518. break;
  1519. case 'property': {
  1520. if (!DataTypeMap.has(words[1])) {
  1521. throw new Error(
  1522. `Unrecognized property data type '${words[1]}' in ply header`
  1523. );
  1524. }
  1525. const StorageType = DataTypeMap.get(words[1]);
  1526. const storageSizeByes = StorageType.BYTES_PER_ELEMENT * element.count;
  1527. if (element.name === 'vertex') bytesPerSplat += StorageType.BYTES_PER_ELEMENT;
  1528. element.properties.push({
  1529. type: words[1],
  1530. name: words[2],
  1531. storage: null,
  1532. byteSize: StorageType.BYTES_PER_ELEMENT,
  1533. storageSizeByes: storageSizeByes
  1534. });
  1535. element.storageSizeBytes += storageSizeByes;
  1536. break;
  1537. }
  1538. case HeaderEndToken:
  1539. done = true;
  1540. break;
  1541. default:
  1542. throw new Error(
  1543. `Unrecognized header value '${words[0]}' in ply header`
  1544. );
  1545. }
  1546. if (done) break;
  1547. }
  1548. return {
  1549. 'chunkElement': chunkElement,
  1550. 'vertexElement': vertexElement,
  1551. 'bytesPerSplat': bytesPerSplat,
  1552. 'headerSizeBytes': headerText.indexOf(HeaderEndToken) + HeaderEndToken.length + 1,
  1553. };
  1554. }
  1555. static decodeHeader(plyBuffer) {
  1556. /**
  1557. * Searches for the first occurrence of a sequence within a buffer.
  1558. * @example
  1559. * find(new Uint8Array([1, 2, 3, 4]), new Uint8Array([3, 4])); // 2
  1560. * @param {Uint8Array} buf - The buffer in which to search.
  1561. * @param {Uint8Array} search - The sequence to search for.
  1562. * @return {number} The index of the first occurrence of the search sequence in the buffer, or -1 if not found.
  1563. */
  1564. const find = (buf, search) => {
  1565. const endIndex = buf.length - search.length;
  1566. let i;
  1567. let j;
  1568. for (i = 0; i <= endIndex; ++i) {
  1569. for (j = 0; j < search.length; ++j) {
  1570. if (buf[i + j] !== search[j]) {
  1571. break;
  1572. }
  1573. }
  1574. if (j === search.length) {
  1575. return i;
  1576. }
  1577. }
  1578. return -1;
  1579. };
  1580. /**
  1581. * Checks if array 'a' starts with the same elements as array 'b'.
  1582. * @example
  1583. * startsWith(new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2])); // true
  1584. * @param {Uint8Array} a - The array to check against.
  1585. * @param {Uint8Array} b - The array of elements to look for at the start of 'a'.
  1586. * @return {boolean} - True if 'a' starts with all elements of 'b', otherwise false.
  1587. */
  1588. const startsWith = (a, b) => {
  1589. if (a.length < b.length) {
  1590. return false;
  1591. }
  1592. for (let i = 0; i < b.length; ++i) {
  1593. if (a[i] !== b[i]) {
  1594. return false;
  1595. }
  1596. }
  1597. return true;
  1598. };
  1599. let buf = new Uint8Array(plyBuffer);
  1600. let endHeaderTokenOffset;
  1601. if (buf.length >= HeaderMagicBytes.length && !startsWith(buf, HeaderMagicBytes)) {
  1602. throw new Error('Invalid PLY header');
  1603. }
  1604. endHeaderTokenOffset = find(buf, HeaderEndTokenBytes);
  1605. if (endHeaderTokenOffset === -1) {
  1606. throw new Error('End of PLY header not found');
  1607. }
  1608. const headerText = new TextDecoder('ascii').decode(
  1609. buf.slice(0, endHeaderTokenOffset)
  1610. );
  1611. const {chunkElement, vertexElement, bytesPerSplat} = CompressedPlyParser.decodeHeaderText(headerText);
  1612. return {
  1613. 'headerSizeBytes': endHeaderTokenOffset + HeaderEndTokenBytes.length,
  1614. 'bytesPerSplat': bytesPerSplat,
  1615. 'chunkElement': chunkElement,
  1616. 'vertexElement': vertexElement
  1617. };
  1618. }
  1619. static readElementData(element, readBuffer, readOffset, fromIndex, toIndex, propertyFilter = null) {
  1620. let dataView = readBuffer instanceof DataView ? readBuffer : new DataView(readBuffer);
  1621. fromIndex = fromIndex || 0;
  1622. toIndex = toIndex || element.count - 1;
  1623. for (let e = fromIndex; e <= toIndex; ++e) {
  1624. for (let j = 0; j < element.properties.length; ++j) {
  1625. const property = element.properties[j];
  1626. const StorageType = DataTypeMap.get(property.type);
  1627. const requiredStorageSizeBytes = StorageType.BYTES_PER_ELEMENT * element.count;
  1628. if ((!property.storage || property.storage.byteLength < requiredStorageSizeBytes) &&
  1629. (!propertyFilter || propertyFilter(property.name))) {
  1630. property.storage = new StorageType(element.count);
  1631. }
  1632. if (property.storage) {
  1633. switch (property.type) {
  1634. case 'char':
  1635. property.storage[e] = dataView.getInt8(readOffset);
  1636. break;
  1637. case 'uchar':
  1638. property.storage[e] = dataView.getUint8(readOffset);
  1639. break;
  1640. case 'short':
  1641. property.storage[e] = dataView.getInt16(readOffset, true);
  1642. break;
  1643. case 'ushort':
  1644. property.storage[e] = dataView.getUint16(readOffset, true);
  1645. break;
  1646. case 'int':
  1647. property.storage[e] = dataView.getInt32(readOffset, true);
  1648. break;
  1649. case 'uint':
  1650. property.storage[e] = dataView.getUint32(readOffset, true);
  1651. break;
  1652. case 'float':
  1653. property.storage[e] = dataView.getFloat32(readOffset, true);
  1654. break;
  1655. case 'double':
  1656. property.storage[e] = dataView.getFloat64(readOffset, true);
  1657. break;
  1658. }
  1659. }
  1660. readOffset += property.byteSize;
  1661. }
  1662. }
  1663. return readOffset;
  1664. }
  1665. static readPly(plyBuffer, propertyFilter = null) {
  1666. const header = CompressedPlyParser.decodeHeader(plyBuffer);
  1667. let readIndex = CompressedPlyParser.readElementData(header.chunkElement, plyBuffer, header.headerSizeBytes, null, null, propertyFilter);
  1668. CompressedPlyParser.readElementData(header.vertexElement, plyBuffer, readIndex, null, null, propertyFilter);
  1669. return {
  1670. 'chunkElement': header.chunkElement,
  1671. 'vertexElement': header.vertexElement
  1672. };
  1673. }
  1674. static getElementStorageArrays(chunkElement, vertexElement) {
  1675. const minX = getElementPropStorage(chunkElement, 'min_x');
  1676. const minY = getElementPropStorage(chunkElement, 'min_y');
  1677. const minZ = getElementPropStorage(chunkElement, 'min_z');
  1678. const maxX = getElementPropStorage(chunkElement, 'max_x');
  1679. const maxY = getElementPropStorage(chunkElement, 'max_y');
  1680. const maxZ = getElementPropStorage(chunkElement, 'max_z');
  1681. const minScaleX = getElementPropStorage(chunkElement, 'min_scale_x');
  1682. const minScaleY = getElementPropStorage(chunkElement, 'min_scale_y');
  1683. const minScaleZ = getElementPropStorage(chunkElement, 'min_scale_z');
  1684. const maxScaleX = getElementPropStorage(chunkElement, 'max_scale_x');
  1685. const maxScaleY = getElementPropStorage(chunkElement, 'max_scale_y');
  1686. const maxScaleZ = getElementPropStorage(chunkElement, 'max_scale_z');
  1687. const position = getElementPropStorage(vertexElement, 'packed_position');
  1688. const rotation = getElementPropStorage(vertexElement, 'packed_rotation');
  1689. const scale = getElementPropStorage(vertexElement, 'packed_scale');
  1690. const color = getElementPropStorage(vertexElement, 'packed_color');
  1691. return {
  1692. positionExtremes: {
  1693. minX, maxX,
  1694. minY, maxY,
  1695. minZ, maxZ
  1696. },
  1697. scaleExtremes: {
  1698. minScaleX, maxScaleX, minScaleY,
  1699. maxScaleY, minScaleZ, maxScaleZ
  1700. },
  1701. position,
  1702. rotation,
  1703. scale,
  1704. color
  1705. };
  1706. }
  1707. static decompressSplat = function() {
  1708. const p = new THREE.Vector3();
  1709. const r = new THREE.Quaternion();
  1710. const s = new THREE.Vector3();
  1711. const c = new THREE.Vector4();
  1712. const OFFSET = UncompressedSplatArray.OFFSET;
  1713. return function(index, chunkSplatIndexOffset, positionArray, positionExtremes, scaleArray, scaleExtremes,
  1714. rotationArray, colorArray, outSplat) {
  1715. outSplat = outSplat || UncompressedSplatArray.createSplat();
  1716. const chunkIndex = Math.floor((chunkSplatIndexOffset + index) / 256);
  1717. unpack111011(p, positionArray[index]);
  1718. unpackRot(r, rotationArray[index]);
  1719. unpack111011(s, scaleArray[index]);
  1720. unpack8888(c, colorArray[index]);
  1721. outSplat[OFFSET.X] = lerp(positionExtremes.minX[chunkIndex], positionExtremes.maxX[chunkIndex], p.x);
  1722. outSplat[OFFSET.Y] = lerp(positionExtremes.minY[chunkIndex], positionExtremes.maxY[chunkIndex], p.y);
  1723. outSplat[OFFSET.Z] = lerp(positionExtremes.minZ[chunkIndex], positionExtremes.maxZ[chunkIndex], p.z);
  1724. outSplat[OFFSET.ROTATION0] = r.x;
  1725. outSplat[OFFSET.ROTATION1] = r.y;
  1726. outSplat[OFFSET.ROTATION2] = r.z;
  1727. outSplat[OFFSET.ROTATION3] = r.w;
  1728. outSplat[OFFSET.SCALE0] = Math.exp(lerp(scaleExtremes.minScaleX[chunkIndex], scaleExtremes.maxScaleX[chunkIndex], s.x));
  1729. outSplat[OFFSET.SCALE1] = Math.exp(lerp(scaleExtremes.minScaleY[chunkIndex], scaleExtremes.maxScaleY[chunkIndex], s.y));
  1730. outSplat[OFFSET.SCALE2] = Math.exp(lerp(scaleExtremes.minScaleZ[chunkIndex], scaleExtremes.maxScaleZ[chunkIndex], s.z));
  1731. outSplat[OFFSET.FDC0] = clamp(Math.floor(c.x * 255), 0, 255);
  1732. outSplat[OFFSET.FDC1] = clamp(Math.floor(c.y * 255), 0, 255);
  1733. outSplat[OFFSET.FDC2] = clamp(Math.floor(c.z * 255), 0, 255);
  1734. outSplat[OFFSET.OPACITY] = clamp(Math.floor(c.w * 255), 0, 255);
  1735. return outSplat;
  1736. };
  1737. }();
  1738. static parseToUncompressedSplatBufferSection(chunkElement, vertexElement, fromIndex, toIndex, chunkSplatIndexOffset,
  1739. vertexDataBuffer, veretxReadOffset, outBuffer, outOffset, propertyFilter = null) {
  1740. CompressedPlyParser.readElementData(vertexElement, vertexDataBuffer, veretxReadOffset, fromIndex, toIndex, propertyFilter);
  1741. const outBytesPerCenter = SplatBuffer.CompressionLevels[0].BytesPerCenter;
  1742. const outBytesPerScale = SplatBuffer.CompressionLevels[0].BytesPerScale;
  1743. const outBytesPerRotation = SplatBuffer.CompressionLevels[0].BytesPerRotation;
  1744. const outBytesPerSplat = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[0].BytesPerSplat;
  1745. const { positionExtremes, scaleExtremes, position, rotation, scale, color } =
  1746. CompressedPlyParser.getElementStorageArrays(chunkElement, vertexElement);
  1747. const OFFSET = UncompressedSplatArray.OFFSET;
  1748. const tempSplat = UncompressedSplatArray.createSplat();
  1749. for (let i = fromIndex; i <= toIndex; ++i) {
  1750. CompressedPlyParser.decompressSplat(i, chunkSplatIndexOffset, position, positionExtremes,
  1751. scale, scaleExtremes, rotation, color, tempSplat);
  1752. const outBase = i * outBytesPerSplat + outOffset;
  1753. const outCenter = new Float32Array(outBuffer, outBase, 3);
  1754. const outScale = new Float32Array(outBuffer, outBase + outBytesPerCenter, 3);
  1755. const outRotation = new Float32Array(outBuffer, outBase + outBytesPerCenter + outBytesPerScale, 4);
  1756. const outColor = new Uint8Array(outBuffer, outBase + outBytesPerCenter + outBytesPerScale + outBytesPerRotation, 4);
  1757. outCenter[0] = tempSplat[OFFSET.X];
  1758. outCenter[1] = tempSplat[OFFSET.Y];
  1759. outCenter[2] = tempSplat[OFFSET.Z];
  1760. outScale[0] = tempSplat[OFFSET.SCALE0];
  1761. outScale[1] = tempSplat[OFFSET.SCALE1];
  1762. outScale[2] = tempSplat[OFFSET.SCALE2];
  1763. outRotation[0] = tempSplat[OFFSET.ROTATION0];
  1764. outRotation[1] = tempSplat[OFFSET.ROTATION1];
  1765. outRotation[2] = tempSplat[OFFSET.ROTATION2];
  1766. outRotation[3] = tempSplat[OFFSET.ROTATION3];
  1767. outColor[0] = tempSplat[OFFSET.FDC0];
  1768. outColor[1] = tempSplat[OFFSET.FDC1];
  1769. outColor[2] = tempSplat[OFFSET.FDC2];
  1770. outColor[3] = tempSplat[OFFSET.OPACITY];
  1771. }
  1772. }
  1773. static parseToUncompressedSplatArray(plyBuffer) {
  1774. const { chunkElement, vertexElement } = CompressedPlyParser.readPly(plyBuffer);
  1775. const splatArray = new UncompressedSplatArray();
  1776. const { positionExtremes, scaleExtremes, position, rotation, scale, color } =
  1777. CompressedPlyParser.getElementStorageArrays(chunkElement, vertexElement);
  1778. for (let i = 0; i < vertexElement.count; ++i) {
  1779. splatArray.addDefaultSplat();
  1780. const newSplat = splatArray.getSplat(splatArray.splatCount - 1);
  1781. CompressedPlyParser.decompressSplat(i, 0, position, positionExtremes, scale, scaleExtremes, rotation, color, newSplat);
  1782. }
  1783. const mat = new THREE.Matrix4();
  1784. mat.identity();
  1785. return splatArray;
  1786. }
  1787. }
  1788. class PlyParser {
  1789. static HeaderEndToken = 'end_header';
  1790. static BaseFields = ['scale_0', 'scale_1', 'scale_2', 'rot_0', 'rot_1', 'rot_2', 'rot_3',
  1791. 'x', 'y', 'z', 'f_dc_0', 'f_dc_1', 'f_dc_2', 'red', 'green', 'blue', 'opacity'];
  1792. static SphericalHarmonicsFields = Array.from(Array(45)).map((e, i) => (`f_rest_${i}`));
  1793. static Fields = [[...PlyParser.BaseFields], [...PlyParser.BaseFields, ...PlyParser.SphericalHarmonicsFields]];
  1794. static checkTextForEndHeader(endHeaderTestText) {
  1795. if (endHeaderTestText.includes(PlyParser.HeaderEndToken)) {
  1796. return true;
  1797. }
  1798. return false;
  1799. }
  1800. static checkBufferForEndHeader(buffer, searchOfset, chunkSize, decoder) {
  1801. const endHeaderTestChunk = new Uint8Array(buffer, Math.max(0, searchOfset - chunkSize), chunkSize);
  1802. const endHeaderTestText = decoder.decode(endHeaderTestChunk);
  1803. return PlyParser.checkTextForEndHeader(endHeaderTestText);
  1804. }
  1805. static decodeHeaderText(headerText) {
  1806. const headerLines = headerText.split('\n');
  1807. const prunedLines = [];
  1808. let splatCount = 0;
  1809. let propertyTypes = {};
  1810. let compressed = false;
  1811. for (let i = 0; i < headerLines.length; i++) {
  1812. const line = headerLines[i].trim();
  1813. prunedLines.push(line);
  1814. if (line.startsWith('element chunk') || line.match(/[A-Za-z]*packed_[A-Za-z]*/)) {
  1815. compressed = true;
  1816. } else if (line.startsWith('element vertex')) {
  1817. const splatCountMatch = line.match(/\d+/);
  1818. if (splatCountMatch) {
  1819. splatCount = parseInt(splatCountMatch[0]);
  1820. }
  1821. } else if (line.startsWith('property')) {
  1822. const propertyMatch = line.match(/(\w+)\s+(\w+)\s+(\w+)/);
  1823. if (propertyMatch) {
  1824. const propertyType = propertyMatch[2];
  1825. const propertyName = propertyMatch[3];
  1826. propertyTypes[propertyName] = propertyType;
  1827. }
  1828. } else if (line === PlyParser.HeaderEndToken) {
  1829. break;
  1830. }
  1831. }
  1832. let bytesPerSplat = 0;
  1833. let fieldOffsets = {};
  1834. const fieldSize = {
  1835. 'double': 8,
  1836. 'int': 4,
  1837. 'uint': 4,
  1838. 'float': 4,
  1839. 'short': 2,
  1840. 'ushort': 2,
  1841. 'uchar': 1,
  1842. };
  1843. const fieldNames = [];
  1844. for (let fieldName in propertyTypes) {
  1845. if (propertyTypes.hasOwnProperty(fieldName)) {
  1846. fieldNames.push(fieldName);
  1847. const type = propertyTypes[fieldName];
  1848. fieldOffsets[fieldName] = bytesPerSplat;
  1849. bytesPerSplat += fieldSize[type];
  1850. }
  1851. }
  1852. let sphericalHarmonicsFieldCount = 0;
  1853. let sphericalHarmonicsCoefficientsPerChannel = 0;
  1854. for (let fieldName of fieldNames) {
  1855. if (fieldName.startsWith('f_rest')) sphericalHarmonicsFieldCount++;
  1856. }
  1857. sphericalHarmonicsCoefficientsPerChannel = sphericalHarmonicsFieldCount / 3;
  1858. let sphericalHarmonicsDegree = 0;
  1859. if (sphericalHarmonicsCoefficientsPerChannel >= 3) sphericalHarmonicsDegree = 1;
  1860. if (sphericalHarmonicsCoefficientsPerChannel >= 8) sphericalHarmonicsDegree = 2;
  1861. let sphericalHarmonicsDegree1Fields = [];
  1862. if (sphericalHarmonicsDegree >= 1) {
  1863. for (let rgb = 0; rgb < 3; rgb++) {
  1864. for (let i = 0; i < 3; i++) {
  1865. sphericalHarmonicsDegree1Fields.push('f_rest_' + (i + sphericalHarmonicsCoefficientsPerChannel * rgb));
  1866. }
  1867. }
  1868. }
  1869. let sphericalHarmonicsDegree2Fields = [];
  1870. if (sphericalHarmonicsDegree >= 2) {
  1871. for (let rgb = 0; rgb < 3; rgb++) {
  1872. for (let i = 0; i < 5; i++) {
  1873. sphericalHarmonicsDegree2Fields.push('f_rest_' + (i + sphericalHarmonicsCoefficientsPerChannel * rgb + 3));
  1874. }
  1875. }
  1876. }
  1877. return {
  1878. 'splatCount': splatCount,
  1879. 'propertyTypes': propertyTypes,
  1880. 'compressed': compressed,
  1881. 'headerText': headerText,
  1882. 'headerLines': prunedLines,
  1883. 'headerSizeBytes': headerText.indexOf(PlyParser.HeaderEndToken) + PlyParser.HeaderEndToken.length + 1,
  1884. 'bytesPerSplat': bytesPerSplat,
  1885. 'fieldOffsets': fieldOffsets,
  1886. 'sphericalHarmonicsDegree': sphericalHarmonicsDegree,
  1887. 'sphericalHarmonicsCoefficientsPerChannel': sphericalHarmonicsCoefficientsPerChannel,
  1888. 'sphericalHarmonicsDegree1Fields': sphericalHarmonicsDegree1Fields,
  1889. 'sphericalHarmonicsDegree2Fields': sphericalHarmonicsDegree2Fields
  1890. };
  1891. }
  1892. static decodeHeadeFromBuffer(plyBuffer) {
  1893. const decoder = new TextDecoder();
  1894. let headerOffset = 0;
  1895. let headerText = '';
  1896. const readChunkSize = 100;
  1897. while (true) {
  1898. if (headerOffset + readChunkSize >= plyBuffer.byteLength) {
  1899. throw new Error('End of file reached while searching for end of header');
  1900. }
  1901. const headerChunk = new Uint8Array(plyBuffer, headerOffset, readChunkSize);
  1902. headerText += decoder.decode(headerChunk);
  1903. headerOffset += readChunkSize;
  1904. if (PlyParser.checkBufferForEndHeader(plyBuffer, headerOffset, readChunkSize * 2, decoder)) {
  1905. break;
  1906. }
  1907. }
  1908. return PlyParser.decodeHeaderText(headerText);
  1909. }
  1910. static findVertexData(plyBuffer, header) {
  1911. return new DataView(plyBuffer, header.headerSizeBytes);
  1912. }
  1913. static readRawVertexFast(vertexData, offset, fieldOffsets, propertiesToRead, propertyTypes, outVertex) {
  1914. let rawVertex = outVertex || {};
  1915. for (let property of propertiesToRead) {
  1916. const propertyType = propertyTypes[property];
  1917. if (propertyType === 'float') {
  1918. rawVertex[property] = vertexData.getFloat32(offset + fieldOffsets[property], true);
  1919. }else if(propertyType === 'double'){//xzw add! 因为我试过一个ply的坐标是double
  1920. rawVertex[property] = vertexData.getFloat64(offset + fieldOffsets[property], true);
  1921. } else if (propertyType === 'uchar') {
  1922. rawVertex[property] = vertexData.getUint8(offset + fieldOffsets[property]) / 255.0;
  1923. }
  1924. }
  1925. }
  1926. static parseToUncompressedSplatBufferSection(header, fromSplat, toSplat, vertexData, vertexDataOffset,
  1927. toBuffer, toOffset, outSphericalHarmonicsDegree = 0) {
  1928. outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
  1929. const outBytesPerCenter = SplatBuffer.CompressionLevels[0].BytesPerCenter;
  1930. const outBytesPerScale = SplatBuffer.CompressionLevels[0].BytesPerScale;
  1931. const outBytesPerRotation = SplatBuffer.CompressionLevels[0].BytesPerRotation;
  1932. const outBytesPerColor = SplatBuffer.CompressionLevels[0].BytesPerColor;
  1933. const outBytesPerSplat = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[outSphericalHarmonicsDegree].BytesPerSplat;
  1934. for (let i = fromSplat; i <= toSplat; i++) {
  1935. const parsedSplat = PlyParser.parseToUncompressedSplat(vertexData, i, header, vertexDataOffset, outSphericalHarmonicsDegree);
  1936. const outBase = i * outBytesPerSplat + toOffset;
  1937. const outCenter = new Float32Array(toBuffer, outBase, 3);
  1938. const outScale = new Float32Array(toBuffer, outBase + outBytesPerCenter, 3);
  1939. const outRotation = new Float32Array(toBuffer, outBase + outBytesPerCenter + outBytesPerScale, 4);
  1940. const outColor = new Uint8Array(toBuffer, outBase + outBytesPerCenter + outBytesPerScale + outBytesPerRotation, 4);
  1941. outCenter[0] = parsedSplat[UncompressedSplatArray.OFFSET.X];
  1942. outCenter[1] = parsedSplat[UncompressedSplatArray.OFFSET.Y];
  1943. outCenter[2] = parsedSplat[UncompressedSplatArray.OFFSET.Z];
  1944. outScale[0] = parsedSplat[UncompressedSplatArray.OFFSET.SCALE0];
  1945. outScale[1] = parsedSplat[UncompressedSplatArray.OFFSET.SCALE1];
  1946. outScale[2] = parsedSplat[UncompressedSplatArray.OFFSET.SCALE2];
  1947. outRotation[0] = parsedSplat[UncompressedSplatArray.OFFSET.ROTATION0];
  1948. outRotation[1] = parsedSplat[UncompressedSplatArray.OFFSET.ROTATION1];
  1949. outRotation[2] = parsedSplat[UncompressedSplatArray.OFFSET.ROTATION2];
  1950. outRotation[3] = parsedSplat[UncompressedSplatArray.OFFSET.ROTATION3];
  1951. outColor[0] = parsedSplat[UncompressedSplatArray.OFFSET.FDC0];
  1952. outColor[1] = parsedSplat[UncompressedSplatArray.OFFSET.FDC1];
  1953. outColor[2] = parsedSplat[UncompressedSplatArray.OFFSET.FDC2];
  1954. outColor[3] = parsedSplat[UncompressedSplatArray.OFFSET.OPACITY];
  1955. if (outSphericalHarmonicsDegree >= 1) {
  1956. const outSphericalHarmonics = new Float32Array(toBuffer, outBase + outBytesPerCenter + outBytesPerScale +
  1957. outBytesPerRotation + outBytesPerColor,
  1958. parsedSplat.sphericalHarmonicsCount);
  1959. for (let i = 0; i <= 8; i++) {
  1960. outSphericalHarmonics[i] = parsedSplat[UncompressedSplatArray.OFFSET.FRC0 + i];
  1961. }
  1962. if (outSphericalHarmonicsDegree >= 2) {
  1963. for (let i = 9; i <= 23; i++) {
  1964. outSphericalHarmonics[i] = parsedSplat[UncompressedSplatArray.OFFSET.FRC0 + i];
  1965. }
  1966. }
  1967. }
  1968. }
  1969. }
  1970. static parseToUncompressedSplat = function() {
  1971. let rawVertex = {};
  1972. const tempRotation = new THREE.Quaternion();
  1973. return function(vertexData, row, header, vertexDataOffset = 0, outSphericalHarmonicsDegree = 0) {
  1974. outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
  1975. PlyParser.readRawVertexFast(vertexData, row * header.bytesPerSplat + vertexDataOffset, header.fieldOffsets,
  1976. PlyParser.Fields[outSphericalHarmonicsDegree > 0 ? 1 : 0], header.propertyTypes, rawVertex);
  1977. const newSplat = UncompressedSplatArray.createSplat(outSphericalHarmonicsDegree);
  1978. if (rawVertex['scale_0'] !== undefined) {
  1979. newSplat[UncompressedSplatArray.OFFSET.SCALE0] = Math.exp(rawVertex['scale_0']);
  1980. newSplat[UncompressedSplatArray.OFFSET.SCALE1] = Math.exp(rawVertex['scale_1']);
  1981. newSplat[UncompressedSplatArray.OFFSET.SCALE2] = Math.exp(rawVertex['scale_2']);
  1982. } else {
  1983. newSplat[UncompressedSplatArray.OFFSET.SCALE0] = 0.01;
  1984. newSplat[UncompressedSplatArray.OFFSET.SCALE1] = 0.01;
  1985. newSplat[UncompressedSplatArray.OFFSET.SCALE2] = 0.01;
  1986. }
  1987. if (rawVertex['f_dc_0'] !== undefined) {
  1988. const SH_C0 = 0.28209479177387814;
  1989. newSplat[UncompressedSplatArray.OFFSET.FDC0] = (0.5 + SH_C0 * rawVertex['f_dc_0']) * 255;
  1990. newSplat[UncompressedSplatArray.OFFSET.FDC1] = (0.5 + SH_C0 * rawVertex['f_dc_1']) * 255;
  1991. newSplat[UncompressedSplatArray.OFFSET.FDC2] = (0.5 + SH_C0 * rawVertex['f_dc_2']) * 255;
  1992. } else if (rawVertex['red'] !== undefined) {
  1993. newSplat[UncompressedSplatArray.OFFSET.FDC0] = rawVertex['red'] * 255;
  1994. newSplat[UncompressedSplatArray.OFFSET.FDC1] = rawVertex['green'] * 255;
  1995. newSplat[UncompressedSplatArray.OFFSET.FDC2] = rawVertex['blue'] * 255;
  1996. } else {
  1997. newSplat[UncompressedSplatArray.OFFSET.FDC0] = 0;
  1998. newSplat[UncompressedSplatArray.OFFSET.FDC1] = 0;
  1999. newSplat[UncompressedSplatArray.OFFSET.FDC2] = 0;
  2000. }
  2001. if (rawVertex['opacity'] !== undefined) {
  2002. newSplat[UncompressedSplatArray.OFFSET.OPACITY] = (1 / (1 + Math.exp(-rawVertex['opacity']))) * 255;
  2003. }else {//xzw add
  2004. newSplat[UncompressedSplatArray.OFFSET.OPACITY] = 1;
  2005. }
  2006. newSplat[UncompressedSplatArray.OFFSET.FDC0] = clamp(Math.floor(newSplat[UncompressedSplatArray.OFFSET.FDC0]), 0, 255);
  2007. newSplat[UncompressedSplatArray.OFFSET.FDC1] = clamp(Math.floor(newSplat[UncompressedSplatArray.OFFSET.FDC1]), 0, 255);
  2008. newSplat[UncompressedSplatArray.OFFSET.FDC2] = clamp(Math.floor(newSplat[UncompressedSplatArray.OFFSET.FDC2]), 0, 255);
  2009. newSplat[UncompressedSplatArray.OFFSET.OPACITY] = clamp(Math.floor(newSplat[UncompressedSplatArray.OFFSET.OPACITY]), 0, 255);
  2010. if (outSphericalHarmonicsDegree >= 1) {
  2011. if (rawVertex['f_rest_0'] !== undefined) {
  2012. for (let i = 0; i < 9; i++) {
  2013. newSplat[UncompressedSplatArray.OFFSET.FRC0 + i] = rawVertex[header.sphericalHarmonicsDegree1Fields[i]];
  2014. }
  2015. if (outSphericalHarmonicsDegree >= 2) {
  2016. for (let i = 0; i < 15; i++) {
  2017. newSplat[UncompressedSplatArray.OFFSET.FRC9 + i] = rawVertex[header.sphericalHarmonicsDegree2Fields[i]];
  2018. }
  2019. }
  2020. } else {
  2021. newSplat[UncompressedSplatArray.OFFSET.FRC0] = 0;
  2022. newSplat[UncompressedSplatArray.OFFSET.FRC1] = 0;
  2023. newSplat[UncompressedSplatArray.OFFSET.FRC2] = 0;
  2024. }
  2025. }
  2026. if(rawVertex['rot_0'] == void 0){//xzw add 表现为任何方向都是圆形 sprite
  2027. tempRotation.set(0,0,0,1);
  2028. }else {
  2029. tempRotation.set(rawVertex['rot_0'], rawVertex['rot_1'], rawVertex['rot_2'], rawVertex['rot_3']);
  2030. tempRotation.normalize();
  2031. }
  2032. newSplat[UncompressedSplatArray.OFFSET.ROTATION0] = tempRotation.x;
  2033. newSplat[UncompressedSplatArray.OFFSET.ROTATION1] = tempRotation.y;
  2034. newSplat[UncompressedSplatArray.OFFSET.ROTATION2] = tempRotation.z;
  2035. newSplat[UncompressedSplatArray.OFFSET.ROTATION3] = tempRotation.w;
  2036. newSplat[UncompressedSplatArray.OFFSET.X] = rawVertex['x'];
  2037. newSplat[UncompressedSplatArray.OFFSET.Y] = rawVertex['y'];
  2038. newSplat[UncompressedSplatArray.OFFSET.Z] = rawVertex['z'];
  2039. return newSplat;
  2040. };
  2041. }();
  2042. static parseToUncompressedSplatArray(plyBuffer, outSphericalHarmonicsDegree = 0) {
  2043. const header = PlyParser.decodeHeadeFromBuffer(plyBuffer);
  2044. if (header.compressed) {
  2045. return CompressedPlyParser.parseToUncompressedSplatArray(plyBuffer);
  2046. } else {
  2047. const splatCount = header.splatCount;
  2048. const vertexData = PlyParser.findVertexData(plyBuffer, header);
  2049. // TODO: Eventually properly support multiple degree spherical harmonics
  2050. // figure out the SH degree from the number of coefficients
  2051. /* let nRestCoeffs = 0;
  2052. for (const propertyName in header.propertyTypes) {
  2053. if (propertyName.startsWith('f_rest_')) {
  2054. nRestCoeffs += 1;
  2055. }
  2056. }
  2057. const nCoeffsPerColor = nRestCoeffs / 3;*/
  2058. // const sphericalHarmonicsDegree = Math.sqrt(nCoeffsPerColor + 1) - 1;
  2059. // const sphericalHarmonicsDegree = 0;
  2060. // console.log('Detected degree', sphericalHarmonicsDegree, 'with ', nCoeffsPerColor, 'coefficients per color');
  2061. // figure out the order in which spherical harmonics should be read
  2062. /* const shFeatureOrder = [];
  2063. for (let rgb = 0; rgb < 3; ++rgb) {
  2064. shFeatureOrder.push(`f_dc_${rgb}`);
  2065. }
  2066. for (let i = 0; i < nCoeffsPerColor; ++i) {
  2067. for (let rgb = 0; rgb < 3; ++rgb) {
  2068. shFeatureOrder.push(`f_rest_${rgb * nCoeffsPerColor + i}`);
  2069. }
  2070. }*/
  2071. const splatArray = new UncompressedSplatArray(outSphericalHarmonicsDegree);
  2072. for (let row = 0; row < splatCount; row++) {
  2073. const newSplat = PlyParser.parseToUncompressedSplat(vertexData, row, header, 0, outSphericalHarmonicsDegree);
  2074. splatArray.addSplat(newSplat);
  2075. }
  2076. return splatArray;
  2077. }
  2078. }
  2079. }
  2080. class SplatPartitioner {
  2081. constructor(sectionCount, sectionFilters, groupingParameters, partitionGenerator) {
  2082. this.sectionCount = sectionCount;
  2083. this.sectionFilters = sectionFilters;
  2084. this.groupingParameters = groupingParameters;
  2085. this.partitionGenerator = partitionGenerator;
  2086. }
  2087. partitionUncompressedSplatArray(splatArray) {
  2088. let groupingParameters;
  2089. let sectionCount;
  2090. let sectionFilters;
  2091. if (this.partitionGenerator) {
  2092. const results = this.partitionGenerator(splatArray);
  2093. groupingParameters = results.groupingParameters;
  2094. sectionCount = results.sectionCount;
  2095. sectionFilters = results.sectionFilters;
  2096. } else {
  2097. groupingParameters = this.groupingParameters;
  2098. sectionCount = this.sectionCount;
  2099. sectionFilters = this.sectionFilters;
  2100. }
  2101. const newArrays = [];
  2102. for (let s = 0; s < sectionCount; s++) {
  2103. const sectionSplats = new UncompressedSplatArray(splatArray.sphericalHarmonicsDegree);
  2104. const sectionFilter = sectionFilters[s];
  2105. for (let i = 0; i < splatArray.splatCount; i++) {
  2106. if (sectionFilter(i)) {
  2107. sectionSplats.addSplatFromArray(splatArray, i);
  2108. }
  2109. }
  2110. newArrays.push(sectionSplats);
  2111. }
  2112. return {
  2113. splatArrays: newArrays,
  2114. parameters: groupingParameters
  2115. };
  2116. }
  2117. static getStandardPartitioner(partitionSize = 0, sceneCenter = new THREE.Vector3(),
  2118. blockSize = SplatBuffer.BucketBlockSize, bucketSize = SplatBuffer.BucketSize) {
  2119. const partitionGenerator = (splatArray) => {
  2120. if (partitionSize <= 0) partitionSize = splatArray.splatCount;
  2121. const centerA = new THREE.Vector3();
  2122. const centerB = new THREE.Vector3();
  2123. const clampDistance = 0.5;
  2124. const clampPoint = (point) => {
  2125. point.x = Math.floor(point.x / clampDistance) * clampDistance;
  2126. point.y = Math.floor(point.y / clampDistance) * clampDistance;
  2127. point.z = Math.floor(point.z / clampDistance) * clampDistance;
  2128. };
  2129. splatArray.splats.sort((a, b) => {
  2130. centerA.set(a[UncompressedSplatArray.OFFSET.X],
  2131. a[UncompressedSplatArray.OFFSET.Y],
  2132. a[UncompressedSplatArray.OFFSET.Z]).sub(sceneCenter);
  2133. clampPoint(centerA);
  2134. const centerADist = centerA.lengthSq();
  2135. centerB.set(b[UncompressedSplatArray.OFFSET.X],
  2136. b[UncompressedSplatArray.OFFSET.Y],
  2137. b[UncompressedSplatArray.OFFSET.Z]).sub(sceneCenter);
  2138. clampPoint(centerB);
  2139. const centerBDist = centerB.lengthSq();
  2140. if (centerADist > centerBDist) return 1;
  2141. else return -1;
  2142. });
  2143. const sectionFilters = [];
  2144. const groupingParameters = [];
  2145. partitionSize = Math.min(splatArray.splatCount, partitionSize);
  2146. const patitionCount = Math.ceil(splatArray.splatCount / partitionSize);
  2147. let currentStartSplat = 0;
  2148. for (let i = 0; i < patitionCount; i ++) {
  2149. let startSplat = currentStartSplat;
  2150. sectionFilters.push((splatIndex) => {
  2151. return splatIndex >= startSplat && splatIndex < startSplat + partitionSize;
  2152. });
  2153. groupingParameters.push({
  2154. 'blocksSize': blockSize,
  2155. 'bucketSize': bucketSize,
  2156. });
  2157. currentStartSplat += partitionSize;
  2158. }
  2159. return {
  2160. 'sectionCount': sectionFilters.length,
  2161. sectionFilters,
  2162. groupingParameters
  2163. };
  2164. };
  2165. return new SplatPartitioner(undefined, undefined, undefined, partitionGenerator);
  2166. }
  2167. }
  2168. class SplatBufferGenerator {
  2169. constructor(splatPartitioner, alphaRemovalThreshold, compressionLevel, sectionSize, sceneCenter, blockSize, bucketSize) {
  2170. this.splatPartitioner = splatPartitioner;
  2171. this.alphaRemovalThreshold = alphaRemovalThreshold;
  2172. this.compressionLevel = compressionLevel;
  2173. this.sectionSize = sectionSize;
  2174. this.sceneCenter = sceneCenter ? new THREE.Vector3().copy(sceneCenter) : undefined;
  2175. this.blockSize = blockSize;
  2176. this.bucketSize = bucketSize;
  2177. }
  2178. generateFromUncompressedSplatArray(splatArray) {
  2179. const partitionResults = this.splatPartitioner.partitionUncompressedSplatArray(splatArray);
  2180. return SplatBuffer.generateFromUncompressedSplatArrays(partitionResults.splatArrays,
  2181. this.alphaRemovalThreshold, this.compressionLevel,
  2182. this.sceneCenter, this.blockSize, this.bucketSize,
  2183. partitionResults.parameters);
  2184. }
  2185. static getStandardGenerator(alphaRemovalThreshold = 1, compressionLevel = 1, sectionSize = 0, sceneCenter = new THREE.Vector3(),
  2186. blockSize = SplatBuffer.BucketBlockSize, bucketSize = SplatBuffer.BucketSize) {
  2187. const splatPartitioner = SplatPartitioner.getStandardPartitioner(sectionSize, sceneCenter, blockSize, bucketSize);
  2188. return new SplatBufferGenerator(splatPartitioner, alphaRemovalThreshold, compressionLevel,
  2189. sectionSize, sceneCenter, blockSize, bucketSize);
  2190. }
  2191. }
  2192. const LoaderStatus = {
  2193. 'Downloading': 0,
  2194. 'Processing': 1,
  2195. 'Done': 2
  2196. };
  2197. class Constants {
  2198. static DepthMapRange = 1 << 16;
  2199. static MemoryPageSize = 65536;
  2200. static BytesPerFloat = 4;
  2201. static BytesPerInt = 4;
  2202. static MaxScenes = 32;
  2203. static StreamingSectionSize = 524288;
  2204. }
  2205. function storeChunksInBuffer(chunks, buffer) {
  2206. let inBytes = 0;
  2207. for (let chunk of chunks) inBytes += chunk.sizeBytes;
  2208. if (!buffer || buffer.byteLength < inBytes) {
  2209. buffer = new ArrayBuffer(inBytes);
  2210. }
  2211. let offset = 0;
  2212. for (let chunk of chunks) {
  2213. new Uint8Array(buffer, offset, chunk.sizeBytes).set(chunk.data);
  2214. offset += chunk.sizeBytes;
  2215. }
  2216. return buffer;
  2217. }
  2218. class PlyLoader {
  2219. static loadFromURL(fileName, onProgress, streamLoadData, onStreamedSectionProgress, minimumAlpha, compressionLevel,
  2220. outSphericalHarmonicsDegree = 0, sectionSize, sceneCenter, blockSize, bucketSize) {
  2221. const streamedSectionSizeBytes = Constants.StreamingSectionSize;
  2222. const splatDataOffsetBytes = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes;
  2223. const sectionCount = 1;
  2224. let streamBufferIn;
  2225. let streamBufferOut;
  2226. let streamedSplatBuffer;
  2227. let compressedPlyHeaderChunksBuffer;
  2228. let maxSplatCount = 0;
  2229. let splatCount = 0;
  2230. let headerLoaded = false;
  2231. let readyToLoadSplatData = false;
  2232. let compressed = false;
  2233. let streamLoadCompleteResolver;
  2234. let streamLoadPromise = new Promise((resolve) => {
  2235. streamLoadCompleteResolver = resolve;
  2236. });
  2237. let numBytesStreamed = 0;
  2238. let numBytesParsed = 0;
  2239. let numBytesDownloaded = 0;
  2240. let headerText = '';
  2241. let header = null;
  2242. let chunks = [];
  2243. const textDecoder = new TextDecoder();
  2244. const localOnProgress = (percent, percentLabel, chunkData) => {
  2245. const loadComplete = percent >= 100;
  2246. if (streamLoadData) {
  2247. if (chunkData) {
  2248. chunks.push({
  2249. 'data': chunkData,
  2250. 'sizeBytes': chunkData.byteLength,
  2251. 'startBytes': numBytesDownloaded,
  2252. 'endBytes': numBytesDownloaded + chunkData.byteLength
  2253. });
  2254. numBytesDownloaded += chunkData.byteLength;
  2255. }
  2256. if (!headerLoaded) {
  2257. headerText += textDecoder.decode(chunkData);
  2258. if (PlyParser.checkTextForEndHeader(headerText)) {
  2259. header = PlyParser.decodeHeaderText(headerText);
  2260. outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
  2261. compressed = header.compressed;
  2262. if (compressed) {
  2263. header = CompressedPlyParser.decodeHeaderText(headerText);
  2264. maxSplatCount = header.vertexElement.count;
  2265. } else {
  2266. maxSplatCount = header.splatCount;
  2267. readyToLoadSplatData = true;
  2268. }
  2269. const shDescriptor = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[outSphericalHarmonicsDegree];
  2270. const splatBufferSizeBytes = splatDataOffsetBytes + shDescriptor.BytesPerSplat * maxSplatCount;
  2271. streamBufferOut = new ArrayBuffer(splatBufferSizeBytes);
  2272. SplatBuffer.writeHeaderToBuffer({
  2273. versionMajor: SplatBuffer.CurrentMajorVersion,
  2274. versionMinor: SplatBuffer.CurrentMinorVersion,
  2275. maxSectionCount: sectionCount,
  2276. sectionCount: sectionCount,
  2277. maxSplatCount: maxSplatCount,
  2278. splatCount: splatCount,
  2279. compressionLevel: 0,
  2280. sceneCenter: new THREE.Vector3()
  2281. }, streamBufferOut);
  2282. numBytesStreamed = header.headerSizeBytes;
  2283. numBytesParsed = header.headerSizeBytes;
  2284. headerLoaded = true;
  2285. }
  2286. } else if (compressed && !readyToLoadSplatData) {
  2287. const sizeRequiredForHeaderAndChunks = header.headerSizeBytes + header.chunkElement.storageSizeBytes;
  2288. compressedPlyHeaderChunksBuffer = storeChunksInBuffer(chunks, compressedPlyHeaderChunksBuffer);
  2289. if (compressedPlyHeaderChunksBuffer.byteLength >= sizeRequiredForHeaderAndChunks) {
  2290. CompressedPlyParser.readElementData(header.chunkElement, compressedPlyHeaderChunksBuffer, header.headerSizeBytes);
  2291. numBytesStreamed = sizeRequiredForHeaderAndChunks;
  2292. numBytesParsed = sizeRequiredForHeaderAndChunks;
  2293. readyToLoadSplatData = true;
  2294. }
  2295. }
  2296. if (headerLoaded && readyToLoadSplatData) {
  2297. if (chunks.length > 0) {
  2298. streamBufferIn = storeChunksInBuffer(chunks, streamBufferIn);
  2299. const bytesLoadedSinceLastStreamedSection = numBytesDownloaded - numBytesStreamed;
  2300. if (bytesLoadedSinceLastStreamedSection > streamedSectionSizeBytes || loadComplete) {
  2301. const numBytesToProcess = numBytesDownloaded - numBytesParsed;
  2302. const addedSplatCount = Math.floor(numBytesToProcess / header.bytesPerSplat);
  2303. const numBytesToParse = addedSplatCount * header.bytesPerSplat;
  2304. const numBytesLeftOver = numBytesToProcess - numBytesToParse;
  2305. const newSplatCount = splatCount + addedSplatCount;
  2306. const parsedDataViewOffset = numBytesParsed - chunks[0].startBytes;
  2307. const dataToParse = new DataView(streamBufferIn, parsedDataViewOffset, numBytesToParse);
  2308. const shDescriptor = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[outSphericalHarmonicsDegree];
  2309. const outOffset = splatCount * shDescriptor.BytesPerSplat + splatDataOffsetBytes;
  2310. if (compressed) {
  2311. CompressedPlyParser.parseToUncompressedSplatBufferSection(header.chunkElement, header.vertexElement, 0,
  2312. addedSplatCount - 1, splatCount,
  2313. dataToParse, 0, streamBufferOut, outOffset);
  2314. } else {
  2315. PlyParser.parseToUncompressedSplatBufferSection(header, 0, addedSplatCount - 1, dataToParse, 0,
  2316. streamBufferOut, outOffset, outSphericalHarmonicsDegree);
  2317. }
  2318. splatCount = newSplatCount;
  2319. if (!streamedSplatBuffer) {
  2320. SplatBuffer.writeSectionHeaderToBuffer({
  2321. maxSplatCount: maxSplatCount,
  2322. splatCount: splatCount,
  2323. bucketSize: 0,
  2324. bucketCount: 0,
  2325. bucketBlockSize: 0,
  2326. compressionScaleRange: 0,
  2327. storageSizeBytes: 0,
  2328. fullBucketCount: 0,
  2329. partiallyFilledBucketCount: 0,
  2330. sphericalHarmonicsDegree: outSphericalHarmonicsDegree
  2331. }, 0, streamBufferOut, SplatBuffer.HeaderSizeBytes);
  2332. streamedSplatBuffer = new SplatBuffer(streamBufferOut, false);
  2333. }
  2334. streamedSplatBuffer.updateLoadedCounts(1, splatCount);
  2335. onStreamedSectionProgress(streamedSplatBuffer, loadComplete);
  2336. numBytesStreamed += streamedSectionSizeBytes;
  2337. numBytesParsed += numBytesToParse;
  2338. if (numBytesLeftOver === 0) {
  2339. chunks = [];
  2340. } else {
  2341. let keepChunks = [];
  2342. let keepSize = 0;
  2343. for (let i = chunks.length - 1; i >= 0; i--) {
  2344. const chunk = chunks[i];
  2345. keepSize += chunk.sizeBytes;
  2346. keepChunks.unshift(chunk);
  2347. if (keepSize >= numBytesLeftOver) break;
  2348. }
  2349. chunks = keepChunks;
  2350. }
  2351. }
  2352. }
  2353. if (loadComplete) {
  2354. streamLoadCompleteResolver(streamedSplatBuffer);
  2355. }
  2356. }
  2357. }
  2358. if (onProgress) onProgress(percent, percentLabel, LoaderStatus.Downloading);
  2359. };
  2360. return fetchWithProgress(fileName, localOnProgress, !streamLoadData).then((plyFileData) => {
  2361. if (onProgress) onProgress(0, '0%', LoaderStatus.Processing);
  2362. const loadPromise = streamLoadData ? streamLoadPromise :
  2363. PlyLoader.loadFromFileData(plyFileData, minimumAlpha, compressionLevel, outSphericalHarmonicsDegree,
  2364. sectionSize, sceneCenter, blockSize, bucketSize);
  2365. return loadPromise.then((splatBuffer) => {
  2366. if (onProgress) onProgress(100, '100%', LoaderStatus.Done);
  2367. return splatBuffer;
  2368. });
  2369. });
  2370. }
  2371. static loadFromFileData(plyFileData, minimumAlpha, compressionLevel, outSphericalHarmonicsDegree = 0,
  2372. sectionSize, sceneCenter, blockSize, bucketSize) {
  2373. return delayedExecute(() => {
  2374. return PlyParser.parseToUncompressedSplatArray(plyFileData, outSphericalHarmonicsDegree);
  2375. })
  2376. .then((splatArray) => {
  2377. const splatBufferGenerator = SplatBufferGenerator.getStandardGenerator(minimumAlpha, compressionLevel, sectionSize,
  2378. sceneCenter, blockSize, bucketSize);
  2379. return splatBufferGenerator.generateFromUncompressedSplatArray(splatArray);
  2380. });
  2381. }
  2382. }
  2383. class SplatParser {
  2384. static RowSizeBytes = 32;
  2385. static CenterSizeBytes = 12;
  2386. static ScaleSizeBytes = 12;
  2387. static RotationSizeBytes = 4;
  2388. static ColorSizeBytes = 4;
  2389. static parseToUncompressedSplatBufferSection(fromSplat, toSplat, fromBuffer, fromOffset, toBuffer, toOffset) {
  2390. const outBytesPerCenter = SplatBuffer.CompressionLevels[0].BytesPerCenter;
  2391. const outBytesPerScale = SplatBuffer.CompressionLevels[0].BytesPerScale;
  2392. const outBytesPerRotation = SplatBuffer.CompressionLevels[0].BytesPerRotation;
  2393. const outBytesPerSplat = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[0].BytesPerSplat;
  2394. for (let i = fromSplat; i <= toSplat; i++) {
  2395. const inBase = i * SplatParser.RowSizeBytes + fromOffset;
  2396. const inCenter = new Float32Array(fromBuffer, inBase, 3);
  2397. const inScale = new Float32Array(fromBuffer, inBase + SplatParser.CenterSizeBytes, 3);
  2398. const inColor = new Uint8Array(fromBuffer, inBase + SplatParser.CenterSizeBytes + SplatParser.ScaleSizeBytes, 4);
  2399. const inRotation = new Uint8Array(fromBuffer, inBase + SplatParser.CenterSizeBytes + SplatParser.ScaleSizeBytes +
  2400. SplatParser.RotationSizeBytes, 4);
  2401. const quat = new THREE.Quaternion((inRotation[1] - 128) / 128, (inRotation[2] - 128) / 128,
  2402. (inRotation[3] - 128) / 128, (inRotation[0] - 128) / 128);
  2403. quat.normalize();
  2404. const outBase = i * outBytesPerSplat + toOffset;
  2405. const outCenter = new Float32Array(toBuffer, outBase, 3);
  2406. const outScale = new Float32Array(toBuffer, outBase + outBytesPerCenter, 3);
  2407. const outRotation = new Float32Array(toBuffer, outBase + outBytesPerCenter + outBytesPerScale, 4);
  2408. const outColor = new Uint8Array(toBuffer, outBase + outBytesPerCenter + outBytesPerScale + outBytesPerRotation, 4);
  2409. outCenter[0] = inCenter[0];
  2410. outCenter[1] = inCenter[1];
  2411. outCenter[2] = inCenter[2];
  2412. outScale[0] = inScale[0];
  2413. outScale[1] = inScale[1];
  2414. outScale[2] = inScale[2];
  2415. outRotation[0] = quat.w;
  2416. outRotation[1] = quat.x;
  2417. outRotation[2] = quat.y;
  2418. outRotation[3] = quat.z;
  2419. outColor[0] = inColor[0];
  2420. outColor[1] = inColor[1];
  2421. outColor[2] = inColor[2];
  2422. outColor[3] = inColor[3];
  2423. }
  2424. }
  2425. static parseStandardSplatToUncompressedSplatArray(inBuffer) {
  2426. // Standard .splat row layout:
  2427. // XYZ - Position (Float32)
  2428. // XYZ - Scale (Float32)
  2429. // RGBA - colors (uint8)
  2430. // IJKL - quaternion/rot (uint8)
  2431. const splatCount = inBuffer.byteLength / SplatParser.RowSizeBytes;
  2432. const splatArray = new UncompressedSplatArray();
  2433. for (let i = 0; i < splatCount; i++) {
  2434. const inBase = i * SplatParser.RowSizeBytes;
  2435. const inCenter = new Float32Array(inBuffer, inBase, 3);
  2436. const inScale = new Float32Array(inBuffer, inBase + SplatParser.CenterSizeBytes, 3);
  2437. const inColor = new Uint8Array(inBuffer, inBase + SplatParser.CenterSizeBytes + SplatParser.ScaleSizeBytes, 4);
  2438. const inRotation = new Uint8Array(inBuffer, inBase + SplatParser.CenterSizeBytes +
  2439. SplatParser.ScaleSizeBytes + SplatParser.ColorSizeBytes, 4);
  2440. const quat = new THREE.Quaternion((inRotation[1] - 128) / 128, (inRotation[2] - 128) / 128,
  2441. (inRotation[3] - 128) / 128, (inRotation[0] - 128) / 128);
  2442. quat.normalize();
  2443. splatArray.addSplatFromComonents(inCenter[0], inCenter[1], inCenter[2], inScale[0], inScale[1], inScale[2],
  2444. quat.w, quat.x, quat.y, quat.z, inColor[0], inColor[1], inColor[2], inColor[3]);
  2445. }
  2446. return splatArray;
  2447. }
  2448. }
  2449. class SplatLoader {
  2450. static loadFromURL(fileName, onProgress, streamLoadData, onStreamedSectionProgress, minimumAlpha, compressionLevel,
  2451. optimizeSplatData, sectionSize, sceneCenter, blockSize, bucketSize) {
  2452. const splatDataOffsetBytes = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes;
  2453. const streamSectionSizeBytes = Constants.StreamingSectionSize;
  2454. const sectionCount = 1;
  2455. let streamBufferIn;
  2456. let streamBufferOut;
  2457. let streamSplatBuffer;
  2458. let maxSplatCount = 0;
  2459. let splatCount = 0;
  2460. let streamLoadCompleteResolver;
  2461. let streamLoadPromise = new Promise((resolve) => {
  2462. streamLoadCompleteResolver = resolve;
  2463. });
  2464. let numBytesStreamed = 0;
  2465. let numBytesLoaded = 0;
  2466. let chunks = [];
  2467. const localOnProgress = (percent, percentStr, chunk, fileSize) => {
  2468. const loadComplete = percent >= 100;
  2469. if (!fileSize) streamLoadData = false;
  2470. if (streamLoadData) {
  2471. if (!streamBufferIn) {
  2472. maxSplatCount = fileSize / SplatParser.RowSizeBytes;
  2473. streamBufferIn = new ArrayBuffer(fileSize);
  2474. const bytesPerSplat = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[0].BytesPerSplat;
  2475. const splatBufferSizeBytes = splatDataOffsetBytes + bytesPerSplat * maxSplatCount;
  2476. streamBufferOut = new ArrayBuffer(splatBufferSizeBytes);
  2477. SplatBuffer.writeHeaderToBuffer({
  2478. versionMajor: SplatBuffer.CurrentMajorVersion,
  2479. versionMinor: SplatBuffer.CurrentMinorVersion,
  2480. maxSectionCount: sectionCount,
  2481. sectionCount: sectionCount,
  2482. maxSplatCount: maxSplatCount,
  2483. splatCount: splatCount,
  2484. compressionLevel: 0,
  2485. sceneCenter: new THREE.Vector3()
  2486. }, streamBufferOut);
  2487. }
  2488. if (chunk) {
  2489. chunks.push(chunk);
  2490. new Uint8Array(streamBufferIn, numBytesLoaded, chunk.byteLength).set(new Uint8Array(chunk));
  2491. numBytesLoaded += chunk.byteLength;
  2492. const bytesLoadedSinceLastSection = numBytesLoaded - numBytesStreamed;
  2493. if (bytesLoadedSinceLastSection > streamSectionSizeBytes || loadComplete) {
  2494. const bytesToUpdate = loadComplete ? bytesLoadedSinceLastSection : streamSectionSizeBytes;
  2495. const addedSplatCount = bytesToUpdate / SplatParser.RowSizeBytes;
  2496. const newSplatCount = splatCount + addedSplatCount;
  2497. SplatParser.parseToUncompressedSplatBufferSection(splatCount, newSplatCount - 1, streamBufferIn, 0,
  2498. streamBufferOut, splatDataOffsetBytes);
  2499. splatCount = newSplatCount;
  2500. if (!streamSplatBuffer) {
  2501. SplatBuffer.writeSectionHeaderToBuffer({
  2502. maxSplatCount: maxSplatCount,
  2503. splatCount: splatCount,
  2504. bucketSize: 0,
  2505. bucketCount: 0,
  2506. bucketBlockSize: 0,
  2507. compressionScaleRange: 0,
  2508. storageSizeBytes: 0,
  2509. fullBucketCount: 0,
  2510. partiallyFilledBucketCount: 0
  2511. }, 0, streamBufferOut, SplatBuffer.HeaderSizeBytes);
  2512. streamSplatBuffer = new SplatBuffer(streamBufferOut, false);
  2513. }
  2514. streamSplatBuffer.updateLoadedCounts(1, splatCount);
  2515. onStreamedSectionProgress(streamSplatBuffer, loadComplete);
  2516. numBytesStreamed += streamSectionSizeBytes;
  2517. }
  2518. }
  2519. if (loadComplete) {
  2520. streamLoadCompleteResolver(streamSplatBuffer);
  2521. }
  2522. }
  2523. if (onProgress) onProgress(percent, percentStr, LoaderStatus.Downloading);
  2524. return streamLoadData;
  2525. };
  2526. return fetchWithProgress(fileName, localOnProgress, true).then((fullBuffer) => {
  2527. if (onProgress) onProgress(0, '0%', LoaderStatus.Processing);
  2528. const loadPromise = streamLoadData ? streamLoadPromise :
  2529. SplatLoader.loadFromFileData(fullBuffer, minimumAlpha, compressionLevel, optimizeSplatData,
  2530. sectionSize, sceneCenter, blockSize, bucketSize);
  2531. return loadPromise.then((splatBuffer) => {
  2532. if (onProgress) onProgress(100, '100%', LoaderStatus.Done);
  2533. return splatBuffer;
  2534. });
  2535. });
  2536. }
  2537. static loadFromFileData(splatFileData, minimumAlpha, compressionLevel, optimizeSplatData,
  2538. sectionSize, sceneCenter, blockSize, bucketSize) {
  2539. return delayedExecute(() => {
  2540. const splatArray = SplatParser.parseStandardSplatToUncompressedSplatArray(splatFileData);
  2541. if (optimizeSplatData) {
  2542. const splatBufferGenerator = SplatBufferGenerator.getStandardGenerator(minimumAlpha, compressionLevel,
  2543. sectionSize, sceneCenter, blockSize,
  2544. bucketSize);
  2545. return splatBufferGenerator.generateFromUncompressedSplatArray(splatArray);
  2546. } else {
  2547. return SplatBuffer.generateFromUncompressedSplatArrays([splatArray], minimumAlpha, 0, new THREE.Vector3());
  2548. }
  2549. });
  2550. }
  2551. }
  2552. class KSplatLoader {
  2553. static checkVersion(buffer) {
  2554. const minVersionMajor = SplatBuffer.CurrentMajorVersion;
  2555. const minVersionMinor = SplatBuffer.CurrentMinorVersion;
  2556. const header = SplatBuffer.parseHeader(buffer);
  2557. if (header.versionMajor === minVersionMajor &&
  2558. header.versionMinor >= minVersionMinor ||
  2559. header.versionMajor > minVersionMajor) {
  2560. return true;
  2561. } else {
  2562. throw new Error(`KSplat version not supported: v${header.versionMajor}.${header.versionMinor}. ` +
  2563. `Minimum required: v${minVersionMajor}.${minVersionMinor}`);
  2564. }
  2565. };
  2566. static loadFromURL(fileName, onProgress, streamLoadData, onSectionBuilt) {
  2567. let streamBuffer;
  2568. let streamSplatBuffer;
  2569. let headerBuffer;
  2570. let header;
  2571. let headerLoaded = false;
  2572. let headerLoading = false;
  2573. let sectionHeadersBuffer;
  2574. let sectionHeaders = [];
  2575. let sectionHeadersLoaded = false;
  2576. let sectionHeadersLoading = false;
  2577. let numBytesLoaded = 0;
  2578. let numBytesStreamed = 0;
  2579. let streamSectionSizeBytes = Constants.StreamingSectionSize;
  2580. let totalBytesToDownload = 0;
  2581. let loadComplete = false;
  2582. let chunks = [];
  2583. let streamLoadCompleteResolver;
  2584. let streamLoadPromise = new Promise((resolve) => {
  2585. streamLoadCompleteResolver = resolve;
  2586. });
  2587. const checkAndLoadHeader = () => {
  2588. if (!headerLoaded && !headerLoading && numBytesLoaded >= SplatBuffer.HeaderSizeBytes) {
  2589. headerLoading = true;
  2590. const headerAssemblyPromise = new Blob(chunks).arrayBuffer();
  2591. headerAssemblyPromise.then((bufferData) => {
  2592. headerBuffer = new ArrayBuffer(SplatBuffer.HeaderSizeBytes);
  2593. new Uint8Array(headerBuffer).set(new Uint8Array(bufferData, 0, SplatBuffer.HeaderSizeBytes));
  2594. KSplatLoader.checkVersion(headerBuffer);
  2595. headerLoading = false;
  2596. headerLoaded = true;
  2597. header = SplatBuffer.parseHeader(headerBuffer);
  2598. window.setTimeout(() => {
  2599. checkAndLoadSectionHeaders();
  2600. }, 1);
  2601. });
  2602. }
  2603. };
  2604. let queuedCheckAndLoadSectionsCount = 0;
  2605. const queueCheckAndLoadSections = () => {
  2606. if (queuedCheckAndLoadSectionsCount === 0) {
  2607. queuedCheckAndLoadSectionsCount++;
  2608. window.setTimeout(() => {
  2609. queuedCheckAndLoadSectionsCount--;
  2610. checkAndLoadSections(true);
  2611. }, 1);
  2612. }
  2613. };
  2614. const checkAndLoadSectionHeaders = () => {
  2615. const performLoad = () => {
  2616. sectionHeadersLoading = true;
  2617. const sectionHeadersAssemblyPromise = new Blob(chunks).arrayBuffer();
  2618. sectionHeadersAssemblyPromise.then((bufferData) => {
  2619. sectionHeadersLoading = false;
  2620. sectionHeadersLoaded = true;
  2621. sectionHeadersBuffer = new ArrayBuffer(header.maxSectionCount * SplatBuffer.SectionHeaderSizeBytes);
  2622. new Uint8Array(sectionHeadersBuffer).set(new Uint8Array(bufferData, SplatBuffer.HeaderSizeBytes,
  2623. header.maxSectionCount * SplatBuffer.SectionHeaderSizeBytes));
  2624. sectionHeaders = SplatBuffer.parseSectionHeaders(header, sectionHeadersBuffer, 0, false);
  2625. let totalSectionStorageStorageByes = 0;
  2626. for (let i = 0; i < header.maxSectionCount; i++) {
  2627. totalSectionStorageStorageByes += sectionHeaders[i].storageSizeBytes;
  2628. }
  2629. const totalStorageSizeBytes = SplatBuffer.HeaderSizeBytes + header.maxSectionCount *
  2630. SplatBuffer.SectionHeaderSizeBytes + totalSectionStorageStorageByes;
  2631. if (!streamBuffer) {
  2632. streamBuffer = new ArrayBuffer(totalStorageSizeBytes);
  2633. let offset = 0;
  2634. for (let i = 0; i < chunks.length; i++) {
  2635. const chunk = chunks[i];
  2636. new Uint8Array(streamBuffer, offset, chunk.byteLength).set(new Uint8Array(chunk));
  2637. offset += chunk.byteLength;
  2638. }
  2639. }
  2640. totalBytesToDownload = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes * header.maxSectionCount;
  2641. for (let i = 0; i <= sectionHeaders.length && i < header.maxSectionCount; i++) {
  2642. totalBytesToDownload += sectionHeaders[i].storageSizeBytes;
  2643. }
  2644. queueCheckAndLoadSections();
  2645. });
  2646. };
  2647. if (!sectionHeadersLoading && !sectionHeadersLoaded && headerLoaded &&
  2648. numBytesLoaded >= SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes * header.maxSectionCount) {
  2649. performLoad();
  2650. }
  2651. };
  2652. const checkAndLoadSections = () => {
  2653. if (sectionHeadersLoaded) {
  2654. if (loadComplete) return;
  2655. loadComplete = numBytesLoaded >= totalBytesToDownload;
  2656. const bytesLoadedSinceLastSection = numBytesLoaded - numBytesStreamed;
  2657. if (bytesLoadedSinceLastSection > streamSectionSizeBytes || loadComplete) {
  2658. numBytesStreamed = numBytesLoaded;
  2659. if (!streamSplatBuffer) streamSplatBuffer = new SplatBuffer(streamBuffer, false);
  2660. const baseDataOffset = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes * header.maxSectionCount;
  2661. let sectionBase = 0;
  2662. let reachedSections = 0;
  2663. let loadedSplatCount = 0;
  2664. for (let i = 0; i < header.maxSectionCount; i++) {
  2665. const sectionHeader = sectionHeaders[i];
  2666. const bucketsDataOffset = sectionBase + sectionHeader.partiallyFilledBucketCount * 4 +
  2667. sectionHeader.bucketStorageSizeBytes * sectionHeader.bucketCount;
  2668. const bytesRequiredToReachSectionSplatData = baseDataOffset + bucketsDataOffset;
  2669. if (numBytesLoaded >= bytesRequiredToReachSectionSplatData) {
  2670. reachedSections++;
  2671. const bytesPastSSectionSplatDataStart = numBytesLoaded - bytesRequiredToReachSectionSplatData;
  2672. const baseDescriptor = SplatBuffer.CompressionLevels[header.compressionLevel];
  2673. const shDesc = baseDescriptor.SphericalHarmonicsDegrees[sectionHeader.sphericalHarmonicsDegree];
  2674. const bytesPerSplat = shDesc.BytesPerSplat;
  2675. let loadedSplatsForSection = Math.floor(bytesPastSSectionSplatDataStart / bytesPerSplat);
  2676. loadedSplatsForSection = Math.min(loadedSplatsForSection, sectionHeader.maxSplatCount);
  2677. loadedSplatCount += loadedSplatsForSection;
  2678. streamSplatBuffer.updateLoadedCounts(reachedSections, loadedSplatCount);
  2679. streamSplatBuffer.updateSectionLoadedCounts(i, loadedSplatsForSection);
  2680. } else {
  2681. break;
  2682. }
  2683. sectionBase += sectionHeader.storageSizeBytes;
  2684. }
  2685. onSectionBuilt(streamSplatBuffer, loadComplete);
  2686. if (loadComplete) {
  2687. streamLoadCompleteResolver(streamSplatBuffer);
  2688. }
  2689. }
  2690. }
  2691. };
  2692. const localOnProgress = (percent, percentStr, chunk) => {
  2693. if (chunk) {
  2694. chunks.push(chunk);
  2695. if (streamBuffer) {
  2696. new Uint8Array(streamBuffer, numBytesLoaded, chunk.byteLength).set(new Uint8Array(chunk));
  2697. }
  2698. numBytesLoaded += chunk.byteLength;
  2699. }
  2700. if (streamLoadData) {
  2701. checkAndLoadHeader();
  2702. checkAndLoadSectionHeaders();
  2703. checkAndLoadSections();
  2704. }
  2705. if (onProgress) onProgress(percent, percentStr, LoaderStatus.Downloading);
  2706. };
  2707. return fetchWithProgress(fileName, localOnProgress, !streamLoadData).then((fullBuffer) => {
  2708. if (onProgress) onProgress(0, '0%', LoaderStatus.Processing);
  2709. const loadPromise = streamLoadData ? streamLoadPromise : KSplatLoader.loadFromFileData(fullBuffer);
  2710. return loadPromise.then((splatBuffer) => {
  2711. if (onProgress) onProgress(100, '100%', LoaderStatus.Done);
  2712. return splatBuffer;
  2713. });
  2714. });
  2715. }
  2716. static loadFromFileData(fileData) {
  2717. return delayedExecute(() => {
  2718. KSplatLoader.checkVersion(fileData);
  2719. return new SplatBuffer(fileData);
  2720. });
  2721. }
  2722. static downloadFile = function() {
  2723. let downLoadLink;
  2724. return function(splatBuffer, fileName) {
  2725. const blob = new Blob([splatBuffer.bufferData], {
  2726. type: 'application/octet-stream',
  2727. });
  2728. if (!downLoadLink) {
  2729. downLoadLink = document.createElement('a');
  2730. document.body.appendChild(downLoadLink);
  2731. }
  2732. downLoadLink.download = fileName;
  2733. downLoadLink.href = URL.createObjectURL(blob);
  2734. downLoadLink.click();
  2735. };
  2736. }();
  2737. }
  2738. const SceneFormat = {
  2739. 'Splat': 0,
  2740. 'KSplat': 1,
  2741. 'Ply': 2
  2742. };
  2743. const sceneFormatFromPath = (path) => {
  2744. if (path.endsWith('.ply')) return SceneFormat.Ply;
  2745. else if (path.endsWith('.splat')) return SceneFormat.Splat;
  2746. else if (path.endsWith('.ksplat')) return SceneFormat.KSplat;
  2747. return null;
  2748. };
  2749. var Utils = /*#__PURE__*/Object.freeze({
  2750. __proto__: null,
  2751. sceneFormatFromPath: sceneFormatFromPath
  2752. });
  2753. /*
  2754. Copyright © 2010-2024 three.js authors & Mark Kellogg
  2755. Permission is hereby granted, free of charge, to any person obtaining a copy
  2756. of this software and associated documentation files (the "Software"), to deal
  2757. in the Software without restriction, including without limitation the rights
  2758. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  2759. copies of the Software, and to permit persons to whom the Software is
  2760. furnished to do so, subject to the following conditions:
  2761. The above copyright notice and this permission notice shall be included in
  2762. all copies or substantial portions of the Software.
  2763. */
  2764. // OrbitControls performs orbiting, dollying (zooming), and panning.
  2765. // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
  2766. //
  2767. // Orbit - left mouse / touch: one-finger move
  2768. // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
  2769. // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
  2770. const _changeEvent = { type: 'change' };
  2771. const _startEvent = { type: 'start' };
  2772. const _endEvent = { type: 'end' };
  2773. const _ray = new Ray$1();
  2774. const _plane = new Plane();
  2775. const TILT_LIMIT = Math.cos( 70 * MathUtils.DEG2RAD );
  2776. class OrbitControls extends EventDispatcher {
  2777. constructor( object, domElement ) {
  2778. super();
  2779. this.object = object;
  2780. this.domElement = domElement;
  2781. this.domElement.style.touchAction = 'none'; // disable touch scroll
  2782. // Set to false to disable this control
  2783. this.enabled = true;
  2784. // "target" sets the location of focus, where the object orbits around
  2785. this.target = new Vector3();
  2786. // How far you can dolly in and out ( PerspectiveCamera only )
  2787. this.minDistance = 0;
  2788. this.maxDistance = Infinity;
  2789. // How far you can zoom in and out ( OrthographicCamera only )
  2790. this.minZoom = 0;
  2791. this.maxZoom = Infinity;
  2792. // How far you can orbit vertically, upper and lower limits.
  2793. // Range is 0 to Math.PI radians.
  2794. this.minPolarAngle = 0; // radians
  2795. this.maxPolarAngle = Math.PI; // radians
  2796. // How far you can orbit horizontally, upper and lower limits.
  2797. // If set, the interval [min, max] must be a sub-interval of [- 2 PI, 2 PI], with ( max - min < 2 PI )
  2798. this.minAzimuthAngle = - Infinity; // radians
  2799. this.maxAzimuthAngle = Infinity; // radians
  2800. // Set to true to enable damping (inertia)
  2801. // If damping is enabled, you must call controls.update() in your animation loop
  2802. this.enableDamping = false;
  2803. this.dampingFactor = 0.05;
  2804. // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
  2805. // Set to false to disable zooming
  2806. this.enableZoom = true;
  2807. this.zoomSpeed = 1.0;
  2808. // Set to false to disable rotating
  2809. this.enableRotate = true;
  2810. this.rotateSpeed = 1.0;
  2811. // Set to false to disable panning
  2812. this.enablePan = true;
  2813. this.panSpeed = 1.0;
  2814. this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
  2815. this.keyPanSpeed = 7.0; // pixels moved per arrow key push
  2816. this.zoomToCursor = false;
  2817. // Set to true to automatically rotate around the target
  2818. // If auto-rotate is enabled, you must call controls.update() in your animation loop
  2819. this.autoRotate = false;
  2820. this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60
  2821. // The four arrow keys
  2822. this.keys = { LEFT: 'KeyA', UP: 'KeyW', RIGHT: 'KeyD', BOTTOM: 'KeyS' };
  2823. // Mouse buttons
  2824. this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
  2825. // Touch fingers
  2826. this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
  2827. // for reset
  2828. this.target0 = this.target.clone();
  2829. this.position0 = this.object.position.clone();
  2830. this.zoom0 = this.object.zoom;
  2831. // the target DOM element for key events
  2832. this._domElementKeyEvents = null;
  2833. //
  2834. // public methods
  2835. //
  2836. this.getPolarAngle = function() {
  2837. return spherical.phi;
  2838. };
  2839. this.getAzimuthalAngle = function() {
  2840. return spherical.theta;
  2841. };
  2842. this.getDistance = function() {
  2843. return this.object.position.distanceTo( this.target );
  2844. };
  2845. this.listenToKeyEvents = function( domElement ) {
  2846. domElement.addEventListener( 'keydown', onKeyDown );
  2847. this._domElementKeyEvents = domElement;
  2848. };
  2849. this.stopListenToKeyEvents = function() {
  2850. this._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );
  2851. this._domElementKeyEvents = null;
  2852. };
  2853. this.saveState = function() {
  2854. scope.target0.copy( scope.target );
  2855. scope.position0.copy( scope.object.position );
  2856. scope.zoom0 = scope.object.zoom;
  2857. };
  2858. this.reset = function() {
  2859. scope.target.copy( scope.target0 );
  2860. scope.object.position.copy( scope.position0 );
  2861. scope.object.zoom = scope.zoom0;
  2862. this.clearDampedRotation();
  2863. this.clearDampedPan();
  2864. scope.object.updateProjectionMatrix();
  2865. scope.dispatchEvent( _changeEvent );
  2866. scope.update();
  2867. state = STATE.NONE;
  2868. };
  2869. this.clearDampedRotation = function() {
  2870. sphericalDelta.theta = 0.0;
  2871. sphericalDelta.phi = 0.0;
  2872. };
  2873. this.clearDampedPan = function() {
  2874. panOffset.set(0, 0, 0);
  2875. };
  2876. // this method is exposed, but perhaps it would be better if we can make it private...
  2877. this.update = function() {
  2878. const offset = new Vector3();
  2879. // so camera.up is the orbit axis
  2880. const quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
  2881. const quatInverse = quat.clone().invert();
  2882. const lastPosition = new Vector3();
  2883. const lastQuaternion = new Quaternion();
  2884. const lastTargetPosition = new Vector3();
  2885. const twoPI = 2 * Math.PI;
  2886. return function update() {
  2887. quat.setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
  2888. quatInverse.copy(quat).invert();
  2889. const position = scope.object.position;
  2890. offset.copy( position ).sub( scope.target );
  2891. // rotate offset to "y-axis-is-up" space
  2892. offset.applyQuaternion( quat );
  2893. // angle from z-axis around y-axis
  2894. spherical.setFromVector3( offset );
  2895. if ( scope.autoRotate && state === STATE.NONE ) {
  2896. rotateLeft( getAutoRotationAngle() );
  2897. }
  2898. if ( scope.enableDamping ) {
  2899. spherical.theta += sphericalDelta.theta * scope.dampingFactor;
  2900. spherical.phi += sphericalDelta.phi * scope.dampingFactor;
  2901. } else {
  2902. spherical.theta += sphericalDelta.theta;
  2903. spherical.phi += sphericalDelta.phi;
  2904. }
  2905. // restrict theta to be between desired limits
  2906. let min = scope.minAzimuthAngle;
  2907. let max = scope.maxAzimuthAngle;
  2908. if ( isFinite( min ) && isFinite( max ) ) {
  2909. if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;
  2910. if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;
  2911. if ( min <= max ) {
  2912. spherical.theta = Math.max( min, Math.min( max, spherical.theta ) );
  2913. } else {
  2914. spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ?
  2915. Math.max( min, spherical.theta ) :
  2916. Math.min( max, spherical.theta );
  2917. }
  2918. }
  2919. // restrict phi to be between desired limits
  2920. spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
  2921. spherical.makeSafe();
  2922. // move target to panned location
  2923. if ( scope.enableDamping === true ) {
  2924. scope.target.addScaledVector( panOffset, scope.dampingFactor );
  2925. } else {
  2926. scope.target.add( panOffset );
  2927. }
  2928. // adjust the camera position based on zoom only if we're not zooming to the cursor or if it's an ortho camera
  2929. // we adjust zoom later in these cases
  2930. if ( scope.zoomToCursor && performCursorZoom || scope.object.isOrthographicCamera ) {
  2931. spherical.radius = clampDistance( spherical.radius );
  2932. } else {
  2933. spherical.radius = clampDistance( spherical.radius * scale );
  2934. }
  2935. offset.setFromSpherical( spherical );
  2936. // rotate offset back to "camera-up-vector-is-up" space
  2937. offset.applyQuaternion( quatInverse );
  2938. position.copy( scope.target ).add( offset );
  2939. scope.object.lookAt( scope.target );
  2940. if ( scope.enableDamping === true ) {
  2941. sphericalDelta.theta *= ( 1 - scope.dampingFactor );
  2942. sphericalDelta.phi *= ( 1 - scope.dampingFactor );
  2943. panOffset.multiplyScalar( 1 - scope.dampingFactor );
  2944. } else {
  2945. sphericalDelta.set( 0, 0, 0 );
  2946. panOffset.set( 0, 0, 0 );
  2947. }
  2948. // adjust camera position
  2949. let zoomChanged = false;
  2950. if ( scope.zoomToCursor && performCursorZoom ) {
  2951. let newRadius = null;
  2952. if ( scope.object.isPerspectiveCamera ) {
  2953. // move the camera down the pointer ray
  2954. // this method avoids floating point error
  2955. const prevRadius = offset.length();
  2956. newRadius = clampDistance( prevRadius * scale );
  2957. const radiusDelta = prevRadius - newRadius;
  2958. scope.object.position.addScaledVector( dollyDirection, radiusDelta );
  2959. scope.object.updateMatrixWorld();
  2960. } else if ( scope.object.isOrthographicCamera ) {
  2961. // adjust the ortho camera position based on zoom changes
  2962. const mouseBefore = new Vector3( mouse.x, mouse.y, 0 );
  2963. mouseBefore.unproject( scope.object );
  2964. scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) );
  2965. scope.object.updateProjectionMatrix();
  2966. zoomChanged = true;
  2967. const mouseAfter = new Vector3( mouse.x, mouse.y, 0 );
  2968. mouseAfter.unproject( scope.object );
  2969. scope.object.position.sub( mouseAfter ).add( mouseBefore );
  2970. scope.object.updateMatrixWorld();
  2971. newRadius = offset.length();
  2972. } else {
  2973. console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled.' );
  2974. scope.zoomToCursor = false;
  2975. }
  2976. // handle the placement of the target
  2977. if ( newRadius !== null ) {
  2978. if ( this.screenSpacePanning ) {
  2979. // position the orbit target in front of the new camera position
  2980. scope.target.set( 0, 0, - 1 )
  2981. .transformDirection( scope.object.matrix )
  2982. .multiplyScalar( newRadius )
  2983. .add( scope.object.position );
  2984. } else {
  2985. // get the ray and translation plane to compute target
  2986. _ray.origin.copy( scope.object.position );
  2987. _ray.direction.set( 0, 0, - 1 ).transformDirection( scope.object.matrix );
  2988. // if the camera is 20 degrees above the horizon then don't adjust the focus target to avoid
  2989. // extremely large values
  2990. if ( Math.abs( scope.object.up.dot( _ray.direction ) ) < TILT_LIMIT ) {
  2991. object.lookAt( scope.target );
  2992. } else {
  2993. _plane.setFromNormalAndCoplanarPoint( scope.object.up, scope.target );
  2994. _ray.intersectPlane( _plane, scope.target );
  2995. }
  2996. }
  2997. }
  2998. } else if ( scope.object.isOrthographicCamera ) {
  2999. scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) );
  3000. scope.object.updateProjectionMatrix();
  3001. zoomChanged = true;
  3002. }
  3003. scale = 1;
  3004. performCursorZoom = false;
  3005. // update condition is:
  3006. // min(camera displacement, camera rotation in radians)^2 > EPS
  3007. // using small-angle approximation cos(x/2) = 1 - x^2 / 8
  3008. if ( zoomChanged ||
  3009. lastPosition.distanceToSquared( scope.object.position ) > EPS ||
  3010. 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ||
  3011. lastTargetPosition.distanceToSquared( scope.target ) > 0 ) {
  3012. scope.dispatchEvent( _changeEvent );
  3013. lastPosition.copy( scope.object.position );
  3014. lastQuaternion.copy( scope.object.quaternion );
  3015. lastTargetPosition.copy( scope.target );
  3016. zoomChanged = false;
  3017. return true;
  3018. }
  3019. return false;
  3020. };
  3021. }();
  3022. this.dispose = function() {
  3023. scope.domElement.removeEventListener( 'contextmenu', onContextMenu );
  3024. scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
  3025. scope.domElement.removeEventListener( 'pointercancel', onPointerUp );
  3026. scope.domElement.removeEventListener( 'wheel', onMouseWheel );
  3027. scope.domElement.removeEventListener( 'pointermove', onPointerMove );
  3028. scope.domElement.removeEventListener( 'pointerup', onPointerUp );
  3029. if ( scope._domElementKeyEvents !== null ) {
  3030. scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );
  3031. scope._domElementKeyEvents = null;
  3032. }
  3033. };
  3034. //
  3035. // internals
  3036. //
  3037. const scope = this;
  3038. const STATE = {
  3039. NONE: - 1,
  3040. ROTATE: 0,
  3041. DOLLY: 1,
  3042. PAN: 2,
  3043. TOUCH_ROTATE: 3,
  3044. TOUCH_PAN: 4,
  3045. TOUCH_DOLLY_PAN: 5,
  3046. TOUCH_DOLLY_ROTATE: 6
  3047. };
  3048. let state = STATE.NONE;
  3049. const EPS = 0.000001;
  3050. // current position in spherical coordinates
  3051. const spherical = new Spherical();
  3052. const sphericalDelta = new Spherical();
  3053. let scale = 1;
  3054. const panOffset = new Vector3();
  3055. const rotateStart = new Vector2();
  3056. const rotateEnd = new Vector2();
  3057. const rotateDelta = new Vector2();
  3058. const panStart = new Vector2();
  3059. const panEnd = new Vector2();
  3060. const panDelta = new Vector2();
  3061. const dollyStart = new Vector2();
  3062. const dollyEnd = new Vector2();
  3063. const dollyDelta = new Vector2();
  3064. const dollyDirection = new Vector3();
  3065. const mouse = new Vector2();
  3066. let performCursorZoom = false;
  3067. const pointers = [];
  3068. const pointerPositions = {};
  3069. function getAutoRotationAngle() {
  3070. return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
  3071. }
  3072. function getZoomScale() {
  3073. return Math.pow( 0.95, scope.zoomSpeed );
  3074. }
  3075. function rotateLeft( angle ) {
  3076. sphericalDelta.theta -= angle;
  3077. }
  3078. function rotateUp( angle ) {
  3079. sphericalDelta.phi -= angle;
  3080. }
  3081. const panLeft = function() {
  3082. const v = new Vector3();
  3083. return function panLeft( distance, objectMatrix ) {
  3084. v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
  3085. v.multiplyScalar( - distance );
  3086. panOffset.add( v );
  3087. };
  3088. }();
  3089. const panUp = function() {
  3090. const v = new Vector3();
  3091. return function panUp( distance, objectMatrix ) {
  3092. if ( scope.screenSpacePanning === true ) {
  3093. v.setFromMatrixColumn( objectMatrix, 1 );
  3094. } else {
  3095. v.setFromMatrixColumn( objectMatrix, 0 );
  3096. v.crossVectors( scope.object.up, v );
  3097. }
  3098. v.multiplyScalar( distance );
  3099. panOffset.add( v );
  3100. };
  3101. }();
  3102. // deltaX and deltaY are in pixels; right and down are positive
  3103. const pan = function() {
  3104. const offset = new Vector3();
  3105. return function pan( deltaX, deltaY ) {
  3106. const element = scope.domElement;
  3107. if ( scope.object.isPerspectiveCamera ) {
  3108. // perspective
  3109. const position = scope.object.position;
  3110. offset.copy( position ).sub( scope.target );
  3111. let targetDistance = offset.length();
  3112. // half of the fov is center to top of screen
  3113. targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
  3114. // we use only clientHeight here so aspect ratio does not distort speed
  3115. panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
  3116. panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
  3117. } else if ( scope.object.isOrthographicCamera ) {
  3118. // orthographic
  3119. panLeft( deltaX * ( scope.object.right - scope.object.left ) /
  3120. scope.object.zoom / element.clientWidth, scope.object.matrix );
  3121. panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom /
  3122. element.clientHeight, scope.object.matrix );
  3123. } else {
  3124. // camera neither orthographic nor perspective
  3125. console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
  3126. scope.enablePan = false;
  3127. }
  3128. };
  3129. }();
  3130. function dollyOut( dollyScale ) {
  3131. if ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) {
  3132. scale /= dollyScale;
  3133. } else {
  3134. console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
  3135. scope.enableZoom = false;
  3136. }
  3137. }
  3138. function dollyIn( dollyScale ) {
  3139. if ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) {
  3140. scale *= dollyScale;
  3141. } else {
  3142. console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
  3143. scope.enableZoom = false;
  3144. }
  3145. }
  3146. function updateMouseParameters( event ) {
  3147. if ( ! scope.zoomToCursor ) {
  3148. return;
  3149. }
  3150. performCursorZoom = true;
  3151. const rect = scope.domElement.getBoundingClientRect();
  3152. const x = event.clientX - rect.left;
  3153. const y = event.clientY - rect.top;
  3154. const w = rect.width;
  3155. const h = rect.height;
  3156. mouse.x = ( x / w ) * 2 - 1;
  3157. mouse.y = - ( y / h ) * 2 + 1;
  3158. dollyDirection.set( mouse.x, mouse.y, 1 ).unproject( object ).sub( object.position ).normalize();
  3159. }
  3160. function clampDistance( dist ) {
  3161. return Math.max( scope.minDistance, Math.min( scope.maxDistance, dist ) );
  3162. }
  3163. //
  3164. // event callbacks - update the object state
  3165. //
  3166. function handleMouseDownRotate( event ) {
  3167. rotateStart.set( event.clientX, event.clientY );
  3168. }
  3169. function handleMouseDownDolly( event ) {
  3170. updateMouseParameters( event );
  3171. dollyStart.set( event.clientX, event.clientY );
  3172. }
  3173. function handleMouseDownPan( event ) {
  3174. panStart.set( event.clientX, event.clientY );
  3175. }
  3176. function handleMouseMoveRotate( event ) {
  3177. rotateEnd.set( event.clientX, event.clientY );
  3178. rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
  3179. const element = scope.domElement;
  3180. rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
  3181. rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
  3182. rotateStart.copy( rotateEnd );
  3183. scope.update();
  3184. }
  3185. function handleMouseMoveDolly( event ) {
  3186. dollyEnd.set( event.clientX, event.clientY );
  3187. dollyDelta.subVectors( dollyEnd, dollyStart );
  3188. if ( dollyDelta.y > 0 ) {
  3189. dollyOut( getZoomScale() );
  3190. } else if ( dollyDelta.y < 0 ) {
  3191. dollyIn( getZoomScale() );
  3192. }
  3193. dollyStart.copy( dollyEnd );
  3194. scope.update();
  3195. }
  3196. function handleMouseMovePan( event ) {
  3197. panEnd.set( event.clientX, event.clientY );
  3198. panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
  3199. pan( panDelta.x, panDelta.y );
  3200. panStart.copy( panEnd );
  3201. scope.update();
  3202. }
  3203. function handleMouseWheel( event ) {
  3204. updateMouseParameters( event );
  3205. if ( event.deltaY < 0 ) {
  3206. dollyIn( getZoomScale() );
  3207. } else if ( event.deltaY > 0 ) {
  3208. dollyOut( getZoomScale() );
  3209. }
  3210. scope.update();
  3211. }
  3212. function handleKeyDown( event ) {
  3213. let needsUpdate = false;
  3214. switch ( event.code ) {
  3215. case scope.keys.UP:
  3216. if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
  3217. rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
  3218. } else {
  3219. pan( 0, scope.keyPanSpeed );
  3220. }
  3221. needsUpdate = true;
  3222. break;
  3223. case scope.keys.BOTTOM:
  3224. if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
  3225. rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
  3226. } else {
  3227. pan( 0, - scope.keyPanSpeed );
  3228. }
  3229. needsUpdate = true;
  3230. break;
  3231. case scope.keys.LEFT:
  3232. if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
  3233. rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
  3234. } else {
  3235. pan( scope.keyPanSpeed, 0 );
  3236. }
  3237. needsUpdate = true;
  3238. break;
  3239. case scope.keys.RIGHT:
  3240. if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
  3241. rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
  3242. } else {
  3243. pan( - scope.keyPanSpeed, 0 );
  3244. }
  3245. needsUpdate = true;
  3246. break;
  3247. }
  3248. if ( needsUpdate ) {
  3249. // prevent the browser from scrolling on cursor keys
  3250. event.preventDefault();
  3251. scope.update();
  3252. }
  3253. }
  3254. function handleTouchStartRotate() {
  3255. if ( pointers.length === 1 ) {
  3256. rotateStart.set( pointers[0].pageX, pointers[0].pageY );
  3257. } else {
  3258. const x = 0.5 * ( pointers[0].pageX + pointers[1].pageX );
  3259. const y = 0.5 * ( pointers[0].pageY + pointers[1].pageY );
  3260. rotateStart.set( x, y );
  3261. }
  3262. }
  3263. function handleTouchStartPan() {
  3264. if ( pointers.length === 1 ) {
  3265. panStart.set( pointers[0].pageX, pointers[0].pageY );
  3266. } else {
  3267. const x = 0.5 * ( pointers[0].pageX + pointers[1].pageX );
  3268. const y = 0.5 * ( pointers[0].pageY + pointers[1].pageY );
  3269. panStart.set( x, y );
  3270. }
  3271. }
  3272. function handleTouchStartDolly() {
  3273. const dx = pointers[0].pageX - pointers[1].pageX;
  3274. const dy = pointers[0].pageY - pointers[1].pageY;
  3275. const distance = Math.sqrt( dx * dx + dy * dy );
  3276. dollyStart.set( 0, distance );
  3277. }
  3278. function handleTouchStartDollyPan() {
  3279. if ( scope.enableZoom ) handleTouchStartDolly();
  3280. if ( scope.enablePan ) handleTouchStartPan();
  3281. }
  3282. function handleTouchStartDollyRotate() {
  3283. if ( scope.enableZoom ) handleTouchStartDolly();
  3284. if ( scope.enableRotate ) handleTouchStartRotate();
  3285. }
  3286. function handleTouchMoveRotate( event ) {
  3287. if ( pointers.length == 1 ) {
  3288. rotateEnd.set( event.pageX, event.pageY );
  3289. } else {
  3290. const position = getSecondPointerPosition( event );
  3291. const x = 0.5 * ( event.pageX + position.x );
  3292. const y = 0.5 * ( event.pageY + position.y );
  3293. rotateEnd.set( x, y );
  3294. }
  3295. rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
  3296. const element = scope.domElement;
  3297. rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
  3298. rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
  3299. rotateStart.copy( rotateEnd );
  3300. }
  3301. function handleTouchMovePan( event ) {
  3302. if ( pointers.length === 1 ) {
  3303. panEnd.set( event.pageX, event.pageY );
  3304. } else {
  3305. const position = getSecondPointerPosition( event );
  3306. const x = 0.5 * ( event.pageX + position.x );
  3307. const y = 0.5 * ( event.pageY + position.y );
  3308. panEnd.set( x, y );
  3309. }
  3310. panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
  3311. pan( panDelta.x, panDelta.y );
  3312. panStart.copy( panEnd );
  3313. }
  3314. function handleTouchMoveDolly( event ) {
  3315. const position = getSecondPointerPosition( event );
  3316. const dx = event.pageX - position.x;
  3317. const dy = event.pageY - position.y;
  3318. const distance = Math.sqrt( dx * dx + dy * dy );
  3319. dollyEnd.set( 0, distance );
  3320. dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
  3321. dollyOut( dollyDelta.y );
  3322. dollyStart.copy( dollyEnd );
  3323. }
  3324. function handleTouchMoveDollyPan( event ) {
  3325. if ( scope.enableZoom ) handleTouchMoveDolly( event );
  3326. if ( scope.enablePan ) handleTouchMovePan( event );
  3327. }
  3328. function handleTouchMoveDollyRotate( event ) {
  3329. if ( scope.enableZoom ) handleTouchMoveDolly( event );
  3330. if ( scope.enableRotate ) handleTouchMoveRotate( event );
  3331. }
  3332. //
  3333. // event handlers - FSM: listen for events and reset state
  3334. //
  3335. function onPointerDown( event ) {
  3336. if ( scope.enabled === false ) return;
  3337. if ( pointers.length === 0 ) {
  3338. scope.domElement.setPointerCapture( event.pointerId );
  3339. scope.domElement.addEventListener( 'pointermove', onPointerMove );
  3340. scope.domElement.addEventListener( 'pointerup', onPointerUp );
  3341. }
  3342. //
  3343. addPointer( event );
  3344. if ( event.pointerType === 'touch' ) {
  3345. onTouchStart( event );
  3346. } else {
  3347. onMouseDown( event );
  3348. }
  3349. }
  3350. function onPointerMove( event ) {
  3351. if ( scope.enabled === false ) return;
  3352. if ( event.pointerType === 'touch' ) {
  3353. onTouchMove( event );
  3354. } else {
  3355. onMouseMove( event );
  3356. }
  3357. }
  3358. function onPointerUp( event ) {
  3359. removePointer( event );
  3360. if ( pointers.length === 0 ) {
  3361. scope.domElement.releasePointerCapture( event.pointerId );
  3362. scope.domElement.removeEventListener( 'pointermove', onPointerMove );
  3363. scope.domElement.removeEventListener( 'pointerup', onPointerUp );
  3364. }
  3365. scope.dispatchEvent( _endEvent );
  3366. state = STATE.NONE;
  3367. }
  3368. function onMouseDown( event ) {
  3369. let mouseAction;
  3370. switch ( event.button ) {
  3371. case 0:
  3372. mouseAction = scope.mouseButtons.LEFT;
  3373. break;
  3374. case 1:
  3375. mouseAction = scope.mouseButtons.MIDDLE;
  3376. break;
  3377. case 2:
  3378. mouseAction = scope.mouseButtons.RIGHT;
  3379. break;
  3380. default:
  3381. mouseAction = - 1;
  3382. }
  3383. switch ( mouseAction ) {
  3384. case MOUSE.DOLLY:
  3385. if ( scope.enableZoom === false ) return;
  3386. handleMouseDownDolly( event );
  3387. state = STATE.DOLLY;
  3388. break;
  3389. case MOUSE.ROTATE:
  3390. if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
  3391. if ( scope.enablePan === false ) return;
  3392. handleMouseDownPan( event );
  3393. state = STATE.PAN;
  3394. } else {
  3395. if ( scope.enableRotate === false ) return;
  3396. handleMouseDownRotate( event );
  3397. state = STATE.ROTATE;
  3398. }
  3399. break;
  3400. case MOUSE.PAN:
  3401. if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
  3402. if ( scope.enableRotate === false ) return;
  3403. handleMouseDownRotate( event );
  3404. state = STATE.ROTATE;
  3405. } else {
  3406. if ( scope.enablePan === false ) return;
  3407. handleMouseDownPan( event );
  3408. state = STATE.PAN;
  3409. }
  3410. break;
  3411. default:
  3412. state = STATE.NONE;
  3413. }
  3414. if ( state !== STATE.NONE ) {
  3415. scope.dispatchEvent( _startEvent );
  3416. }
  3417. }
  3418. function onMouseMove( event ) {
  3419. switch ( state ) {
  3420. case STATE.ROTATE:
  3421. if ( scope.enableRotate === false ) return;
  3422. handleMouseMoveRotate( event );
  3423. break;
  3424. case STATE.DOLLY:
  3425. if ( scope.enableZoom === false ) return;
  3426. handleMouseMoveDolly( event );
  3427. break;
  3428. case STATE.PAN:
  3429. if ( scope.enablePan === false ) return;
  3430. handleMouseMovePan( event );
  3431. break;
  3432. }
  3433. }
  3434. function onMouseWheel( event ) {
  3435. if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
  3436. event.preventDefault();
  3437. scope.dispatchEvent( _startEvent );
  3438. handleMouseWheel( event );
  3439. scope.dispatchEvent( _endEvent );
  3440. }
  3441. function onKeyDown( event ) {
  3442. if ( scope.enabled === false || scope.enablePan === false ) return;
  3443. handleKeyDown( event );
  3444. }
  3445. function onTouchStart( event ) {
  3446. trackPointer( event );
  3447. switch ( pointers.length ) {
  3448. case 1:
  3449. switch ( scope.touches.ONE ) {
  3450. case TOUCH.ROTATE:
  3451. if ( scope.enableRotate === false ) return;
  3452. handleTouchStartRotate();
  3453. state = STATE.TOUCH_ROTATE;
  3454. break;
  3455. case TOUCH.PAN:
  3456. if ( scope.enablePan === false ) return;
  3457. handleTouchStartPan();
  3458. state = STATE.TOUCH_PAN;
  3459. break;
  3460. default:
  3461. state = STATE.NONE;
  3462. }
  3463. break;
  3464. case 2:
  3465. switch ( scope.touches.TWO ) {
  3466. case TOUCH.DOLLY_PAN:
  3467. if ( scope.enableZoom === false && scope.enablePan === false ) return;
  3468. handleTouchStartDollyPan();
  3469. state = STATE.TOUCH_DOLLY_PAN;
  3470. break;
  3471. case TOUCH.DOLLY_ROTATE:
  3472. if ( scope.enableZoom === false && scope.enableRotate === false ) return;
  3473. handleTouchStartDollyRotate();
  3474. state = STATE.TOUCH_DOLLY_ROTATE;
  3475. break;
  3476. default:
  3477. state = STATE.NONE;
  3478. }
  3479. break;
  3480. default:
  3481. state = STATE.NONE;
  3482. }
  3483. if ( state !== STATE.NONE ) {
  3484. scope.dispatchEvent( _startEvent );
  3485. }
  3486. }
  3487. function onTouchMove( event ) {
  3488. trackPointer( event );
  3489. switch ( state ) {
  3490. case STATE.TOUCH_ROTATE:
  3491. if ( scope.enableRotate === false ) return;
  3492. handleTouchMoveRotate( event );
  3493. scope.update();
  3494. break;
  3495. case STATE.TOUCH_PAN:
  3496. if ( scope.enablePan === false ) return;
  3497. handleTouchMovePan( event );
  3498. scope.update();
  3499. break;
  3500. case STATE.TOUCH_DOLLY_PAN:
  3501. if ( scope.enableZoom === false && scope.enablePan === false ) return;
  3502. handleTouchMoveDollyPan( event );
  3503. scope.update();
  3504. break;
  3505. case STATE.TOUCH_DOLLY_ROTATE:
  3506. if ( scope.enableZoom === false && scope.enableRotate === false ) return;
  3507. handleTouchMoveDollyRotate( event );
  3508. scope.update();
  3509. break;
  3510. default:
  3511. state = STATE.NONE;
  3512. }
  3513. }
  3514. function onContextMenu( event ) {
  3515. if ( scope.enabled === false ) return;
  3516. event.preventDefault();
  3517. }
  3518. function addPointer( event ) {
  3519. pointers.push( event );
  3520. }
  3521. function removePointer( event ) {
  3522. delete pointerPositions[event.pointerId];
  3523. for ( let i = 0; i < pointers.length; i ++ ) {
  3524. if ( pointers[i].pointerId == event.pointerId ) {
  3525. pointers.splice( i, 1 );
  3526. return;
  3527. }
  3528. }
  3529. }
  3530. function trackPointer( event ) {
  3531. let position = pointerPositions[event.pointerId];
  3532. if ( position === undefined ) {
  3533. position = new Vector2();
  3534. pointerPositions[event.pointerId] = position;
  3535. }
  3536. position.set( event.pageX, event.pageY );
  3537. }
  3538. function getSecondPointerPosition( event ) {
  3539. const pointer = ( event.pointerId === pointers[0].pointerId ) ? pointers[1] : pointers[0];
  3540. return pointerPositions[pointer.pointerId];
  3541. }
  3542. //
  3543. scope.domElement.addEventListener( 'contextmenu', onContextMenu );
  3544. scope.domElement.addEventListener( 'pointerdown', onPointerDown );
  3545. scope.domElement.addEventListener( 'pointercancel', onPointerUp );
  3546. scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
  3547. // force an update at start
  3548. this.update();
  3549. }
  3550. }
  3551. const fadeElement = (element, out, displayStyle, duration, onComplete) => {
  3552. const startTime = performance.now();
  3553. let startOpacity = element.style.display === 'none' ? 0 : parseFloat(element.style.opacity);
  3554. if (isNaN(startOpacity)) startOpacity = 1;
  3555. const interval = window.setInterval(() => {
  3556. const currentTime = performance.now();
  3557. const elapsed = currentTime - startTime;
  3558. let t = Math.min(elapsed / duration, 1.0);
  3559. if (t > 0.999) t = 1;
  3560. let opacity;
  3561. if (out) {
  3562. opacity = (1.0 - t) * startOpacity;
  3563. if (opacity < 0.0001) opacity = 0;
  3564. } else {
  3565. opacity = (1.0 - startOpacity) * t + startOpacity;
  3566. }
  3567. if (opacity > 0) {
  3568. element.style.display = displayStyle;
  3569. element.style.opacity = opacity;
  3570. } else {
  3571. element.style.display = 'none';
  3572. }
  3573. if (t >= 1) {
  3574. if (onComplete) onComplete();
  3575. window.clearInterval(interval);
  3576. }
  3577. }, 16);
  3578. return interval;
  3579. };
  3580. const cancelFade = (interval) => {
  3581. window.clearInterval(interval);
  3582. };
  3583. const STANDARD_FADE_DURATION = 500;
  3584. class LoadingSpinner {
  3585. static elementIDGen = 0;
  3586. constructor(message, container) {
  3587. this.taskIDGen = 0;
  3588. this.elementID = LoadingSpinner.elementIDGen++;
  3589. this.tasks = [];
  3590. this.message = message || 'Loading...';
  3591. this.container = container || document.body;
  3592. this.spinnerContainerOuter = document.createElement('div');
  3593. this.spinnerContainerOuter.className = `spinnerOuterContainer${this.elementID}`;
  3594. this.spinnerContainerOuter.style.display = 'none';
  3595. this.spinnerContainerPrimary = document.createElement('div');
  3596. this.spinnerContainerPrimary.className = `spinnerContainerPrimary${this.elementID}`;
  3597. this.spinnerPrimary = document.createElement('div');
  3598. this.spinnerPrimary.classList.add(`spinner${this.elementID}`, `spinnerPrimary${this.elementID}`);
  3599. this.messageContainerPrimary = document.createElement('div');
  3600. this.messageContainerPrimary.classList.add(`messageContainer${this.elementID}`, `messageContainerPrimary${this.elementID}`);
  3601. this.messageContainerPrimary.innerHTML = this.message;
  3602. this.spinnerContainerMin = document.createElement('div');
  3603. this.spinnerContainerMin.className = `spinnerContainerMin${this.elementID}`;
  3604. this.spinnerMin = document.createElement('div');
  3605. this.spinnerMin.classList.add(`spinner${this.elementID}`, `spinnerMin${this.elementID}`);
  3606. this.messageContainerMin = document.createElement('div');
  3607. this.messageContainerMin.classList.add(`messageContainer${this.elementID}`, `messageContainerMin${this.elementID}`);
  3608. this.messageContainerMin.innerHTML = this.message;
  3609. this.spinnerContainerPrimary.appendChild(this.spinnerPrimary);
  3610. this.spinnerContainerPrimary.appendChild(this.messageContainerPrimary);
  3611. this.spinnerContainerOuter.appendChild(this.spinnerContainerPrimary);
  3612. this.spinnerContainerMin.appendChild(this.spinnerMin);
  3613. this.spinnerContainerMin.appendChild(this.messageContainerMin);
  3614. this.spinnerContainerOuter.appendChild(this.spinnerContainerMin);
  3615. const style = document.createElement('style');
  3616. style.innerHTML = `
  3617. .spinnerOuterContainer${this.elementID} {
  3618. width: 100%;
  3619. height: 100%;
  3620. margin: 0;
  3621. top: 0;
  3622. left: 0;
  3623. position: absolute;
  3624. pointer-events: none;
  3625. }
  3626. .messageContainer${this.elementID} {
  3627. height: 20px;
  3628. font-family: arial;
  3629. font-size: 12pt;
  3630. color: #ffffff;
  3631. text-align: center;
  3632. vertical-align: middle;
  3633. }
  3634. .spinner${this.elementID} {
  3635. padding: 15px;
  3636. background: #07e8d6;
  3637. z-index:99999;
  3638. aspect-ratio: 1;
  3639. border-radius: 50%;
  3640. --_m:
  3641. conic-gradient(#0000,#000),
  3642. linear-gradient(#000 0 0) content-box;
  3643. -webkit-mask: var(--_m);
  3644. mask: var(--_m);
  3645. -webkit-mask-composite: source-out;
  3646. mask-composite: subtract;
  3647. box-sizing: border-box;
  3648. animation: load 1s linear infinite;
  3649. }
  3650. .spinnerContainerPrimary${this.elementID} {
  3651. z-index:99999;
  3652. background-color: rgba(128, 128, 128, 0.75);
  3653. border: #666666 1px solid;
  3654. border-radius: 5px;
  3655. padding-top: 20px;
  3656. padding-bottom: 10px;
  3657. margin: 0;
  3658. position: absolute;
  3659. top: 50%;
  3660. left: 50%;
  3661. transform: translate(-80px, -80px);
  3662. width: 180px;
  3663. pointer-events: auto;
  3664. }
  3665. .spinnerPrimary${this.elementID} {
  3666. width: 120px;
  3667. margin-left: 30px;
  3668. }
  3669. .messageContainerPrimary${this.elementID} {
  3670. padding-top: 15px;
  3671. }
  3672. .spinnerContainerMin${this.elementID} {
  3673. z-index:99999;
  3674. background-color: rgba(128, 128, 128, 0.75);
  3675. border: #666666 1px solid;
  3676. border-radius: 5px;
  3677. padding-top: 20px;
  3678. padding-bottom: 15px;
  3679. margin: 0;
  3680. position: absolute;
  3681. bottom: 50px;
  3682. left: 50%;
  3683. transform: translate(-50%, 0);
  3684. display: flex;
  3685. flex-direction: left;
  3686. pointer-events: auto;
  3687. min-width: 250px;
  3688. }
  3689. .messageContainerMin${this.elementID} {
  3690. margin-right: 15px;
  3691. }
  3692. .spinnerMin${this.elementID} {
  3693. width: 50px;
  3694. height: 50px;
  3695. margin-left: 15px;
  3696. margin-right: 25px;
  3697. }
  3698. .messageContainerMin${this.elementID} {
  3699. padding-top: 15px;
  3700. }
  3701. @keyframes load {
  3702. to{transform: rotate(1turn)}
  3703. }
  3704. `;
  3705. this.spinnerContainerOuter.appendChild(style);
  3706. this.container.appendChild(this.spinnerContainerOuter);
  3707. this.setMinimized(false, true);
  3708. this.fadeTransitions = [];
  3709. }
  3710. addTask(message) {
  3711. const newTask = {
  3712. 'message': message,
  3713. 'id': this.taskIDGen++
  3714. };
  3715. this.tasks.push(newTask);
  3716. this.update();
  3717. return newTask.id;
  3718. }
  3719. removeTask(id) {
  3720. let index = 0;
  3721. for (let task of this.tasks) {
  3722. if (task.id === id) {
  3723. this.tasks.splice(index, 1);
  3724. break;
  3725. }
  3726. index++;
  3727. }
  3728. this.update();
  3729. }
  3730. removeAllTasks() {
  3731. this.tasks = [];
  3732. this.update();
  3733. }
  3734. setMessageForTask(id, message) {
  3735. for (let task of this.tasks) {
  3736. if (task.id === id) {
  3737. task.message = message;
  3738. break;
  3739. }
  3740. }
  3741. this.update();
  3742. }
  3743. update() {
  3744. if (this.tasks.length > 0) {
  3745. this.show();
  3746. this.setMessage(this.tasks[this.tasks.length - 1].message);
  3747. } else {
  3748. this.hide();
  3749. }
  3750. }
  3751. show() {
  3752. this.spinnerContainerOuter.style.display = 'block';
  3753. this.visible = true;
  3754. }
  3755. hide() {
  3756. this.spinnerContainerOuter.style.display = 'none';
  3757. this.visible = false;
  3758. }
  3759. setContainer(container) {
  3760. if (this.container) {
  3761. this.container.removeChild(this.spinnerContainerOuter);
  3762. }
  3763. if (container) {
  3764. this.container = container;
  3765. this.container.appendChild(this.spinnerContainerOuter);
  3766. this.spinnerContainerOuter.style.zIndex = this.container.style.zIndex + 1;
  3767. }
  3768. }
  3769. setMinimized(minimized, instant) {
  3770. const showHideSpinner = (element, show, instant, displayStyle, fadeTransitionsIndex) => {
  3771. if (instant) {
  3772. element.style.display = show ? displayStyle : 'none';
  3773. } else {
  3774. this.fadeTransitions[fadeTransitionsIndex] = fadeElement(element, !show, displayStyle, STANDARD_FADE_DURATION, () => {
  3775. this.fadeTransitions[fadeTransitionsIndex] = null;
  3776. });
  3777. }
  3778. };
  3779. showHideSpinner(this.spinnerContainerPrimary, !minimized, instant, 'block', 0);
  3780. showHideSpinner(this.spinnerContainerMin, minimized, instant, 'flex', 1);
  3781. this.minimized = minimized;
  3782. }
  3783. setMessage(msg) {
  3784. this.messageContainerPrimary.innerHTML = msg;
  3785. this.messageContainerMin.innerHTML = msg;
  3786. }
  3787. }
  3788. class LoadingProgressBar {
  3789. constructor(container) {
  3790. this.idGen = 0;
  3791. this.tasks = [];
  3792. this.container = container || document.body;
  3793. this.progressBarContainerOuter = document.createElement('div');
  3794. this.progressBarContainerOuter.className = 'progressBarOuterContainer';
  3795. this.progressBarContainerOuter.style.display = 'none';
  3796. this.progressBarBox = document.createElement('div');
  3797. this.progressBarBox.className = 'progressBarBox';
  3798. this.progressBarBackground = document.createElement('div');
  3799. this.progressBarBackground.className = 'progressBarBackground';
  3800. this.progressBar = document.createElement('div');
  3801. this.progressBar.className = 'progressBar';
  3802. this.progressBarBackground.appendChild(this.progressBar);
  3803. this.progressBarBox.appendChild(this.progressBarBackground);
  3804. this.progressBarContainerOuter.appendChild(this.progressBarBox);
  3805. const style = document.createElement('style');
  3806. style.innerHTML = `
  3807. .progressBarOuterContainer {
  3808. width: 100%;
  3809. height: 100%;
  3810. margin: 0;
  3811. top: 0;
  3812. left: 0;
  3813. position: absolute;
  3814. pointer-events: none;
  3815. }
  3816. .progressBarBox {
  3817. z-index:99999;
  3818. padding: 7px 9px 5px 7px;
  3819. background-color: rgba(190, 190, 190, 0.75);
  3820. border: #555555 1px solid;
  3821. border-radius: 15px;
  3822. margin: 0;
  3823. position: absolute;
  3824. bottom: 50px;
  3825. left: 50%;
  3826. transform: translate(-50%, 0);
  3827. width: 180px;
  3828. height: 30px;
  3829. pointer-events: auto;
  3830. }
  3831. .progressBarBackground {
  3832. width: 100%;
  3833. height: 25px;
  3834. border-radius:10px;
  3835. background-color: rgba(128, 128, 128, 0.75);
  3836. border: #444444 1px solid;
  3837. box-shadow: inset 0 0 10px #333333;
  3838. }
  3839. .progressBar {
  3840. height: 25px;
  3841. width: 0px;
  3842. border-radius:10px;
  3843. background-color: rgba(0, 200, 0, 0.75);
  3844. box-shadow: inset 0 0 10px #003300;
  3845. }
  3846. `;
  3847. this.progressBarContainerOuter.appendChild(style);
  3848. this.container.appendChild(this.progressBarContainerOuter);
  3849. }
  3850. show() {
  3851. this.progressBarContainerOuter.style.display = 'block';
  3852. }
  3853. hide() {
  3854. this.progressBarContainerOuter.style.display = 'none';
  3855. }
  3856. setProgress(progress) {
  3857. this.progressBar.style.width = progress + '%';
  3858. }
  3859. setContainer(container) {
  3860. if (this.container) {
  3861. this.container.removeChild(this.progressBarContainerOuter);
  3862. }
  3863. if (container) {
  3864. this.container = container;
  3865. this.container.appendChild(this.progressBarContainerOuter);
  3866. this.progressBarContainerOuter.style.zIndex = this.container.style.zIndex + 1;
  3867. }
  3868. }
  3869. }
  3870. class InfoPanel {
  3871. constructor(container) {
  3872. this.container = container || document.body;
  3873. this.infoCells = {};
  3874. const layout = [
  3875. ['Camera position', 'cameraPosition'],
  3876. ['Camera look-at', 'cameraLookAt'],
  3877. ['Camera up', 'cameraUp'],
  3878. ['Camera mode', 'orthographicCamera'],
  3879. ['Cursor position', 'cursorPosition'],
  3880. ['FPS', 'fps'],
  3881. ['Rendering:', 'renderSplatCount'],
  3882. ['Sort time', 'sortTime'],
  3883. ['Render window', 'renderWindow'],
  3884. ['Focal adjustment', 'focalAdjustment'],
  3885. ['Splat scale', 'splatScale'],
  3886. ['Point cloud mode', 'pointCloudMode']
  3887. ];
  3888. this.infoPanelContainer = document.createElement('div');
  3889. const style = document.createElement('style');
  3890. style.innerHTML = `
  3891. .infoPanel {
  3892. width: 430px;
  3893. padding: 10px;
  3894. background-color: rgba(50, 50, 50, 0.85);
  3895. border: #555555 2px solid;
  3896. color: #dddddd;
  3897. border-radius: 10px;
  3898. z-index: 9999;
  3899. font-family: arial;
  3900. font-size: 11pt;
  3901. text-align: left;
  3902. margin: 0;
  3903. top: 10px;
  3904. left:10px;
  3905. position: absolute;
  3906. pointer-events: auto;
  3907. }
  3908. .info-panel-cell {
  3909. margin-bottom: 5px;
  3910. padding-bottom: 2px;
  3911. }
  3912. .label-cell {
  3913. font-weight: bold;
  3914. font-size: 12pt;
  3915. width: 140px;
  3916. }
  3917. `;
  3918. this.infoPanelContainer.append(style);
  3919. this.infoPanel = document.createElement('div');
  3920. this.infoPanel.className = 'infoPanel';
  3921. const infoTable = document.createElement('div');
  3922. infoTable.style.display = 'table';
  3923. for (let layoutEntry of layout) {
  3924. const row = document.createElement('div');
  3925. row.style.display = 'table-row';
  3926. row.className = 'info-panel-row';
  3927. const labelCell = document.createElement('div');
  3928. labelCell.style.display = 'table-cell';
  3929. labelCell.innerHTML = `${layoutEntry[0]}: `;
  3930. labelCell.classList.add('info-panel-cell', 'label-cell');
  3931. const spacerCell = document.createElement('div');
  3932. spacerCell.style.display = 'table-cell';
  3933. spacerCell.style.width = '10px';
  3934. spacerCell.innerHTML = ' ';
  3935. spacerCell.className = 'info-panel-cell';
  3936. const infoCell = document.createElement('div');
  3937. infoCell.style.display = 'table-cell';
  3938. infoCell.innerHTML = '';
  3939. infoCell.className = 'info-panel-cell';
  3940. this.infoCells[layoutEntry[1]] = infoCell;
  3941. row.appendChild(labelCell);
  3942. row.appendChild(spacerCell);
  3943. row.appendChild(infoCell);
  3944. infoTable.appendChild(row);
  3945. }
  3946. this.infoPanel.appendChild(infoTable);
  3947. this.infoPanelContainer.append(this.infoPanel);
  3948. this.infoPanelContainer.style.display = 'none';
  3949. this.container.appendChild(this.infoPanelContainer);
  3950. this.visible = false;
  3951. }
  3952. update = function(renderDimensions, cameraPosition, cameraLookAtPosition, cameraUp, orthographicCamera,
  3953. meshCursorPosition, currentFPS, splatCount, splatRenderCount,
  3954. splatRenderCountPct, lastSortTime, focalAdjustment, splatScale, pointCloudMode) {
  3955. const cameraPosString = `${cameraPosition.x.toFixed(5)}, ${cameraPosition.y.toFixed(5)}, ${cameraPosition.z.toFixed(5)}`;
  3956. if (this.infoCells.cameraPosition.innerHTML !== cameraPosString) {
  3957. this.infoCells.cameraPosition.innerHTML = cameraPosString;
  3958. }
  3959. if (cameraLookAtPosition) {
  3960. const cla = cameraLookAtPosition;
  3961. const cameraLookAtString = `${cla.x.toFixed(5)}, ${cla.y.toFixed(5)}, ${cla.z.toFixed(5)}`;
  3962. if (this.infoCells.cameraLookAt.innerHTML !== cameraLookAtString) {
  3963. this.infoCells.cameraLookAt.innerHTML = cameraLookAtString;
  3964. }
  3965. }
  3966. const cameraUpString = `${cameraUp.x.toFixed(5)}, ${cameraUp.y.toFixed(5)}, ${cameraUp.z.toFixed(5)}`;
  3967. if (this.infoCells.cameraUp.innerHTML !== cameraUpString) {
  3968. this.infoCells.cameraUp.innerHTML = cameraUpString;
  3969. }
  3970. this.infoCells.orthographicCamera.innerHTML = orthographicCamera ? 'Orthographic' : 'Perspective';
  3971. if (meshCursorPosition) {
  3972. const cursPos = meshCursorPosition;
  3973. const cursorPosString = `${cursPos.x.toFixed(5)}, ${cursPos.y.toFixed(5)}, ${cursPos.z.toFixed(5)}`;
  3974. this.infoCells.cursorPosition.innerHTML = cursorPosString;
  3975. } else {
  3976. this.infoCells.cursorPosition.innerHTML = 'N/A';
  3977. }
  3978. this.infoCells.fps.innerHTML = currentFPS;
  3979. this.infoCells.renderWindow.innerHTML = `${renderDimensions.x} x ${renderDimensions.y}`;
  3980. this.infoCells.renderSplatCount.innerHTML =
  3981. `${splatRenderCount} splats out of ${splatCount} (${splatRenderCountPct.toFixed(2)}%)`;
  3982. this.infoCells.sortTime.innerHTML = `${lastSortTime.toFixed(3)} ms`;
  3983. this.infoCells.focalAdjustment.innerHTML = `${focalAdjustment.toFixed(3)}`;
  3984. this.infoCells.splatScale.innerHTML = `${splatScale.toFixed(3)}`;
  3985. this.infoCells.pointCloudMode.innerHTML = `${pointCloudMode}`;
  3986. };
  3987. setContainer(container) {
  3988. if (this.container) {
  3989. this.container.removeChild(this.infoPanelContainer);
  3990. }
  3991. if (container) {
  3992. this.container = container;
  3993. this.container.appendChild(this.infoPanelContainer);
  3994. this.infoPanelContainer.style.zIndex = this.container.style.zIndex + 1;
  3995. }
  3996. }
  3997. show() {
  3998. this.infoPanelContainer.style.display = 'block';
  3999. this.visible = true;
  4000. }
  4001. hide() {
  4002. this.infoPanelContainer.style.display = 'none';
  4003. this.visible = false;
  4004. }
  4005. }
  4006. class ArrowHelper extends THREE.Object3D {
  4007. constructor(dir = new THREE.Vector3(0, 0, 1), origin = new THREE.Vector3(0, 0, 0), length = 1,
  4008. radius = 0.1, color = 0xffff00, headLength = length * 0.2, headRadius = headLength * 0.2) {
  4009. super();
  4010. this.type = 'ArrowHelper';
  4011. const lineGeometry = new THREE.CylinderGeometry(radius, radius, length, 32);
  4012. lineGeometry.translate(0, length / 2.0, 0);
  4013. const coneGeometry = new THREE.CylinderGeometry( 0, headRadius, headLength, 32);
  4014. coneGeometry.translate(0, length, 0);
  4015. this.position.copy( origin );
  4016. this.line = new THREE.Mesh(lineGeometry, new THREE.MeshBasicMaterial({color: color, toneMapped: false}));
  4017. this.line.matrixAutoUpdate = false;
  4018. this.add(this.line);
  4019. this.cone = new THREE.Mesh(coneGeometry, new THREE.MeshBasicMaterial({color: color, toneMapped: false}));
  4020. this.cone.matrixAutoUpdate = false;
  4021. this.add(this.cone);
  4022. this.setDirection(dir);
  4023. }
  4024. setDirection( dir ) {
  4025. if (dir.y > 0.99999) {
  4026. this.quaternion.set(0, 0, 0, 1);
  4027. } else if (dir.y < - 0.99999) {
  4028. this.quaternion.set(1, 0, 0, 0);
  4029. } else {
  4030. _axis.set(dir.z, 0, -dir.x).normalize();
  4031. const radians = Math.acos(dir.y);
  4032. this.quaternion.setFromAxisAngle(_axis, radians);
  4033. }
  4034. }
  4035. setColor( color ) {
  4036. this.line.material.color.set(color);
  4037. this.cone.material.color.set(color);
  4038. }
  4039. copy(source) {
  4040. super.copy(source, false);
  4041. this.line.copy(source.line);
  4042. this.cone.copy(source.cone);
  4043. return this;
  4044. }
  4045. dispose() {
  4046. this.line.geometry.dispose();
  4047. this.line.material.dispose();
  4048. this.cone.geometry.dispose();
  4049. this.cone.material.dispose();
  4050. }
  4051. }
  4052. class SceneHelper {
  4053. constructor(threeScene) {
  4054. this.threeScene = threeScene;
  4055. this.splatRenderTarget = null;
  4056. this.renderTargetCopyQuad = null;
  4057. this.renderTargetCopyCamera = null;
  4058. this.meshCursor = null;
  4059. this.focusMarker = null;
  4060. this.controlPlane = null;
  4061. this.debugRoot = null;
  4062. this.secondaryDebugRoot = null;
  4063. }
  4064. updateSplatRenderTargetForRenderDimensions(width, height) {
  4065. this.destroySplatRendertarget();
  4066. this.splatRenderTarget = new THREE.WebGLRenderTarget(width, height, {
  4067. format: THREE.RGBAFormat,
  4068. stencilBuffer: false,
  4069. depthBuffer: true,
  4070. });
  4071. this.splatRenderTarget.depthTexture = new THREE.DepthTexture(width, height);
  4072. this.splatRenderTarget.depthTexture.format = THREE.DepthFormat;
  4073. this.splatRenderTarget.depthTexture.type = THREE.UnsignedIntType;
  4074. }
  4075. destroySplatRendertarget() {
  4076. if (this.splatRenderTarget) {
  4077. this.splatRenderTarget = null;
  4078. }
  4079. }
  4080. setupRenderTargetCopyObjects() {
  4081. const uniforms = {
  4082. 'sourceColorTexture': {
  4083. 'type': 't',
  4084. 'value': null
  4085. },
  4086. 'sourceDepthTexture': {
  4087. 'type': 't',
  4088. 'value': null
  4089. },
  4090. };
  4091. const renderTargetCopyMaterial = new THREE.ShaderMaterial({
  4092. vertexShader: `
  4093. varying vec2 vUv;
  4094. void main() {
  4095. vUv = uv;
  4096. gl_Position = vec4( position.xy, 0.0, 1.0 );
  4097. }
  4098. `,
  4099. fragmentShader: `
  4100. #include <common>
  4101. #include <packing>
  4102. varying vec2 vUv;
  4103. uniform sampler2D sourceColorTexture;
  4104. uniform sampler2D sourceDepthTexture;
  4105. void main() {
  4106. vec4 color = texture2D(sourceColorTexture, vUv);
  4107. float fragDepth = texture2D(sourceDepthTexture, vUv).x;
  4108. gl_FragDepth = fragDepth;
  4109. gl_FragColor = vec4(color.rgb, color.a * 2.0);
  4110. }
  4111. `,
  4112. uniforms: uniforms,
  4113. depthWrite: false,
  4114. depthTest: false,
  4115. transparent: true,
  4116. blending: THREE.CustomBlending,
  4117. blendSrc: THREE.SrcAlphaFactor,
  4118. blendSrcAlpha: THREE.SrcAlphaFactor,
  4119. blendDst: THREE.OneMinusSrcAlphaFactor,
  4120. blendDstAlpha: THREE.OneMinusSrcAlphaFactor
  4121. });
  4122. renderTargetCopyMaterial.extensions.fragDepth = true;
  4123. this.renderTargetCopyQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), renderTargetCopyMaterial);
  4124. this.renderTargetCopyCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
  4125. }
  4126. destroyRenderTargetCopyObjects() {
  4127. if (this.renderTargetCopyQuad) {
  4128. disposeAllMeshes(this.renderTargetCopyQuad);
  4129. this.renderTargetCopyQuad = null;
  4130. }
  4131. }
  4132. setupMeshCursor() {
  4133. if (!this.meshCursor) {
  4134. const coneGeometry = new THREE.ConeGeometry(0.5, 1.5, 32);
  4135. const coneMaterial = new THREE.MeshBasicMaterial({color: 0xFFFFFF});
  4136. const downArrow = new THREE.Mesh(coneGeometry, coneMaterial);
  4137. downArrow.rotation.set(0, 0, Math.PI);
  4138. downArrow.position.set(0, 1, 0);
  4139. const upArrow = new THREE.Mesh(coneGeometry, coneMaterial);
  4140. upArrow.position.set(0, -1, 0);
  4141. const leftArrow = new THREE.Mesh(coneGeometry, coneMaterial);
  4142. leftArrow.rotation.set(0, 0, Math.PI / 2.0);
  4143. leftArrow.position.set(1, 0, 0);
  4144. const rightArrow = new THREE.Mesh(coneGeometry, coneMaterial);
  4145. rightArrow.rotation.set(0, 0, -Math.PI / 2.0);
  4146. rightArrow.position.set(-1, 0, 0);
  4147. this.meshCursor = new THREE.Object3D();
  4148. this.meshCursor.add(downArrow);
  4149. this.meshCursor.add(upArrow);
  4150. this.meshCursor.add(leftArrow);
  4151. this.meshCursor.add(rightArrow);
  4152. this.meshCursor.scale.set(0.1, 0.1, 0.1);
  4153. this.threeScene.add(this.meshCursor);
  4154. this.meshCursor.visible = false;
  4155. }
  4156. }
  4157. destroyMeshCursor() {
  4158. if (this.meshCursor) {
  4159. disposeAllMeshes(this.meshCursor);
  4160. this.threeScene.remove(this.meshCursor);
  4161. this.meshCursor = null;
  4162. }
  4163. }
  4164. setMeshCursorVisibility(visible) {
  4165. this.meshCursor.visible = visible;
  4166. }
  4167. getMeschCursorVisibility() {
  4168. return this.meshCursor.visible;
  4169. }
  4170. setMeshCursorPosition(position) {
  4171. this.meshCursor.position.copy(position);
  4172. }
  4173. positionAndOrientMeshCursor(position, camera) {
  4174. this.meshCursor.position.copy(position);
  4175. this.meshCursor.up.copy(camera.up);
  4176. this.meshCursor.lookAt(camera.position);
  4177. }
  4178. setupFocusMarker() {
  4179. if (!this.focusMarker) {
  4180. const sphereGeometry = new THREE.SphereGeometry(.5, 32, 32);
  4181. const focusMarkerMaterial = SceneHelper.buildFocusMarkerMaterial();
  4182. focusMarkerMaterial.depthTest = false;
  4183. focusMarkerMaterial.depthWrite = false;
  4184. focusMarkerMaterial.transparent = true;
  4185. this.focusMarker = new THREE.Mesh(sphereGeometry, focusMarkerMaterial);
  4186. }
  4187. }
  4188. destroyFocusMarker() {
  4189. if (this.focusMarker) {
  4190. disposeAllMeshes(this.focusMarker);
  4191. this.focusMarker = null;
  4192. }
  4193. }
  4194. updateFocusMarker = function() {
  4195. const tempPosition = new THREE.Vector3();
  4196. const tempMatrix = new THREE.Matrix4();
  4197. const toCamera = new THREE.Vector3();
  4198. return function(position, camera, viewport) {
  4199. tempMatrix.copy(camera.matrixWorld).invert();
  4200. tempPosition.copy(position).applyMatrix4(tempMatrix);
  4201. tempPosition.normalize().multiplyScalar(10);
  4202. tempPosition.applyMatrix4(camera.matrixWorld);
  4203. toCamera.copy(camera.position).sub(position);
  4204. const toCameraDistance = toCamera.length();
  4205. this.focusMarker.position.copy(position);
  4206. this.focusMarker.scale.set(toCameraDistance, toCameraDistance, toCameraDistance);
  4207. this.focusMarker.material.uniforms.realFocusPosition.value.copy(position);
  4208. this.focusMarker.material.uniforms.viewport.value.copy(viewport);
  4209. this.focusMarker.material.uniformsNeedUpdate = true;
  4210. };
  4211. }();
  4212. setFocusMarkerVisibility(visible) {
  4213. this.focusMarker.visible = visible;
  4214. }
  4215. setFocusMarkerOpacity(opacity) {
  4216. this.focusMarker.material.uniforms.opacity.value = opacity;
  4217. this.focusMarker.material.uniformsNeedUpdate = true;
  4218. }
  4219. getFocusMarkerOpacity() {
  4220. return this.focusMarker.material.uniforms.opacity.value;
  4221. }
  4222. setupControlPlane() {
  4223. if (!this.controlPlane) {
  4224. const planeGeometry = new THREE.PlaneGeometry(1, 1);
  4225. planeGeometry.rotateX(-Math.PI / 2);
  4226. const planeMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});
  4227. planeMaterial.transparent = true;
  4228. planeMaterial.opacity = 0.6;
  4229. planeMaterial.depthTest = false;
  4230. planeMaterial.depthWrite = false;
  4231. planeMaterial.side = THREE.DoubleSide;
  4232. const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
  4233. const arrowDir = new THREE.Vector3(0, 1, 0);
  4234. arrowDir.normalize();
  4235. const arrowOrigin = new THREE.Vector3(0, 0, 0);
  4236. const arrowLength = 0.5;
  4237. const arrowRadius = 0.01;
  4238. const arrowColor = 0x00dd00;
  4239. const arrowHelper = new ArrowHelper(arrowDir, arrowOrigin, arrowLength, arrowRadius, arrowColor, 0.1, 0.03);
  4240. this.controlPlane = new THREE.Object3D();
  4241. this.controlPlane.add(planeMesh);
  4242. this.controlPlane.add(arrowHelper);
  4243. }
  4244. }
  4245. destroyControlPlane() {
  4246. if (this.controlPlane) {
  4247. disposeAllMeshes(this.controlPlane);
  4248. this.controlPlane = null;
  4249. }
  4250. }
  4251. setControlPlaneVisibility(visible) {
  4252. this.controlPlane.visible = visible;
  4253. }
  4254. positionAndOrientControlPlane = function() {
  4255. const tempQuaternion = new THREE.Quaternion();
  4256. const defaultUp = new THREE.Vector3(0, 1, 0);
  4257. return function(position, up) {
  4258. tempQuaternion.setFromUnitVectors(defaultUp, up);
  4259. this.controlPlane.position.copy(position);
  4260. this.controlPlane.quaternion.copy(tempQuaternion);
  4261. };
  4262. }();
  4263. addDebugMeshes() {
  4264. this.debugRoot = this.createDebugMeshes();
  4265. this.secondaryDebugRoot = this.createSecondaryDebugMeshes();
  4266. this.threeScene.add(this.debugRoot);
  4267. this.threeScene.add(this.secondaryDebugRoot);
  4268. }
  4269. destroyDebugMeshes() {
  4270. for (let debugRoot of [this.debugRoot, this.secondaryDebugRoot]) {
  4271. if (debugRoot) {
  4272. disposeAllMeshes(debugRoot);
  4273. this.threeScene.remove(debugRoot);
  4274. }
  4275. }
  4276. this.debugRoot = null;
  4277. this.secondaryDebugRoot = null;
  4278. }
  4279. createDebugMeshes(renderOrder) {
  4280. const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
  4281. const debugMeshRoot = new THREE.Object3D();
  4282. const createMesh = (color, position) => {
  4283. let sphereMesh = new THREE.Mesh(sphereGeometry, SceneHelper.buildDebugMaterial(color));
  4284. sphereMesh.renderOrder = renderOrder;
  4285. debugMeshRoot.add(sphereMesh);
  4286. sphereMesh.position.fromArray(position);
  4287. };
  4288. createMesh(0xff0000, [-50, 0, 0]);
  4289. createMesh(0xff0000, [50, 0, 0]);
  4290. createMesh(0x00ff00, [0, 0, -50]);
  4291. createMesh(0x00ff00, [0, 0, 50]);
  4292. createMesh(0xffaa00, [5, 0, 5]);
  4293. return debugMeshRoot;
  4294. }
  4295. createSecondaryDebugMeshes(renderOrder) {
  4296. const boxGeometry = new THREE.BoxGeometry(3, 3, 3);
  4297. const debugMeshRoot = new THREE.Object3D();
  4298. let boxColor = 0xBBBBBB;
  4299. const createMesh = (position) => {
  4300. let boxMesh = new THREE.Mesh(boxGeometry, SceneHelper.buildDebugMaterial(boxColor));
  4301. boxMesh.renderOrder = renderOrder;
  4302. debugMeshRoot.add(boxMesh);
  4303. boxMesh.position.fromArray(position);
  4304. };
  4305. let separation = 10;
  4306. createMesh([-separation, 0, -separation]);
  4307. createMesh([-separation, 0, separation]);
  4308. createMesh([separation, 0, -separation]);
  4309. createMesh([separation, 0, separation]);
  4310. return debugMeshRoot;
  4311. }
  4312. static buildDebugMaterial(color) {
  4313. const vertexShaderSource = `
  4314. #include <common>
  4315. varying float ndcDepth;
  4316. void main() {
  4317. gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position.xyz, 1.0);
  4318. ndcDepth = gl_Position.z / gl_Position.w;
  4319. gl_Position.x = gl_Position.x / gl_Position.w;
  4320. gl_Position.y = gl_Position.y / gl_Position.w;
  4321. gl_Position.z = 0.0;
  4322. gl_Position.w = 1.0;
  4323. }
  4324. `;
  4325. const fragmentShaderSource = `
  4326. #include <common>
  4327. uniform vec3 color;
  4328. varying float ndcDepth;
  4329. void main() {
  4330. gl_FragDepth = (ndcDepth + 1.0) / 2.0;
  4331. gl_FragColor = vec4(color.rgb, 0.0);
  4332. }
  4333. `;
  4334. const uniforms = {
  4335. 'color': {
  4336. 'type': 'v3',
  4337. 'value': new THREE.Color(color)
  4338. },
  4339. };
  4340. const material = new THREE.ShaderMaterial({
  4341. uniforms: uniforms,
  4342. vertexShader: vertexShaderSource,
  4343. fragmentShader: fragmentShaderSource,
  4344. transparent: false,
  4345. depthTest: true,
  4346. depthWrite: true,
  4347. side: THREE.FrontSide
  4348. });
  4349. material.extensions.fragDepth = true;
  4350. return material;
  4351. }
  4352. static buildFocusMarkerMaterial(color) {
  4353. const vertexShaderSource = `
  4354. #include <common>
  4355. uniform vec2 viewport;
  4356. uniform vec3 realFocusPosition;
  4357. varying vec4 ndcPosition;
  4358. varying vec4 ndcCenter;
  4359. varying vec4 ndcFocusPosition;
  4360. void main() {
  4361. float radius = 0.01;
  4362. vec4 viewPosition = modelViewMatrix * vec4(position.xyz, 1.0);
  4363. vec4 viewCenter = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);
  4364. vec4 viewFocusPosition = modelViewMatrix * vec4(realFocusPosition, 1.0);
  4365. ndcPosition = projectionMatrix * viewPosition;
  4366. ndcPosition = ndcPosition * vec4(1.0 / ndcPosition.w);
  4367. ndcCenter = projectionMatrix * viewCenter;
  4368. ndcCenter = ndcCenter * vec4(1.0 / ndcCenter.w);
  4369. ndcFocusPosition = projectionMatrix * viewFocusPosition;
  4370. ndcFocusPosition = ndcFocusPosition * vec4(1.0 / ndcFocusPosition.w);
  4371. gl_Position = projectionMatrix * viewPosition;
  4372. }
  4373. `;
  4374. const fragmentShaderSource = `
  4375. #include <common>
  4376. uniform vec3 color;
  4377. uniform vec2 viewport;
  4378. uniform float opacity;
  4379. varying vec4 ndcPosition;
  4380. varying vec4 ndcCenter;
  4381. varying vec4 ndcFocusPosition;
  4382. void main() {
  4383. vec2 screenPosition = vec2(ndcPosition) * viewport;
  4384. vec2 screenCenter = vec2(ndcCenter) * viewport;
  4385. vec2 screenVec = screenPosition - screenCenter;
  4386. float projectedRadius = length(screenVec);
  4387. float lineWidth = 0.0005 * viewport.y;
  4388. float aaRange = 0.0025 * viewport.y;
  4389. float radius = 0.06 * viewport.y;
  4390. float radDiff = abs(projectedRadius - radius) - lineWidth;
  4391. float alpha = 1.0 - clamp(radDiff / 5.0, 0.0, 1.0);
  4392. gl_FragColor = vec4(color.rgb, alpha * opacity);
  4393. }
  4394. `;
  4395. const uniforms = {
  4396. 'color': {
  4397. 'type': 'v3',
  4398. 'value': new THREE.Color(color)
  4399. },
  4400. 'realFocusPosition': {
  4401. 'type': 'v3',
  4402. 'value': new THREE.Vector3()
  4403. },
  4404. 'viewport': {
  4405. 'type': 'v2',
  4406. 'value': new THREE.Vector2()
  4407. },
  4408. 'opacity': {
  4409. 'value': 0.0
  4410. }
  4411. };
  4412. const material = new THREE.ShaderMaterial({
  4413. uniforms: uniforms,
  4414. vertexShader: vertexShaderSource,
  4415. fragmentShader: fragmentShaderSource,
  4416. transparent: true,
  4417. depthTest: false,
  4418. depthWrite: false,
  4419. side: THREE.FrontSide
  4420. });
  4421. return material;
  4422. }
  4423. dispose() {
  4424. this.destroyMeshCursor();
  4425. this.destroyFocusMarker();
  4426. this.destroyDebugMeshes();
  4427. this.destroyControlPlane();
  4428. this.destroyRenderTargetCopyObjects();
  4429. this.destroySplatRendertarget();
  4430. }
  4431. }
  4432. const VectorRight = new THREE.Vector3(1, 0, 0);
  4433. const VectorUp = new THREE.Vector3(0, 1, 0);
  4434. const VectorBackward = new THREE.Vector3(0, 0, 1);
  4435. class Ray {
  4436. constructor(origin = new THREE.Vector3(), direction = new THREE.Vector3()) {
  4437. this.origin = new THREE.Vector3();
  4438. this.direction = new THREE.Vector3();
  4439. this.setParameters(origin, direction);
  4440. }
  4441. setParameters(origin, direction) {
  4442. this.origin.copy(origin);
  4443. this.direction.copy(direction).normalize();
  4444. }
  4445. boxContainsPoint(box, point, epsilon) {
  4446. return point.x < box.min.x - epsilon || point.x > box.max.x + epsilon ||
  4447. point.y < box.min.y - epsilon || point.y > box.max.y + epsilon ||
  4448. point.z < box.min.z - epsilon || point.z > box.max.z + epsilon ? false : true;
  4449. }
  4450. intersectBox = function() {
  4451. const planeIntersectionPoint = new THREE.Vector3();
  4452. const planeIntersectionPointArray = [];
  4453. const originArray = [];
  4454. const directionArray = [];
  4455. return function(box, outHit) {
  4456. originArray[0] = this.origin.x;
  4457. originArray[1] = this.origin.y;
  4458. originArray[2] = this.origin.z;
  4459. directionArray[0] = this.direction.x;
  4460. directionArray[1] = this.direction.y;
  4461. directionArray[2] = this.direction.z;
  4462. if (this.boxContainsPoint(box, this.origin, 0.0001)) {
  4463. if (outHit) {
  4464. outHit.origin.copy(this.origin);
  4465. outHit.normal.set(0, 0, 0);
  4466. outHit.distance = -1;
  4467. }
  4468. return true;
  4469. }
  4470. for (let i = 0; i < 3; i++) {
  4471. if (directionArray[i] == 0.0) continue;
  4472. const hitNormal = i == 0 ? VectorRight : i == 1 ? VectorUp : VectorBackward;
  4473. const extremeVec = directionArray[i] < 0 ? box.max : box.min;
  4474. let multiplier = -Math.sign(directionArray[i]);
  4475. planeIntersectionPointArray[0] = i == 0 ? extremeVec.x : i == 1 ? extremeVec.y : extremeVec.z;
  4476. let toSide = planeIntersectionPointArray[0] - originArray[i];
  4477. if (toSide * multiplier < 0) {
  4478. const idx1 = (i + 1) % 3;
  4479. const idx2 = (i + 2) % 3;
  4480. planeIntersectionPointArray[2] = directionArray[idx1] / directionArray[i] * toSide + originArray[idx1];
  4481. planeIntersectionPointArray[1] = directionArray[idx2] / directionArray[i] * toSide + originArray[idx2];
  4482. planeIntersectionPoint.set(planeIntersectionPointArray[i],
  4483. planeIntersectionPointArray[idx2],
  4484. planeIntersectionPointArray[idx1]);
  4485. if (this.boxContainsPoint(box, planeIntersectionPoint, 0.0001)) {
  4486. if (outHit) {
  4487. outHit.origin.copy(planeIntersectionPoint);
  4488. outHit.normal.copy(hitNormal).multiplyScalar(multiplier);
  4489. outHit.distance = planeIntersectionPoint.sub(this.origin).length();
  4490. }
  4491. return true;
  4492. }
  4493. }
  4494. }
  4495. return false;
  4496. };
  4497. }();
  4498. intersectSphere = function() {
  4499. const toSphereCenterVec = new THREE.Vector3();
  4500. return function(center, radius, outHit) {
  4501. toSphereCenterVec.copy(center).sub(this.origin);
  4502. const toClosestApproach = toSphereCenterVec.dot(this.direction);
  4503. const toClosestApproachSq = toClosestApproach * toClosestApproach;
  4504. const toSphereCenterSq = toSphereCenterVec.dot(toSphereCenterVec);
  4505. const diffSq = toSphereCenterSq - toClosestApproachSq;
  4506. const radiusSq = radius * radius;
  4507. if (diffSq > radiusSq) return false;
  4508. const thc = Math.sqrt(radiusSq - diffSq);
  4509. const t0 = toClosestApproach - thc;
  4510. const t1 = toClosestApproach + thc;
  4511. if (t1 < 0) return false;
  4512. let t = t0 < 0 ? t1 : t0;
  4513. if (outHit) {
  4514. outHit.origin.copy(this.origin).addScaledVector(this.direction, t);
  4515. outHit.normal.copy(outHit.origin).sub(center).normalize();
  4516. outHit.distance = t;
  4517. }
  4518. return true;
  4519. };
  4520. }();
  4521. }
  4522. class Hit {
  4523. constructor() {
  4524. this.origin = new THREE.Vector3();
  4525. this.normal = new THREE.Vector3();
  4526. this.distance = 0;
  4527. this.splatIndex = 0;
  4528. }
  4529. set(origin, normal, distance, splatIndex) {
  4530. this.origin.copy(origin);
  4531. this.normal.copy(normal);
  4532. this.distance = distance;
  4533. this.splatIndex = splatIndex;
  4534. }
  4535. clone() {
  4536. const hitClone = new Hit();
  4537. hitClone.origin.copy(this.origin);
  4538. hitClone.normal.copy(this.normal);
  4539. hitClone.distance = this.distance;
  4540. hitClone.splatIndex = this.splatIndex;
  4541. return hitClone;
  4542. }
  4543. }
  4544. class Raycaster {
  4545. constructor(origin, direction, raycastAgainstTrueSplatEllipsoid = false) {
  4546. this.ray = new Ray(origin, direction);
  4547. this.raycastAgainstTrueSplatEllipsoid = raycastAgainstTrueSplatEllipsoid;
  4548. }
  4549. setFromCameraAndScreenPosition = function() {
  4550. const ndcCoords = new THREE.Vector2();
  4551. return function(camera, screenPosition, screenDimensions) {
  4552. ndcCoords.x = screenPosition.x / screenDimensions.x * 2.0 - 1.0;
  4553. ndcCoords.y = (screenDimensions.y - screenPosition.y) / screenDimensions.y * 2.0 - 1.0;
  4554. if (camera.isPerspectiveCamera) {
  4555. this.ray.origin.setFromMatrixPosition(camera.matrixWorld);
  4556. this.ray.direction.set(ndcCoords.x, ndcCoords.y, 0.5 ).unproject(camera).sub(this.ray.origin).normalize();
  4557. this.camera = camera;
  4558. } else if (camera.isOrthographicCamera) {
  4559. this.ray.origin.set(ndcCoords.x, ndcCoords.y,
  4560. (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera);
  4561. this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld);
  4562. this.camera = camera;
  4563. } else {
  4564. throw new Error('Raycaster::setFromCameraAndScreenPosition() -> Unsupported camera type');
  4565. }
  4566. };
  4567. }();
  4568. intersectSplatMesh = function() {
  4569. const toLocal = new THREE.Matrix4();
  4570. const fromLocal = new THREE.Matrix4();
  4571. const sceneTransform = new THREE.Matrix4();
  4572. const localRay = new Ray();
  4573. const tempPoint = new THREE.Vector3();
  4574. return function(splatMesh, outHits = []) {
  4575. const splatTree = splatMesh.getSplatTree();
  4576. if (!splatTree) return;
  4577. for (let s = 0; s < splatTree.subTrees.length; s++) {
  4578. const subTree = splatTree.subTrees[s];
  4579. fromLocal.copy(splatMesh.matrixWorld);
  4580. if (splatMesh.dynamicMode) {
  4581. splatMesh.getSceneTransform(s, sceneTransform);
  4582. fromLocal.multiply(sceneTransform);
  4583. }
  4584. toLocal.copy(fromLocal).invert();
  4585. //直接在ray上乘上模型变换
  4586. localRay.origin.copy(this.ray.origin).applyMatrix4(toLocal);
  4587. localRay.direction.copy(this.ray.origin).add(this.ray.direction);
  4588. localRay.direction.applyMatrix4(toLocal).sub(localRay.origin).normalize();
  4589. const outHitsForSubTree = [];
  4590. if (subTree.rootNode) {
  4591. this.castRayAtSplatTreeNode(localRay, splatTree, subTree.rootNode, outHitsForSubTree);
  4592. }
  4593. outHitsForSubTree.forEach((hit) => {
  4594. hit.origin.applyMatrix4(fromLocal);
  4595. hit.normal.applyMatrix4(fromLocal).normalize();
  4596. hit.distance = tempPoint.copy(hit.origin).sub(this.ray.origin).length();
  4597. });
  4598. outHits.push(...outHitsForSubTree);
  4599. }
  4600. outHits.sort((a, b) => {
  4601. if (a.distance > b.distance) return 1;
  4602. else return -1;
  4603. });
  4604. return outHits;
  4605. };
  4606. }();
  4607. castRayAtSplatTreeNode = function() {
  4608. const tempColor = new THREE.Vector4();
  4609. const tempCenter = new THREE.Vector3();
  4610. const tempScale = new THREE.Vector3();
  4611. const tempRotation = new THREE.Quaternion();
  4612. const tempHit = new Hit();
  4613. const scaleEpsilon = 0.0000001;
  4614. const origin = new THREE.Vector3(0, 0, 0);
  4615. const uniformScaleMatrix = new THREE.Matrix4();
  4616. const scaleMatrix = new THREE.Matrix4();
  4617. const rotationMatrix = new THREE.Matrix4();
  4618. const toSphereSpace = new THREE.Matrix4();
  4619. const fromSphereSpace = new THREE.Matrix4();
  4620. const tempRay = new Ray();
  4621. return function(ray, splatTree, node, outHits = []) {
  4622. if (!ray.intersectBox(node.boundingBox)) {
  4623. return;
  4624. }
  4625. if (node.data && node.data.indexes && node.data.indexes.length > 0) {
  4626. for (let i = 0; i < node.data.indexes.length; i++) {
  4627. const splatGlobalIndex = node.data.indexes[i];
  4628. splatTree.splatMesh.getSplatColor(splatGlobalIndex, tempColor);
  4629. splatTree.splatMesh.getSplatCenter(splatGlobalIndex, tempCenter);
  4630. splatTree.splatMesh.getSplatScaleAndRotation(splatGlobalIndex, tempScale, tempRotation);
  4631. if (tempScale.x <= scaleEpsilon || tempScale.y <= scaleEpsilon || tempScale.z <= scaleEpsilon) {
  4632. continue;
  4633. }
  4634. if (!this.raycastAgainstTrueSplatEllipsoid) {//是只需得到近似值的意思吗
  4635. const radius = (tempScale.x + tempScale.y + tempScale.z) / 3; //椭球的平均半径
  4636. if (ray.intersectSphere(tempCenter, radius, tempHit)) { //没细看,是pick球上哪个点?
  4637. const hitClone = tempHit.clone();
  4638. hitClone.splatIndex = splatGlobalIndex;
  4639. outHits.push(hitClone);
  4640. }
  4641. } else {
  4642. scaleMatrix.makeScale(tempScale.x, tempScale.y, tempScale.z);
  4643. rotationMatrix.makeRotationFromQuaternion(tempRotation);
  4644. const uniformScale = Math.log10(tempColor.w) * 2.0;
  4645. uniformScaleMatrix.makeScale(uniformScale, uniformScale, uniformScale);
  4646. fromSphereSpace.copy(uniformScaleMatrix).multiply(rotationMatrix).multiply(scaleMatrix);
  4647. toSphereSpace.copy(fromSphereSpace).invert();
  4648. tempRay.origin.copy(ray.origin).sub(tempCenter).applyMatrix4(toSphereSpace);
  4649. tempRay.direction.copy(ray.origin).add(ray.direction).sub(tempCenter);
  4650. tempRay.direction.applyMatrix4(toSphereSpace).sub(tempRay.origin).normalize();
  4651. if (tempRay.intersectSphere(origin, 1.0, tempHit)) {
  4652. const hitClone = tempHit.clone();
  4653. hitClone.splatIndex = splatGlobalIndex;
  4654. hitClone.origin.applyMatrix4(fromSphereSpace).add(tempCenter);
  4655. outHits.push(hitClone);
  4656. }
  4657. }
  4658. }
  4659. }
  4660. if (node.children && node.children.length > 0) {
  4661. for (let child of node.children) {
  4662. this.castRayAtSplatTreeNode(ray, splatTree, child, outHits);
  4663. }
  4664. }
  4665. return outHits;
  4666. };
  4667. }();
  4668. }
  4669. /**
  4670. * SplatScene: Descriptor for a single splat scene managed by an instance of SplatMesh.
  4671. */
  4672. class SplatScene {
  4673. constructor(splatBuffer, position = new THREE.Vector3(), quaternion = new THREE.Quaternion(),
  4674. scale = new THREE.Vector3(1, 1, 1), minimumAlpha = 1) {
  4675. this.splatBuffer = splatBuffer;
  4676. this.position = position.clone();
  4677. this.quaternion = quaternion.clone();
  4678. this.scale = scale.clone();
  4679. this.transform = new THREE.Matrix4();
  4680. this.minimumAlpha = minimumAlpha;
  4681. this.updateTransform();
  4682. }
  4683. copyTransformData(otherScene) {
  4684. this.position.copy(otherScene.position);
  4685. this.quaternion.copy(otherScene.quaternion);
  4686. this.scale.copy(otherScene.scale);
  4687. this.transform.copy(otherScene.transform);
  4688. }
  4689. updateTransform() {
  4690. this.transform.compose(this.position, this.quaternion, this.scale);
  4691. }
  4692. }
  4693. /*
  4694. 将点云中的点按照八叉树的方式一层层等分存储。
  4695. 每个子节点自动向下分割,直到层数到达最大,或者其点数量小于最大容纳点数为止。
  4696. */
  4697. class SplatTreeNode {
  4698. static idGen = 0;
  4699. constructor(min, max, depth, id) {
  4700. this.min = new THREE.Vector3().copy(min);
  4701. this.max = new THREE.Vector3().copy(max);
  4702. this.boundingBox = new THREE.Box3(this.min, this.max);
  4703. this.center = new THREE.Vector3().copy(this.max).sub(this.min).multiplyScalar(0.5).add(this.min);
  4704. this.depth = depth;
  4705. this.children = [];
  4706. this.data = null;
  4707. this.id = id || SplatTreeNode.idGen++;
  4708. }
  4709. }
  4710. class SplatSubTree {
  4711. constructor(maxDepth, maxCentersPerNode) {
  4712. this.maxDepth = maxDepth;
  4713. this.maxCentersPerNode = maxCentersPerNode;
  4714. this.sceneDimensions = new THREE.Vector3();
  4715. this.sceneMin = new THREE.Vector3();
  4716. this.sceneMax = new THREE.Vector3();
  4717. this.rootNode = null;
  4718. this.nodesWithIndexes = [];
  4719. this.splatMesh = null;
  4720. }
  4721. static convertWorkerSubTreeNode(workerSubTreeNode) {
  4722. const minVector = new THREE.Vector3().fromArray(workerSubTreeNode.min);
  4723. const maxVector = new THREE.Vector3().fromArray(workerSubTreeNode.max);
  4724. const convertedNode = new SplatTreeNode(minVector, maxVector, workerSubTreeNode.depth, workerSubTreeNode.id);
  4725. if (workerSubTreeNode.data.indexes) {
  4726. convertedNode.data = {
  4727. 'indexes': []
  4728. };
  4729. for (let index of workerSubTreeNode.data.indexes) {
  4730. convertedNode.data.indexes.push(index);
  4731. }
  4732. }
  4733. if (workerSubTreeNode.children) {
  4734. for (let child of workerSubTreeNode.children) {
  4735. convertedNode.children.push(SplatSubTree.convertWorkerSubTreeNode(child));
  4736. }
  4737. }
  4738. return convertedNode;
  4739. }
  4740. static convertWorkerSubTree(workerSubTree, splatMesh) {
  4741. const convertedSubTree = new SplatSubTree(workerSubTree.maxDepth, workerSubTree.maxCentersPerNode);
  4742. convertedSubTree.sceneMin = new THREE.Vector3().fromArray(workerSubTree.sceneMin);
  4743. convertedSubTree.sceneMax = new THREE.Vector3().fromArray(workerSubTree.sceneMax);
  4744. convertedSubTree.splatMesh = splatMesh;
  4745. convertedSubTree.rootNode = SplatSubTree.convertWorkerSubTreeNode(workerSubTree.rootNode);
  4746. const visitLeavesFromNode = (node, visitFunc) => {
  4747. if (node.children.length === 0) visitFunc(node);
  4748. for (let child of node.children) {
  4749. visitLeavesFromNode(child, visitFunc);
  4750. }
  4751. };
  4752. convertedSubTree.nodesWithIndexes = [];
  4753. visitLeavesFromNode(convertedSubTree.rootNode, (node) => {
  4754. if (node.data && node.data.indexes && node.data.indexes.length > 0) {
  4755. convertedSubTree.nodesWithIndexes.push(node);
  4756. }
  4757. });
  4758. return convertedSubTree;
  4759. }
  4760. }
  4761. function createSplatTreeWorker(self) {
  4762. let WorkerSplatTreeNodeIDGen = 0;
  4763. class WorkerBox3 {
  4764. constructor(min, max) {
  4765. this.min = [min[0], min[1], min[2]];
  4766. this.max = [max[0], max[1], max[2]];
  4767. }
  4768. containsPoint(point) {
  4769. return point[0] >= this.min[0] && point[0] <= this.max[0] &&
  4770. point[1] >= this.min[1] && point[1] <= this.max[1] &&
  4771. point[2] >= this.min[2] && point[2] <= this.max[2];
  4772. }
  4773. }
  4774. class WorkerSplatSubTree {
  4775. constructor(maxDepth, maxCentersPerNode) {
  4776. this.maxDepth = maxDepth;
  4777. this.maxCentersPerNode = maxCentersPerNode;
  4778. this.sceneDimensions = [];
  4779. this.sceneMin = [];
  4780. this.sceneMax = [];
  4781. this.rootNode = null;
  4782. this.addedIndexes = {};
  4783. this.nodesWithIndexes = [];
  4784. this.splatMesh = null;
  4785. this.disposed = false;
  4786. }
  4787. }
  4788. class WorkerSplatTreeNode {
  4789. constructor(min, max, depth, id) {
  4790. this.min = [min[0], min[1], min[2]];
  4791. this.max = [max[0], max[1], max[2]];
  4792. this.center = [(max[0] - min[0]) * 0.5 + min[0],
  4793. (max[1] - min[1]) * 0.5 + min[1],
  4794. (max[2] - min[2]) * 0.5 + min[2]];
  4795. this.depth = depth;
  4796. this.children = [];
  4797. this.data = null;
  4798. this.id = id || WorkerSplatTreeNodeIDGen++;
  4799. }
  4800. }
  4801. processSplatTreeNode = function(tree, node, indexToCenter, sceneCenters) {
  4802. const splatCount = node.data.indexes.length;
  4803. if (splatCount < tree.maxCentersPerNode || node.depth > tree.maxDepth) {//该节点停止分割
  4804. const newIndexes = [];
  4805. for (let i = 0; i < node.data.indexes.length; i++) {
  4806. if (!tree.addedIndexes[node.data.indexes[i]]) {
  4807. newIndexes.push(node.data.indexes[i]);
  4808. tree.addedIndexes[node.data.indexes[i]] = true;
  4809. }
  4810. }
  4811. node.data.indexes = newIndexes;
  4812. node.data.indexes.sort((a, b) => {
  4813. if (a > b) return 1;
  4814. else return -1;
  4815. });
  4816. tree.nodesWithIndexes.push(node);
  4817. return;
  4818. }
  4819. const nodeDimensions = [node.max[0] - node.min[0],
  4820. node.max[1] - node.min[1],
  4821. node.max[2] - node.min[2]];
  4822. const halfDimensions = [nodeDimensions[0] * 0.5,
  4823. nodeDimensions[1] * 0.5,
  4824. nodeDimensions[2] * 0.5];
  4825. const nodeCenter = [node.min[0] + halfDimensions[0],
  4826. node.min[1] + halfDimensions[1],
  4827. node.min[2] + halfDimensions[2]];
  4828. const childrenBounds = [ //分割成八块
  4829. // top section, clockwise from upper-left (looking from above, +Y)
  4830. new WorkerBox3([nodeCenter[0] - halfDimensions[0], nodeCenter[1], nodeCenter[2] - halfDimensions[2]],
  4831. [nodeCenter[0], nodeCenter[1] + halfDimensions[1], nodeCenter[2]]),
  4832. new WorkerBox3([nodeCenter[0], nodeCenter[1], nodeCenter[2] - halfDimensions[2]],
  4833. [nodeCenter[0] + halfDimensions[0], nodeCenter[1] + halfDimensions[1], nodeCenter[2]]),
  4834. new WorkerBox3([nodeCenter[0], nodeCenter[1], nodeCenter[2]],
  4835. [nodeCenter[0] + halfDimensions[0], nodeCenter[1] + halfDimensions[1], nodeCenter[2] + halfDimensions[2]]),
  4836. new WorkerBox3([nodeCenter[0] - halfDimensions[0], nodeCenter[1], nodeCenter[2]],
  4837. [nodeCenter[0], nodeCenter[1] + halfDimensions[1], nodeCenter[2] + halfDimensions[2]]),
  4838. // bottom section, clockwise from lower-left (looking from above, +Y)
  4839. new WorkerBox3([nodeCenter[0] - halfDimensions[0], nodeCenter[1] - halfDimensions[1], nodeCenter[2] - halfDimensions[2]],
  4840. [nodeCenter[0], nodeCenter[1], nodeCenter[2]]),
  4841. new WorkerBox3([nodeCenter[0], nodeCenter[1] - halfDimensions[1], nodeCenter[2] - halfDimensions[2]],
  4842. [nodeCenter[0] + halfDimensions[0], nodeCenter[1], nodeCenter[2]]),
  4843. new WorkerBox3([nodeCenter[0], nodeCenter[1] - halfDimensions[1], nodeCenter[2]],
  4844. [nodeCenter[0] + halfDimensions[0], nodeCenter[1], nodeCenter[2] + halfDimensions[2]]),
  4845. new WorkerBox3([nodeCenter[0] - halfDimensions[0], nodeCenter[1] - halfDimensions[1], nodeCenter[2]],
  4846. [nodeCenter[0], nodeCenter[1], nodeCenter[2] + halfDimensions[2]]),
  4847. ];
  4848. const splatCounts = [];
  4849. const baseIndexes = [];
  4850. for (let i = 0; i < childrenBounds.length; i++) {
  4851. splatCounts[i] = 0;
  4852. baseIndexes[i] = [];
  4853. }
  4854. const center = [0, 0, 0];
  4855. for (let i = 0; i < splatCount; i++) {//查找每一块包含的index
  4856. const splatGlobalIndex = node.data.indexes[i];
  4857. const centerBase = indexToCenter[splatGlobalIndex];
  4858. center[0] = sceneCenters[centerBase];
  4859. center[1] = sceneCenters[centerBase + 1];
  4860. center[2] = sceneCenters[centerBase + 2];
  4861. for (let j = 0; j < childrenBounds.length; j++) {
  4862. if (childrenBounds[j].containsPoint(center)) {
  4863. splatCounts[j]++;
  4864. baseIndexes[j].push(splatGlobalIndex);
  4865. break //xzw!!!! add 防止ply转的如果都是000点,就会每块都有然后不停分割下去
  4866. }
  4867. }
  4868. }
  4869. for (let i = 0; i < childrenBounds.length; i++) {
  4870. const childNode = new WorkerSplatTreeNode(childrenBounds[i].min, childrenBounds[i].max, node.depth + 1);//创建子节点
  4871. childNode.data = {
  4872. 'indexes': baseIndexes[i]
  4873. };
  4874. node.children.push(childNode);
  4875. }
  4876. node.data = {};//清空自身index
  4877. for (let child of node.children) {
  4878. processSplatTreeNode(tree, child, indexToCenter, sceneCenters); //继续分割
  4879. }
  4880. return;
  4881. };
  4882. const buildSubTree = (sceneCenters, maxDepth, maxCentersPerNode) => {
  4883. const sceneMin = [0, 0, 0];
  4884. const sceneMax = [0, 0, 0];
  4885. const indexes = [];
  4886. const centerCount = Math.floor(sceneCenters.length / 4);
  4887. for ( let i = 0; i < centerCount; i ++) {
  4888. const base = i * 4;
  4889. const x = sceneCenters[base];
  4890. const y = sceneCenters[base + 1];
  4891. const z = sceneCenters[base + 2];
  4892. const index = Math.round(sceneCenters[base + 3]);
  4893. if (i === 0 || x < sceneMin[0]) sceneMin[0] = x;
  4894. if (i === 0 || x > sceneMax[0]) sceneMax[0] = x;
  4895. if (i === 0 || y < sceneMin[1]) sceneMin[1] = y;
  4896. if (i === 0 || y > sceneMax[1]) sceneMax[1] = y;
  4897. if (i === 0 || z < sceneMin[2]) sceneMin[2] = z;
  4898. if (i === 0 || z > sceneMax[2]) sceneMax[2] = z;
  4899. indexes.push(index);
  4900. }
  4901. const subTree = new WorkerSplatSubTree(maxDepth, maxCentersPerNode);
  4902. subTree.sceneMin = sceneMin;
  4903. subTree.sceneMax = sceneMax;
  4904. subTree.rootNode = new WorkerSplatTreeNode(subTree.sceneMin, subTree.sceneMax, 0);
  4905. subTree.rootNode.data = {
  4906. 'indexes': indexes
  4907. };
  4908. return subTree;
  4909. };
  4910. function createSplatTree(allCenters, maxDepth, maxCentersPerNode) {
  4911. const indexToCenter = [];
  4912. for (let sceneCenters of allCenters) {
  4913. const centerCount = Math.floor(sceneCenters.length / 4);
  4914. for ( let i = 0; i < centerCount; i ++) {
  4915. const base = i * 4;
  4916. const index = Math.round(sceneCenters[base + 3]);
  4917. indexToCenter[index] = base;
  4918. }
  4919. }
  4920. const subTrees = [];
  4921. for (let sceneCenters of allCenters) {
  4922. const subTree = buildSubTree(sceneCenters, maxDepth, maxCentersPerNode);//建立树
  4923. subTrees.push(subTree);
  4924. processSplatTreeNode(subTree, subTree.rootNode, indexToCenter, sceneCenters);//开始分割
  4925. }
  4926. self.postMessage({
  4927. 'subTrees': subTrees
  4928. });
  4929. }
  4930. self.onmessage = (e) => {
  4931. if (e.data.process) {
  4932. createSplatTree(e.data.process.centers, e.data.process.maxDepth, e.data.process.maxCentersPerNode);
  4933. }
  4934. };
  4935. }
  4936. function workerProcessCenters(splatTreeWorker, centers, transferBuffers, maxDepth, maxCentersPerNode) {
  4937. splatTreeWorker.postMessage({
  4938. 'process': {
  4939. 'centers': centers,
  4940. 'maxDepth': maxDepth,
  4941. 'maxCentersPerNode': maxCentersPerNode
  4942. }
  4943. }, transferBuffers);
  4944. }
  4945. function checkAndCreateWorker() {
  4946. const splatTreeWorker = new Worker(
  4947. URL.createObjectURL(
  4948. new Blob(['(', createSplatTreeWorker.toString(), ')(self)'], {
  4949. type: 'application/javascript',
  4950. }),
  4951. ),
  4952. );
  4953. return splatTreeWorker;
  4954. }
  4955. /**
  4956. * SplatTree: Octree tailored to splat data from a SplatMesh instance
  4957. */
  4958. class SplatTree {
  4959. constructor(maxDepth, maxCentersPerNode) {
  4960. this.maxDepth = maxDepth;
  4961. this.maxCentersPerNode = maxCentersPerNode;
  4962. this.subTrees = [];
  4963. this.splatMesh = null;
  4964. }
  4965. dispose() {
  4966. this.diposeSplatTreeWorker();
  4967. this.disposed = true;
  4968. }
  4969. diposeSplatTreeWorker() {
  4970. if (this.splatTreeWorker) this.splatTreeWorker.terminate();
  4971. this.splatTreeWorker = null;
  4972. };
  4973. /**
  4974. * Construct this instance of SplatTree from an instance of SplatMesh.
  4975. *
  4976. * @param {SplatMesh} splatMesh The instance of SplatMesh from which to construct this splat tree.
  4977. * @param {function} filterFunc Optional function to filter out unwanted splats.
  4978. * @param {function} onIndexesUpload Function to be called when the upload of splat centers to the splat tree
  4979. * builder worker starts and finishes.
  4980. * @param {function} onSplatTreeConstruction Function to be called when the conversion of the local splat tree from
  4981. * the format produced by the splat tree builder worker starts and ends.
  4982. * @return {undefined}
  4983. */
  4984. processSplatMesh = function(splatMesh, filterFunc = () => true, onIndexesUpload, onSplatTreeConstruction) {
  4985. if (!this.splatTreeWorker) this.splatTreeWorker = checkAndCreateWorker();
  4986. this.splatMesh = splatMesh;
  4987. this.subTrees = [];
  4988. const center = new THREE.Vector3();
  4989. const addCentersForScene = (splatOffset, splatCount) => {
  4990. const sceneCenters = new Float32Array(splatCount * 4);
  4991. let addedCount = 0;
  4992. for (let i = 0; i < splatCount; i++) {
  4993. const globalSplatIndex = i + splatOffset;
  4994. if (filterFunc(globalSplatIndex)) {
  4995. splatMesh.getSplatCenter(globalSplatIndex, center);
  4996. const addBase = addedCount * 4;
  4997. sceneCenters[addBase] = center.x;
  4998. sceneCenters[addBase + 1] = center.y;
  4999. sceneCenters[addBase + 2] = center.z;
  5000. sceneCenters[addBase + 3] = globalSplatIndex;
  5001. addedCount++;
  5002. }
  5003. }
  5004. return sceneCenters;
  5005. };
  5006. return new Promise((resolve) => {
  5007. const checkForEarlyExit = () => {
  5008. if (this.disposed) {
  5009. this.diposeSplatTreeWorker();
  5010. resolve();
  5011. return true;
  5012. }
  5013. return false;
  5014. };
  5015. if (onIndexesUpload) onIndexesUpload(false);
  5016. delayedExecute(() => {
  5017. if (checkForEarlyExit()) return;
  5018. const allCenters = [];
  5019. if (splatMesh.dynamicMode) {
  5020. let splatOffset = 0;
  5021. for (let s = 0; s < splatMesh.scenes.length; s++) {
  5022. const scene = splatMesh.getScene(s);
  5023. const splatCount = scene.splatBuffer.getSplatCount();
  5024. const sceneCenters = addCentersForScene(splatOffset, splatCount);
  5025. allCenters.push(sceneCenters);
  5026. splatOffset += splatCount;
  5027. }
  5028. } else {
  5029. const sceneCenters = addCentersForScene(0, splatMesh.getSplatCount());
  5030. allCenters.push(sceneCenters);
  5031. }
  5032. this.splatTreeWorker.onmessage = (e) => {
  5033. if (checkForEarlyExit()) return;
  5034. if (e.data.subTrees) {
  5035. if (onSplatTreeConstruction) onSplatTreeConstruction(false);
  5036. delayedExecute(() => {
  5037. if (checkForEarlyExit()) return;
  5038. for (let workerSubTree of e.data.subTrees) {
  5039. const convertedSubTree = SplatSubTree.convertWorkerSubTree(workerSubTree, splatMesh);
  5040. this.subTrees.push(convertedSubTree);
  5041. }
  5042. this.diposeSplatTreeWorker();
  5043. if (onSplatTreeConstruction) onSplatTreeConstruction(true);
  5044. delayedExecute(() => {
  5045. resolve();
  5046. });
  5047. });
  5048. }
  5049. };
  5050. delayedExecute(() => {
  5051. if (checkForEarlyExit()) return;
  5052. if (onIndexesUpload) onIndexesUpload(true);
  5053. const transferBuffers = allCenters.map((array) => array.buffer);
  5054. workerProcessCenters(this.splatTreeWorker, allCenters, transferBuffers, this.maxDepth, this.maxCentersPerNode);
  5055. });
  5056. });
  5057. });
  5058. };
  5059. countLeaves() {
  5060. let leafCount = 0;
  5061. this.visitLeaves(() => {
  5062. leafCount++;
  5063. });
  5064. return leafCount;
  5065. }
  5066. visitLeaves(visitFunc) {//traverse
  5067. const visitLeavesFromNode = (node, visitFunc) => {
  5068. if (node.children.length === 0) visitFunc(node);
  5069. for (let child of node.children) {
  5070. visitLeavesFromNode(child, visitFunc);
  5071. }
  5072. };
  5073. for (let subTree of this.subTrees) {
  5074. visitLeavesFromNode(subTree.rootNode, visitFunc);
  5075. }
  5076. }
  5077. }
  5078. function WebGLExtensions( gl ) {
  5079. const extensions = {};
  5080. function getExtension( name ) {
  5081. if ( extensions[name] !== undefined ) {
  5082. return extensions[name];
  5083. }
  5084. let extension;
  5085. switch ( name ) {
  5086. case 'WEBGL_depth_texture':
  5087. extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) ||
  5088. gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );
  5089. break;
  5090. case 'EXT_texture_filter_anisotropic':
  5091. extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) ||
  5092. gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) ||
  5093. gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
  5094. break;
  5095. case 'WEBGL_compressed_texture_s3tc':
  5096. extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) ||
  5097. gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) ||
  5098. gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
  5099. break;
  5100. case 'WEBGL_compressed_texture_pvrtc':
  5101. extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) ||
  5102. gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
  5103. break;
  5104. default:
  5105. extension = gl.getExtension( name );
  5106. }
  5107. extensions[name] = extension;
  5108. return extension;
  5109. }
  5110. return {
  5111. has: function( name ) {
  5112. return getExtension( name ) !== null;
  5113. },
  5114. init: function( capabilities ) {
  5115. if ( capabilities.isWebGL2 ) {
  5116. getExtension( 'EXT_color_buffer_float' );
  5117. getExtension( 'WEBGL_clip_cull_distance' );
  5118. } else {
  5119. getExtension( 'WEBGL_depth_texture' );
  5120. getExtension( 'OES_texture_float' );
  5121. getExtension( 'OES_texture_half_float' );
  5122. getExtension( 'OES_texture_half_float_linear' );
  5123. getExtension( 'OES_standard_derivatives' );
  5124. getExtension( 'OES_element_index_uint' );
  5125. getExtension( 'OES_vertex_array_object' );
  5126. getExtension( 'ANGLE_instanced_arrays' );
  5127. }
  5128. getExtension( 'OES_texture_float_linear' );
  5129. getExtension( 'EXT_color_buffer_half_float' );
  5130. getExtension( 'WEBGL_multisampled_render_to_texture' );
  5131. },
  5132. get: function( name ) {
  5133. const extension = getExtension( name );
  5134. if ( extension === null ) {
  5135. console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );
  5136. }
  5137. return extension;
  5138. }
  5139. };
  5140. }
  5141. function WebGLCapabilities( gl, extensions, parameters ) {
  5142. let maxAnisotropy;
  5143. function getMaxAnisotropy() {
  5144. if ( maxAnisotropy !== undefined ) return maxAnisotropy;
  5145. if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {
  5146. const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
  5147. maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );
  5148. } else {
  5149. maxAnisotropy = 0;
  5150. }
  5151. return maxAnisotropy;
  5152. }
  5153. function getMaxPrecision( precision ) {
  5154. if ( precision === 'highp' ) {
  5155. if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&
  5156. gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {
  5157. return 'highp';
  5158. }
  5159. precision = 'mediump';
  5160. }
  5161. if ( precision === 'mediump' ) {
  5162. if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&
  5163. gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {
  5164. return 'mediump';
  5165. }
  5166. }
  5167. return 'lowp';
  5168. }
  5169. const isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl.constructor.name === 'WebGL2RenderingContext';
  5170. let precision = parameters.precision !== undefined ? parameters.precision : 'highp';
  5171. const maxPrecision = getMaxPrecision( precision );
  5172. if ( maxPrecision !== precision ) {
  5173. console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );
  5174. precision = maxPrecision;
  5175. }
  5176. const drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' );
  5177. const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;
  5178. const maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
  5179. const maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
  5180. const maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );
  5181. const maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );
  5182. const maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
  5183. const maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );
  5184. const maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );
  5185. const maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );
  5186. const vertexTextures = maxVertexTextures > 0;
  5187. const floatFragmentTextures = isWebGL2 || extensions.has( 'OES_texture_float' );
  5188. const floatVertexTextures = vertexTextures && floatFragmentTextures;
  5189. const maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0;
  5190. return {
  5191. isWebGL2: isWebGL2,
  5192. drawBuffers: drawBuffers,
  5193. getMaxAnisotropy: getMaxAnisotropy,
  5194. getMaxPrecision: getMaxPrecision,
  5195. precision: precision,
  5196. logarithmicDepthBuffer: logarithmicDepthBuffer,
  5197. maxTextures: maxTextures,
  5198. maxVertexTextures: maxVertexTextures,
  5199. maxTextureSize: maxTextureSize,
  5200. maxCubemapSize: maxCubemapSize,
  5201. maxAttributes: maxAttributes,
  5202. maxVertexUniforms: maxVertexUniforms,
  5203. maxVaryings: maxVaryings,
  5204. maxFragmentUniforms: maxFragmentUniforms,
  5205. vertexTextures: vertexTextures,
  5206. floatFragmentTextures: floatFragmentTextures,
  5207. floatVertexTextures: floatVertexTextures,
  5208. maxSamples: maxSamples
  5209. };
  5210. }
  5211. const SceneRevealMode = {
  5212. Default: 0,
  5213. Gradual: 1,
  5214. Instant: 2
  5215. };
  5216. const LogLevel = {
  5217. None: 0,
  5218. Error: 1,
  5219. Warning: 2,
  5220. Info: 3,
  5221. Debug: 4
  5222. };
  5223. const dummyGeometry = new THREE.BufferGeometry();
  5224. const dummyMaterial = new THREE.MeshBasicMaterial();
  5225. const COVARIANCES_ELEMENTS_PER_SPLAT = 6;
  5226. const CENTER_COLORS_ELEMENTS_PER_SPLAT = 4;
  5227. const COVARIANCES_ELEMENTS_PER_TEXEL = 4;
  5228. const CENTER_COLORS_ELEMENTS_PER_TEXEL = 4;
  5229. const TRANSFORM_INDEXES_ELEMENTS_PER_TEXEL = 1;
  5230. const SCENE_FADEIN_RATE_FAST = 0.012;
  5231. const SCENE_FADEIN_RATE_GRADUAL = 0.003;
  5232. const VISIBLE_REGION_EXPANSION_DELTA = 1;
  5233. /**
  5234. * SplatMesh: Container for one or more splat scenes, abstracting them into a single unified container for
  5235. * splat data. Additionally contains data structures and code to make the splat data renderable as a Three.js mesh.
  5236. */
  5237. class SplatMesh extends THREE.Mesh {
  5238. constructor(dynamicMode = true, halfPrecisionCovariancesOnGPU = false, devicePixelRatio = 1,
  5239. enableDistancesComputationOnGPU = true, integerBasedDistancesComputation = false,
  5240. antialiased = false, maxScreenSpaceSplatSize = 2048, logLevel = LogLevel.None, sphericalHarmonicsDegree = 0) {
  5241. super(dummyGeometry, dummyMaterial);
  5242. // Reference to a Three.js renderer
  5243. this.renderer = undefined;
  5244. // Use 16-bit floating point values when storing splat covariance data in textures, instead of 32-bit
  5245. this.halfPrecisionCovariancesOnGPU = halfPrecisionCovariancesOnGPU;
  5246. // When 'dynamicMode' is true, scenes are assumed to be non-static. Dynamic scenes are handled differently
  5247. // and certain optimizations cannot be made for them. Additionally, by default, all splat data retrieved from
  5248. // this splat mesh will not have their scene transform applied to them if the splat mesh is dynamic. That
  5249. // can be overriden via parameters to the individual functions that are used to retrieve splat data.
  5250. this.dynamicMode = dynamicMode;
  5251. // Ratio of the resolution in physical pixels to the resolution in CSS pixels for the current display device
  5252. this.devicePixelRatio = devicePixelRatio;
  5253. // Use a transform feedback to calculate splat distances from the camera
  5254. this.enableDistancesComputationOnGPU = enableDistancesComputationOnGPU;
  5255. // Use a faster integer-based approach for calculating splat distances from the camera
  5256. this.integerBasedDistancesComputation = integerBasedDistancesComputation;
  5257. // When true, will perform additional steps during rendering to address artifacts caused by the rendering of gaussians at a
  5258. // substantially different resolution than that at which they were rendered during training. This will only work correctly
  5259. // for models that were trained using a process that utilizes this compensation calculation. For more details:
  5260. // https://github.com/nerfstudio-project/gsplat/pull/117
  5261. // https://github.com/graphdeco-inria/gaussian-splatting/issues/294#issuecomment-1772688093
  5262. this.antialiased = antialiased;
  5263. // Specify the maximum clip space splat size, can help deal with large splats that get too unwieldy
  5264. this.maxScreenSpaceSplatSize = maxScreenSpaceSplatSize;
  5265. // The verbosity of console logging
  5266. this.logLevel = logLevel;
  5267. // Degree 0 means no spherical harmonics
  5268. this.sphericalHarmonicsDegree = sphericalHarmonicsDegree;
  5269. this.minSphericalHarmonicsDegree = 0;
  5270. // The individual splat scenes stored in this splat mesh, each containing their own transform
  5271. this.scenes = [];
  5272. // Special octree tailored to SplatMesh instances
  5273. this.splatTree = null;
  5274. this.baseSplatTree = null;
  5275. // Textures in which splat data will be stored for rendering
  5276. this.splatDataTextures = {};
  5277. this.distancesTransformFeedback = {
  5278. 'id': null,
  5279. 'vertexShader': null,
  5280. 'fragmentShader': null,
  5281. 'program': null,
  5282. 'centersBuffer': null,
  5283. 'transformIndexesBuffer': null,
  5284. 'outDistancesBuffer': null,
  5285. 'centersLoc': -1,
  5286. 'modelViewProjLoc': -1,
  5287. 'transformIndexesLoc': -1,
  5288. 'transformsLocs': []
  5289. };
  5290. this.globalSplatIndexToLocalSplatIndexMap = [];
  5291. this.globalSplatIndexToSceneIndexMap = [];
  5292. this.lastBuildSplatCount = 0;
  5293. this.lastBuildScenes = [];
  5294. this.lastBuildMaxSplatCount = 0;
  5295. this.lastBuildSceneCount = 0;
  5296. this.firstRenderTime = -1;
  5297. this.finalBuild = false;
  5298. this.webGLUtils = null;
  5299. this.boundingBox = new THREE.Box3();
  5300. this.calculatedSceneCenter = new THREE.Vector3();
  5301. this.maxSplatDistanceFromSceneCenter = 0;
  5302. this.visibleRegionBufferRadius = 0;
  5303. this.visibleRegionRadius = 0;
  5304. this.visibleRegionFadeStartRadius = 0;
  5305. this.visibleRegionChanging = false;
  5306. this.splatScale = 1.0;
  5307. this.pointCloudModeEnabled = false;
  5308. this.disposed = false;
  5309. this.lastRenderer = null;
  5310. this.visible = false;
  5311. }
  5312. /**
  5313. * Build the Three.js material that is used to render the splats.
  5314. * @param {number} dynamicMode If true, it means the scene geometry represented by this splat mesh is not stationary or
  5315. * that the splat count might change
  5316. * @param {boolean} antialiased If true, calculate compensation factor to deal with gaussians being rendered at a significantly
  5317. * different resolution than that of their training
  5318. * @param {number} maxScreenSpaceSplatSize The maximum clip space splat size
  5319. * @param {number} splatScale Value by which all splats are scaled in screen-space (default is 1.0)
  5320. * @param {number} pointCloudModeEnabled Render all splats as screen-space circles
  5321. * @param {number} maxSphericalHarmonicsDegree Degree of spherical harmonics to utilize in rendering splats
  5322. * @return {THREE.ShaderMaterial}
  5323. */
  5324. static buildMaterial(dynamicMode = false, antialiased = false, maxScreenSpaceSplatSize = 2048,
  5325. splatScale = 1.0, pointCloudModeEnabled = false, maxSphericalHarmonicsDegree = 0) {
  5326. // Contains the code to project 3D covariance to 2D and from there calculate the quad (using the eigen vectors of the
  5327. // 2D covariance) that is ultimately rasterized
  5328. let vertexShaderSource = `
  5329. precision highp float;
  5330. #include <common>
  5331. attribute uint splatIndex;
  5332. uniform highp sampler2D covariancesTexture;
  5333. uniform highp usampler2D centersColorsTexture;
  5334. uniform highp sampler2D sphericalHarmonicsTexture;`;
  5335. if (dynamicMode) {
  5336. vertexShaderSource += `
  5337. uniform highp usampler2D transformIndexesTexture;
  5338. uniform highp mat4 transforms[${Constants.MaxScenes}];
  5339. uniform vec2 transformIndexesTextureSize;
  5340. `;
  5341. }
  5342. vertexShaderSource += `
  5343. uniform vec2 focal;
  5344. uniform float orthoZoom;
  5345. uniform int orthographicMode;
  5346. uniform int pointCloudModeEnabled;
  5347. uniform float inverseFocalAdjustment;
  5348. uniform vec2 viewport;
  5349. uniform vec2 basisViewport;
  5350. uniform vec2 covariancesTextureSize;
  5351. uniform vec2 centersColorsTextureSize;
  5352. uniform int sphericalHarmonicsDegree;
  5353. uniform vec2 sphericalHarmonicsTextureSize;
  5354. uniform int sphericalHarmonics8BitMode;
  5355. uniform float visibleRegionRadius;
  5356. uniform float visibleRegionFadeStartRadius;
  5357. uniform float firstRenderTime;
  5358. uniform float currentTime;
  5359. uniform int fadeInComplete;
  5360. uniform vec3 sceneCenter;
  5361. uniform float splatScale;
  5362. varying vec4 vColor;
  5363. varying vec2 vUv;
  5364. varying vec2 vPosition;
  5365. const float sqrt8 = sqrt(8.0);
  5366. const float minAlpha = 1.0 / 255.0;
  5367. const vec4 encodeNorm4 = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
  5368. const uvec4 mask4 = uvec4(uint(0x000000FF), uint(0x0000FF00), uint(0x00FF0000), uint(0xFF000000));
  5369. const uvec4 shift4 = uvec4(0, 8, 16, 24);
  5370. vec4 uintToRGBAVec (uint u) {
  5371. uvec4 urgba = mask4 & u;
  5372. urgba = urgba >> shift4;
  5373. vec4 rgba = vec4(urgba) * encodeNorm4;
  5374. return rgba;
  5375. }
  5376. vec2 getDataUV(in int stride, in int offset, in vec2 dimensions) {
  5377. vec2 samplerUV = vec2(0.0, 0.0);
  5378. float d = float(splatIndex * uint(stride) + uint(offset)) / dimensions.x;
  5379. samplerUV.y = float(floor(d)) / dimensions.y;
  5380. samplerUV.x = fract(d);
  5381. return samplerUV;
  5382. }
  5383. vec2 getDataUVF(in uint sIndex, in float stride, in uint offset, in vec2 dimensions) {
  5384. vec2 samplerUV = vec2(0.0, 0.0);
  5385. float d = float(uint(float(sIndex) * stride) + offset) / dimensions.x;
  5386. samplerUV.y = float(floor(d)) / dimensions.y;
  5387. samplerUV.x = fract(d);
  5388. return samplerUV;
  5389. }
  5390. const float SH_C1 = 0.4886025119029199f;
  5391. const float[5] SH_C2 = float[](1.0925484, -1.0925484, 0.3153916, -1.0925484, 0.5462742);
  5392. const vec3 vecOnes3 = vec3(1.0, 1.0, 1.0);
  5393. void main () {
  5394. uint oddOffset = splatIndex & uint(0x00000001);
  5395. uint doubleOddOffset = oddOffset * uint(2);
  5396. bool isEven = oddOffset == uint(0);
  5397. uint nearestEvenIndex = splatIndex - oddOffset;
  5398. float fOddOffset = float(oddOffset);
  5399. uvec4 sampledCenterColor = texture(centersColorsTexture, getDataUV(1, 0, centersColorsTextureSize));
  5400. vec3 splatCenter = uintBitsToFloat(uvec3(sampledCenterColor.gba));`;
  5401. if (dynamicMode) {
  5402. vertexShaderSource += `
  5403. uint transformIndex = texture(transformIndexesTexture, getDataUV(1, 0, transformIndexesTextureSize)).r;
  5404. mat4 transform = transforms[transformIndex];
  5405. mat4 transformModelViewMatrix = modelViewMatrix * transform;
  5406. `;
  5407. } else {
  5408. vertexShaderSource += `mat4 transformModelViewMatrix = modelViewMatrix;`;
  5409. }
  5410. vertexShaderSource += `
  5411. vec4 viewCenter = transformModelViewMatrix * vec4(splatCenter, 1.0);
  5412. vec4 clipCenter = projectionMatrix * viewCenter;
  5413. float clip = 1.2 * clipCenter.w;
  5414. if (clipCenter.z < -clip || clipCenter.x < -clip || clipCenter.x > clip || clipCenter.y < -clip || clipCenter.y > clip) {
  5415. gl_Position = vec4(0.0, 0.0, 2.0, 1.0);
  5416. return;
  5417. }
  5418. vPosition = position.xy;
  5419. vColor = uintToRGBAVec(sampledCenterColor.r);
  5420. `;
  5421. if (maxSphericalHarmonicsDegree >= 1) {
  5422. vertexShaderSource += `
  5423. if (sphericalHarmonicsDegree >= 1) {
  5424. `;
  5425. if (dynamicMode) {
  5426. vertexShaderSource += `
  5427. mat4 mTransform = modelMatrix * transform;
  5428. vec3 worldViewDir = normalize(splatCenter - vec3(inverse(mTransform) * vec4(cameraPosition, 1.0)));
  5429. `;
  5430. } else {
  5431. vertexShaderSource += `
  5432. vec3 worldViewDir = normalize(splatCenter - cameraPosition);
  5433. `;
  5434. }
  5435. if (maxSphericalHarmonicsDegree >= 2) {
  5436. vertexShaderSource += `
  5437. vec4 sampledSH0123 = texture(sphericalHarmonicsTexture, getDataUV(6, 0, sphericalHarmonicsTextureSize));
  5438. vec4 sampledSH4567 = texture(sphericalHarmonicsTexture, getDataUV(6, 1, sphericalHarmonicsTextureSize));
  5439. vec4 sampledSH891011 = texture(sphericalHarmonicsTexture, getDataUV(6, 2, sphericalHarmonicsTextureSize));
  5440. vec3 sh1 = sampledSH0123.rgb;
  5441. vec3 sh2 = vec3(sampledSH0123.a, sampledSH4567.rg);
  5442. vec3 sh3 = vec3(sampledSH4567.ba, sampledSH891011.r);
  5443. `;
  5444. } else {
  5445. vertexShaderSource += `
  5446. vec2 shUV = getDataUVF(nearestEvenIndex, 2.5, doubleOddOffset, sphericalHarmonicsTextureSize);
  5447. vec4 sampledSH0123 = texture(sphericalHarmonicsTexture, shUV);
  5448. shUV = getDataUVF(nearestEvenIndex, 2.5, doubleOddOffset + uint(1), sphericalHarmonicsTextureSize);
  5449. vec4 sampledSH4567 = texture(sphericalHarmonicsTexture, shUV);
  5450. shUV = getDataUVF(nearestEvenIndex, 2.5, doubleOddOffset + uint(2), sphericalHarmonicsTextureSize);
  5451. vec4 sampledSH891011 = texture(sphericalHarmonicsTexture, shUV);
  5452. vec3 sh1 = vec3(sampledSH0123.rgb) * (1.0 - fOddOffset) + vec3(sampledSH0123.ba, sampledSH4567.r) * fOddOffset;
  5453. vec3 sh2 = vec3(sampledSH0123.a, sampledSH4567.rg) * (1.0 - fOddOffset) + vec3(sampledSH4567.gba) * fOddOffset;
  5454. vec3 sh3 = vec3(sampledSH4567.ba, sampledSH891011.r) * (1.0 - fOddOffset) + vec3(sampledSH891011.rgb) * fOddOffset;
  5455. `;
  5456. }
  5457. vertexShaderSource += `
  5458. if (sphericalHarmonics8BitMode == 1) {
  5459. sh1 = sh1 * 2.0 - vecOnes3;
  5460. sh2 = sh2 * 2.0 - vecOnes3;
  5461. sh3 = sh3 * 2.0 - vecOnes3;
  5462. }
  5463. float x = worldViewDir.x;
  5464. float y = worldViewDir.y;
  5465. float z = worldViewDir.z;
  5466. vColor.rgb += SH_C1 * (-sh1 * y + sh2 * z - sh3 * x);
  5467. `;
  5468. if (maxSphericalHarmonicsDegree >= 2) {
  5469. vertexShaderSource += `
  5470. if (sphericalHarmonicsDegree >= 2) {
  5471. float xx = x * x;
  5472. float yy = y * y;
  5473. float zz = z * z;
  5474. float xy = x * y;
  5475. float yz = y * z;
  5476. float xz = x * z;
  5477. vec4 sampledSH12131415 = texture(sphericalHarmonicsTexture, getDataUV(6, 3, sphericalHarmonicsTextureSize));
  5478. vec4 sampledSH16171819 = texture(sphericalHarmonicsTexture, getDataUV(6, 4, sphericalHarmonicsTextureSize));
  5479. vec4 sampledSH20212223 = texture(sphericalHarmonicsTexture, getDataUV(6, 5, sphericalHarmonicsTextureSize));
  5480. vec3 sh4 = sampledSH891011.gba;
  5481. vec3 sh5 = sampledSH12131415.rgb;
  5482. vec3 sh6 = vec3(sampledSH12131415.a, sampledSH16171819.rg);
  5483. vec3 sh7 = vec3(sampledSH16171819.ba, sampledSH20212223.r);
  5484. vec3 sh8 = sampledSH20212223.gba;
  5485. if (sphericalHarmonics8BitMode == 1) {
  5486. sh4 = sh4 * 2.0 - vecOnes3;
  5487. sh5 = sh5 * 2.0 - vecOnes3;
  5488. sh6 = sh6 * 2.0 - vecOnes3;
  5489. sh7 = sh7 * 2.0 - vecOnes3;
  5490. sh8 = sh8 * 2.0 - vecOnes3;
  5491. }
  5492. vColor.rgb +=
  5493. (SH_C2[0] * xy) * sh4 +
  5494. (SH_C2[1] * yz) * sh5 +
  5495. (SH_C2[2] * (2.0 * zz - xx - yy)) * sh6 +
  5496. (SH_C2[3] * xz) * sh7 +
  5497. (SH_C2[4] * (xx - yy)) * sh8;
  5498. }
  5499. `;
  5500. }
  5501. vertexShaderSource += `
  5502. }
  5503. `;
  5504. }
  5505. vertexShaderSource += `
  5506. vec4 sampledCovarianceA = texture(covariancesTexture,
  5507. getDataUVF(nearestEvenIndex, 1.5, oddOffset, covariancesTextureSize));
  5508. vec4 sampledCovarianceB = texture(covariancesTexture,
  5509. getDataUVF(nearestEvenIndex, 1.5, oddOffset + uint(1), covariancesTextureSize));
  5510. vec3 cov3D_M11_M12_M13 = vec3(sampledCovarianceA.rgb) * (1.0 - fOddOffset) +
  5511. vec3(sampledCovarianceA.ba, sampledCovarianceB.r) * fOddOffset;
  5512. vec3 cov3D_M22_M23_M33 = vec3(sampledCovarianceA.a, sampledCovarianceB.rg) * (1.0 - fOddOffset) +
  5513. vec3(sampledCovarianceB.gba) * fOddOffset;
  5514. // Construct the 3D covariance matrix
  5515. mat3 Vrk = mat3(
  5516. cov3D_M11_M12_M13.x, cov3D_M11_M12_M13.y, cov3D_M11_M12_M13.z,
  5517. cov3D_M11_M12_M13.y, cov3D_M22_M23_M33.x, cov3D_M22_M23_M33.y,
  5518. cov3D_M11_M12_M13.z, cov3D_M22_M23_M33.y, cov3D_M22_M23_M33.z
  5519. );
  5520. // Construct the Jacobian行列式 of the affine仿射 approximation逼近 of the projection matrix. It will be used to transform the
  5521. // 3D covariance matrix instead of using the actual projection matrix because that transformation would
  5522. // require a non-linear component (perspective division) which would yield a non-gaussian result. (This assumes假设
  5523. // the current projection is a perspective projection).
  5524. //翻译结果:构建投影矩阵仿射近似的雅可比矩阵。它将被用来变换3D协方差矩阵,而不是直接使用实际的投影矩阵,因为使用实际投影矩阵进行变换需要包含非线性成分(透视除法),这会导致非高斯的结果。这里假设当前的投影为透视投影。
  5525. //看不懂 https://zhuanlan.zhihu.com/p/661569671
  5526. mat3 J;
  5527. if (orthographicMode == 1) {
  5528. // Since the projection is linear, we don't need an approximation
  5529. J = transpose(mat3(orthoZoom, 0.0, 0.0,
  5530. 0.0, orthoZoom, 0.0,
  5531. 0.0, 0.0, 0.0));
  5532. } else {
  5533. // Construct the Jacobian of the affine approximation of the projection matrix. It will be used to transform the
  5534. // 3D covariance matrix instead of using the actual projection matrix because that transformation would
  5535. // require a non-linear component (perspective division) which would yield a non-gaussian result.
  5536. float s = 1.0 / (viewCenter.z * viewCenter.z);
  5537. J = mat3(
  5538. focal.x / viewCenter.z, 0., -(focal.x * viewCenter.x) * s,
  5539. 0., focal.y / viewCenter.z, -(focal.y * viewCenter.y) * s,
  5540. 0., 0., 0.
  5541. );
  5542. }
  5543. // Concatenate the projection approximation with the model-view transformation
  5544. mat3 W = transpose(mat3(transformModelViewMatrix));
  5545. mat3 T = W * J;
  5546. // Transform the 3D covariance matrix (Vrk) to compute the 2D covariance matrix
  5547. mat3 cov2Dm = transpose(T) * Vrk * T;
  5548. `;
  5549. if (antialiased) {
  5550. vertexShaderSource += `
  5551. float detOrig = cov2Dm[0][0] * cov2Dm[1][1] - cov2Dm[0][1] * cov2Dm[0][1];
  5552. cov2Dm[0][0] += 0.3;
  5553. cov2Dm[1][1] += 0.3;
  5554. float detBlur = cov2Dm[0][0] * cov2Dm[1][1] - cov2Dm[0][1] * cov2Dm[0][1];
  5555. float compensation = sqrt(max(detOrig / detBlur, 0.0));
  5556. `;
  5557. } else {
  5558. vertexShaderSource += `
  5559. cov2Dm[0][0] += 0.3;
  5560. cov2Dm[1][1] += 0.3;
  5561. float compensation = 1.0;
  5562. `;
  5563. }
  5564. vertexShaderSource += `
  5565. vColor.a *= compensation;
  5566. if (vColor.a < minAlpha) return;
  5567. // We are interested in the upper-left 2x2 portion of the projected 3D covariance matrix because
  5568. // we only care about the X and Y values. We want the X-diagonal, cov2Dm[0][0],
  5569. // the Y-diagonal, cov2Dm[1][1], and the correlation between the two cov2Dm[0][1]. We don't
  5570. // need cov2Dm[1][0] because it is a symetric matrix.
  5571. vec3 cov2Dv = vec3(cov2Dm[0][0], cov2Dm[0][1], cov2Dm[1][1]);
  5572. vec3 ndcCenter = clipCenter.xyz / clipCenter.w;
  5573. // We now need to solve for the eigen-values and eigen vectors of the 2D covariance matrix
  5574. // so that we can determine the 2D basis for the splat. This is done using the method described
  5575. // here: https://people.math.harvard.edu/~knill/teaching/math21b2004/exhibits/2dmatrices/index.html 公式出处
  5576. // After calculating the eigen-values and eigen-vectors, we calculate the basis for rendering the splat
  5577. // by normalizing the eigen-vectors and then multiplying them by (sqrt(8) * eigen-value), which is
  5578. // equal to scaling them by sqrt(8) standard deviations.
  5579. //
  5580. // This is a different approach than in the original work at INRIA. In that work they compute the
  5581. // max extents of the projected splat in screen space to form a screen-space aligned bounding rectangle
  5582. // which forms the geometry that is actually rasterized. The dimensions of that bounding box are 3.0
  5583. // times the maximum eigen-value, or 3 standard deviations. They then use the inverse 2D covariance
  5584. // matrix (called 'conic') in the CUDA rendering thread to determine fragment opacity by calculating the
  5585. // full gaussian: exp(-0.5 * (X - mean) * conic * (X - mean)) * splat opacity
  5586. float a = cov2Dv.x;
  5587. float d = cov2Dv.z;
  5588. float b = cov2Dv.y;
  5589. float D = a * d - b * b;
  5590. float trace = a + d; //追踪
  5591. float traceOver2 = 0.5 * trace;
  5592. float term2 = sqrt(max(0.1f, traceOver2 * traceOver2 - D));
  5593. float eigenValue1 = traceOver2 + term2; //特征值L1
  5594. float eigenValue2 = traceOver2 - term2; //特征值L1
  5595. if (pointCloudModeEnabled == 1) {// each splat is rendered as a filled circle
  5596. eigenValue1 = eigenValue2 = 0.2;
  5597. }
  5598. if (eigenValue2 <= 0.0) return;
  5599. vec2 eigenVector1 = normalize(vec2(b, eigenValue1 - a));
  5600. // since the eigen vectors are orthogonal, we derive the second one from the first 由于本征向量是正交的,因此我们从第一个导出第二个
  5601. vec2 eigenVector2 = vec2(eigenVector1.y, -eigenVector1.x);
  5602. // We use sqrt(8) standard deviations偏差 instead of 3 to eliminate消除 more of the splat with a very low opacity.
  5603. vec2 basisVector1 = eigenVector1 * splatScale * min(sqrt8 * sqrt(eigenValue1), ${parseInt(maxScreenSpaceSplatSize)}.0);
  5604. vec2 basisVector2 = eigenVector2 * splatScale * min(sqrt8 * sqrt(eigenValue2), ${parseInt(maxScreenSpaceSplatSize)}.0);
  5605. if (fadeInComplete == 0) {
  5606. float opacityAdjust = 1.0;
  5607. float centerDist = length(splatCenter - sceneCenter);
  5608. float renderTime = max(currentTime - firstRenderTime, 0.0);
  5609. float fadeDistance = 0.75;
  5610. float distanceLoadFadeInFactor = step(visibleRegionFadeStartRadius, centerDist);
  5611. distanceLoadFadeInFactor = (1.0 - distanceLoadFadeInFactor) +
  5612. (1.0 - clamp((centerDist - visibleRegionFadeStartRadius) / fadeDistance, 0.0, 1.0)) *
  5613. distanceLoadFadeInFactor;
  5614. opacityAdjust *= distanceLoadFadeInFactor;
  5615. vColor.a *= opacityAdjust;
  5616. }
  5617. vec2 ndcOffset = vec2(vPosition.x * basisVector1 + vPosition.y * basisVector2) *
  5618. basisViewport * 2.0 * inverseFocalAdjustment;
  5619. vec4 quadPos = vec4(ndcCenter.xy + ndcOffset, ndcCenter.z , 1.0);
  5620. gl_Position = quadPos; //将整个正方形做一定的拉伸形变,之后在fs中因为只显示中间的圆,所以也就是显示拉伸后的圆。(vs只计算四个顶点的具体位置,其他位置是插值得到的)
  5621. // Scale the position data we send to the fragment shader
  5622. vPosition *= sqrt8;
  5623. }`;
  5624. //面片模拟椭球,总是有厚度
  5625. const fragmentShaderSource = `
  5626. precision highp float;
  5627. #include <common>
  5628. uniform vec3 debugColor;
  5629. varying vec4 vColor;
  5630. varying vec2 vUv;
  5631. varying vec2 vPosition;
  5632. void main () {
  5633. // Compute the positional squared distance from the center of the splat to the current fragment.
  5634. float A = dot(vPosition, vPosition);
  5635. // Since the positional data in vPosition has been scaled by sqrt(8), the squared result will be
  5636. // scaled by a factor of 8. If the squared result is larger than 8, it means it is outside the ellipse
  5637. // defined by the rectangle formed by vPosition. It also means it's farther
  5638. // away than sqrt(8) standard deviations from the mean.
  5639. if (A > 8.0) discard; //position的范围半径为1。指一个rectangle面中的范围。椭圆外的完全透明
  5640. vec3 color = vColor.rgb;
  5641. // Since the rendered splat is scaled by sqrt(8), the inverse covariance matrix that is part of
  5642. // the gaussian formula becomes the identity matrix. We're then left with (X - mean) * (X - mean),
  5643. // and since 'mean' is zero, we have X * X, which is the same as A:
  5644. float opacity = exp(-0.5 * A) * vColor.a; //周围的透明度降低
  5645. gl_FragColor = vec4(color.rgb, opacity);
  5646. //不知道为何ply转过来的vColor.a是0,(但是在外面访问的opacity并不是0), 导致全是黑色
  5647. }`;
  5648. const uniforms = {
  5649. 'sceneCenter': {
  5650. 'type': 'v3',
  5651. 'value': new THREE.Vector3()
  5652. },
  5653. 'fadeInComplete': {
  5654. 'type': 'i',
  5655. 'value': 0
  5656. },
  5657. 'orthographicMode': {
  5658. 'type': 'i',
  5659. 'value': 0
  5660. },
  5661. 'visibleRegionFadeStartRadius': {
  5662. 'type': 'f',
  5663. 'value': 0.0
  5664. },
  5665. 'visibleRegionRadius': {
  5666. 'type': 'f',
  5667. 'value': 0.0
  5668. },
  5669. 'currentTime': {
  5670. 'type': 'f',
  5671. 'value': 0.0
  5672. },
  5673. 'firstRenderTime': {
  5674. 'type': 'f',
  5675. 'value': 0.0
  5676. },
  5677. 'covariancesTexture': {//协方差
  5678. 'type': 't',
  5679. 'value': null
  5680. },
  5681. 'centersColorsTexture': {
  5682. 'type': 't',
  5683. 'value': null
  5684. },
  5685. 'sphericalHarmonicsTexture': {
  5686. 'type': 't',
  5687. 'value': null
  5688. },
  5689. 'focal': {
  5690. 'type': 'v2',
  5691. 'value': new THREE.Vector2()
  5692. },
  5693. 'orthoZoom': {
  5694. 'type': 'f',
  5695. 'value': 1.0
  5696. },
  5697. 'inverseFocalAdjustment': {
  5698. 'type': 'f',
  5699. 'value': 1.0
  5700. },
  5701. 'viewport': {
  5702. 'type': 'v2',
  5703. 'value': new THREE.Vector2()
  5704. },
  5705. 'basisViewport': {
  5706. 'type': 'v2',
  5707. 'value': new THREE.Vector2()
  5708. },
  5709. 'debugColor': {
  5710. 'type': 'v3',
  5711. 'value': new THREE.Color()
  5712. },
  5713. 'covariancesTextureSize': {
  5714. 'type': 'v2',
  5715. 'value': new THREE.Vector2(1024, 1024)
  5716. },
  5717. 'centersColorsTextureSize': {
  5718. 'type': 'v2',
  5719. 'value': new THREE.Vector2(1024, 1024)
  5720. },
  5721. 'sphericalHarmonicsDegree': {
  5722. 'type': 'i',
  5723. 'value': maxSphericalHarmonicsDegree
  5724. },
  5725. 'sphericalHarmonicsTextureSize': {
  5726. 'type': 'v2',
  5727. 'value': new THREE.Vector2(1024, 1024)
  5728. },
  5729. 'sphericalHarmonics8BitMode': {
  5730. 'type': 'i',
  5731. 'value': 0
  5732. },
  5733. 'splatScale': {
  5734. 'type': 'f',
  5735. 'value': splatScale
  5736. },
  5737. 'pointCloudModeEnabled': {
  5738. 'type': 'i',
  5739. 'value': pointCloudModeEnabled ? 1 : 0
  5740. }
  5741. };
  5742. if (dynamicMode) {
  5743. uniforms['transformIndexesTexture'] = {
  5744. 'type': 't',
  5745. 'value': null
  5746. };
  5747. const transformMatrices = [];
  5748. for (let i = 0; i < Constants.MaxScenes; i++) {
  5749. transformMatrices.push(new THREE.Matrix4());
  5750. }
  5751. uniforms['transforms'] = {
  5752. 'type': 'mat4',
  5753. 'value': transformMatrices
  5754. };
  5755. uniforms['transformIndexesTextureSize'] = {
  5756. 'type': 'v2',
  5757. 'value': new THREE.Vector2(1024, 1024)
  5758. };
  5759. }
  5760. const material = new THREE.ShaderMaterial({
  5761. uniforms: uniforms,
  5762. vertexShader: vertexShaderSource,
  5763. fragmentShader: fragmentShaderSource,
  5764. transparent: true,
  5765. alphaTest: 1.0,
  5766. blending: THREE.NormalBlending,
  5767. depthTest: true,
  5768. depthWrite: false,
  5769. side: THREE.DoubleSide
  5770. });
  5771. return material;
  5772. }
  5773. /**
  5774. * Build the Three.js geometry that will be used to render the splats. The geometry is instanced and is made up of
  5775. * vertices for a single quad as well as an attribute buffer for the splat indexes.
  5776. * @param {number} maxSplatCount The maximum number of splats that the geometry will need to accomodate
  5777. * @return {THREE.InstancedBufferGeometry}
  5778. */
  5779. static buildGeomtery(maxSplatCount) {
  5780. const baseGeometry = new THREE.BufferGeometry();
  5781. baseGeometry.setIndex([0, 1, 2, 0, 2, 3]);
  5782. // Vertices for the instanced quad
  5783. const positionsArray = new Float32Array(4 * 3);
  5784. const positions = new THREE.BufferAttribute(positionsArray, 3);
  5785. baseGeometry.setAttribute('position', positions);
  5786. positions.setXYZ(0, -1.0, -1.0, 0.0);
  5787. positions.setXYZ(1, -1.0, 1.0, 0.0);
  5788. positions.setXYZ(2, 1.0, 1.0, 0.0);
  5789. positions.setXYZ(3, 1.0, -1.0, 0.0);
  5790. positions.needsUpdate = true;
  5791. const geometry = new THREE.InstancedBufferGeometry().copy(baseGeometry);
  5792. // Splat index buffer
  5793. const splatIndexArray = new Uint32Array(maxSplatCount);
  5794. const splatIndexes = new THREE.InstancedBufferAttribute(splatIndexArray, 1, false);
  5795. splatIndexes.setUsage(THREE.DynamicDrawUsage);
  5796. geometry.setAttribute('splatIndex', splatIndexes);
  5797. geometry.instanceCount = 0;
  5798. return geometry;
  5799. }
  5800. /**
  5801. * Build a container for each scene managed by this splat mesh based on an instance of SplatBuffer, along with optional
  5802. * transform data (position, scale, rotation) passed to the splat mesh during the build process.
  5803. * @param {Array<THREE.Matrix4>} splatBuffers SplatBuffer instances containing splats for each scene
  5804. * @param {Array<object>} sceneOptions Array of options objects: {
  5805. *
  5806. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  5807. *
  5808. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  5809. *
  5810. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  5811. * }
  5812. * @return {Array<THREE.Matrix4>}
  5813. */
  5814. static buildScenes(splatBuffers, sceneOptions) {
  5815. const scenes = [];
  5816. scenes.length = splatBuffers.length;
  5817. for (let i = 0; i < splatBuffers.length; i++) {
  5818. const splatBuffer = splatBuffers[i];
  5819. const options = sceneOptions[i] || {};
  5820. let positionArray = options['position'] || [0, 0, 0];
  5821. let rotationArray = options['rotation'] || [0, 0, 0, 1];
  5822. let scaleArray = options['scale'] || [1, 1, 1];
  5823. const position = new THREE.Vector3().fromArray(positionArray);
  5824. const rotation = new THREE.Quaternion().fromArray(rotationArray);
  5825. const scale = new THREE.Vector3().fromArray(scaleArray);
  5826. scenes[i] = SplatMesh.createScene(splatBuffer, position, rotation, scale, options.splatAlphaRemovalThreshold || 1);
  5827. }
  5828. return scenes;
  5829. }
  5830. static createScene(splatBuffer, position, rotation, scale, minimumAlpha) {
  5831. return new SplatScene(splatBuffer, position, rotation, scale, minimumAlpha);
  5832. }
  5833. /**
  5834. * Build data structures that map global splat indexes (based on a unified index across all splat buffers) to
  5835. * local data within a single scene.
  5836. * @param {Array<SplatBuffer>} splatBuffers Instances of SplatBuffer off which to build the maps
  5837. * @return {object}
  5838. */
  5839. static buildSplatIndexMaps(splatBuffers) {
  5840. const localSplatIndexMap = [];
  5841. const sceneIndexMap = [];
  5842. let totalSplatCount = 0;
  5843. for (let s = 0; s < splatBuffers.length; s++) {
  5844. const splatBuffer = splatBuffers[s];
  5845. const maxSplatCount = splatBuffer.getMaxSplatCount();
  5846. for (let i = 0; i < maxSplatCount; i++) {
  5847. localSplatIndexMap[totalSplatCount] = i;
  5848. sceneIndexMap[totalSplatCount] = s;
  5849. totalSplatCount++;
  5850. }
  5851. }
  5852. return {
  5853. localSplatIndexMap,
  5854. sceneIndexMap
  5855. };
  5856. }
  5857. /**
  5858. * Build an instance of SplatTree (a specialized octree) for the given splat mesh.
  5859. * @param {Array<number>} minAlphas Array of minimum splat slphas for each scene
  5860. * @param {function} onSplatTreeIndexesUpload Function to be called when the upload of splat centers to the splat tree
  5861. * builder worker starts and finishes.
  5862. * @param {function} onSplatTreeConstruction Function to be called when the conversion of the local splat tree from
  5863. * the format produced by the splat tree builder worker starts and ends.
  5864. * @return {SplatTree}
  5865. */
  5866. buildSplatTree = function(minAlphas = [], onSplatTreeIndexesUpload, onSplatTreeConstruction) {
  5867. return new Promise((resolve) => {
  5868. this.disposeSplatTree();
  5869. // TODO: expose SplatTree constructor parameters (maximumDepth and maxCentersPerNode) so that they can
  5870. // be configured on a per-scene basis
  5871. this.baseSplatTree = new SplatTree(8, 1000);
  5872. const buildStartTime = performance.now();
  5873. const splatColor = new THREE.Vector4();
  5874. this.baseSplatTree.processSplatMesh(this, (splatIndex) => {
  5875. this.getSplatColor(splatIndex, splatColor);
  5876. const sceneIndex = this.getSceneIndexForSplat(splatIndex);
  5877. const minAlpha = minAlphas[sceneIndex] || 1;
  5878. return splatColor.w >= minAlpha;
  5879. }, onSplatTreeIndexesUpload, onSplatTreeConstruction)
  5880. .then(() => {
  5881. const buildTime = performance.now() - buildStartTime;
  5882. if (this.logLevel >= LogLevel.Info) console.log('SplatTree build: ' + buildTime + ' ms');
  5883. if (this.disposed) {
  5884. resolve();
  5885. } else {
  5886. this.splatTree = this.baseSplatTree;
  5887. this.baseSplatTree = null;
  5888. let leavesWithVertices = 0;
  5889. let avgSplatCount = 0;
  5890. let maxSplatCount = 0;
  5891. let nodeCount = 0;
  5892. this.splatTree.visitLeaves((node) => {
  5893. const nodeSplatCount = node.data.indexes.length;
  5894. if (nodeSplatCount > 0) {
  5895. avgSplatCount += nodeSplatCount;
  5896. maxSplatCount = Math.max(maxSplatCount, nodeSplatCount);
  5897. nodeCount++;
  5898. leavesWithVertices++;
  5899. }
  5900. });
  5901. if (this.logLevel >= LogLevel.Info) {
  5902. console.log(`SplatTree leaves: ${this.splatTree.countLeaves()}`);
  5903. console.log(`SplatTree leaves with splats:${leavesWithVertices}`);
  5904. avgSplatCount = avgSplatCount / nodeCount;
  5905. console.log(`Avg splat count per node: ${avgSplatCount}`);
  5906. console.log(`Total splat count: ${this.getSplatCount()}`);
  5907. }
  5908. resolve();
  5909. }
  5910. });
  5911. });
  5912. };
  5913. /**
  5914. * Construct this instance of SplatMesh.
  5915. * @param {Array<SplatBuffer>} splatBuffers The base splat data, instances of SplatBuffer
  5916. * @param {Array<object>} sceneOptions Dynamic options for each scene {
  5917. *
  5918. * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
  5919. * value (valid range: 0 - 255), defaults to 1
  5920. *
  5921. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  5922. *
  5923. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  5924. *
  5925. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  5926. *
  5927. * }
  5928. * @param {boolean} keepSceneTransforms For a scene that already exists and is being overwritten, this flag
  5929. * says to keep the transform from the existing scene.
  5930. * @param {boolean} finalBuild Will the splat mesh be in its final state after this build?
  5931. * @param {function} onSplatTreeIndexesUpload Function to be called when the upload of splat centers to the splat tree
  5932. * builder worker starts and finishes.
  5933. * @param {function} onSplatTreeConstruction Function to be called when the conversion of the local splat tree from
  5934. * the format produced by the splat tree builder worker starts and ends.
  5935. * @return {object} Object containing info about the splats that are updated
  5936. */
  5937. build(splatBuffers, sceneOptions, keepSceneTransforms = true, finalBuild = false,
  5938. onSplatTreeIndexesUpload, onSplatTreeConstruction) {
  5939. this.sceneOptions = sceneOptions;
  5940. this.finalBuild = finalBuild;
  5941. const maxSplatCount = SplatMesh.getTotalMaxSplatCountForSplatBuffers(splatBuffers);
  5942. const newScenes = SplatMesh.buildScenes(splatBuffers, sceneOptions);
  5943. if (keepSceneTransforms) {
  5944. for (let i = 0; i < this.scenes.length && i < newScenes.length; i++) {
  5945. const newScene = newScenes[i];
  5946. const existingScene = this.getScene(i);
  5947. newScene.copyTransformData(existingScene);
  5948. }
  5949. }
  5950. this.scenes = newScenes;
  5951. let minSphericalHarmonicsDegree = 3;
  5952. for (let splatBuffer of splatBuffers) {
  5953. const splatBufferSphericalHarmonicsDegree = splatBuffer.getMinSphericalHarmonicsDegree();
  5954. if (splatBufferSphericalHarmonicsDegree < minSphericalHarmonicsDegree) {
  5955. minSphericalHarmonicsDegree = splatBufferSphericalHarmonicsDegree;
  5956. }
  5957. }
  5958. this.minSphericalHarmonicsDegree = Math.min(minSphericalHarmonicsDegree, this.sphericalHarmonicsDegree);
  5959. let splatBuffersChanged = false;
  5960. if (splatBuffers.length !== this.lastBuildScenes.length) {
  5961. splatBuffersChanged = true;
  5962. } else {
  5963. for (let i = 0; i < splatBuffers.length; i++) {
  5964. const splatBuffer = splatBuffers[i];
  5965. if (splatBuffer !== this.lastBuildScenes[i].splatBuffer) {
  5966. splatBuffersChanged = true;
  5967. break;
  5968. }
  5969. }
  5970. }
  5971. let isUpdateBuild = true;
  5972. if (this.scenes.length !== 1 ||
  5973. this.lastBuildSceneCount !== this.scenes.length ||
  5974. this.lastBuildMaxSplatCount !== maxSplatCount ||
  5975. splatBuffersChanged) {
  5976. isUpdateBuild = false;
  5977. }
  5978. if (!isUpdateBuild) {
  5979. this.boundingBox = new THREE.Box3();
  5980. this.maxSplatDistanceFromSceneCenter = 0;
  5981. this.visibleRegionBufferRadius = 0;
  5982. this.visibleRegionRadius = 0;
  5983. this.visibleRegionFadeStartRadius = 0;
  5984. this.firstRenderTime = -1;
  5985. this.lastBuildScenes = [];
  5986. this.lastBuildSplatCount = 0;
  5987. this.lastBuildMaxSplatCount = 0;
  5988. this.disposeMeshData();
  5989. this.geometry = SplatMesh.buildGeomtery(maxSplatCount);
  5990. this.material = SplatMesh.buildMaterial(this.dynamicMode, this.antialiased, this.maxScreenSpaceSplatSize,
  5991. this.splatScale, this.pointCloudModeEnabled, this.minSphericalHarmonicsDegree);
  5992. const indexMaps = SplatMesh.buildSplatIndexMaps(splatBuffers);
  5993. this.globalSplatIndexToLocalSplatIndexMap = indexMaps.localSplatIndexMap;
  5994. this.globalSplatIndexToSceneIndexMap = indexMaps.sceneIndexMap;
  5995. }
  5996. const splatCount = this.getSplatCount();
  5997. if (this.enableDistancesComputationOnGPU) this.setupDistancesComputationTransformFeedback();
  5998. const dataUpdateResults = this.refreshGPUDataFromSplatBuffers(isUpdateBuild);
  5999. for (let i = 0; i < this.scenes.length; i++) {
  6000. this.lastBuildScenes[i] = this.scenes[i];
  6001. }
  6002. this.lastBuildSplatCount = splatCount;
  6003. this.lastBuildMaxSplatCount = this.getMaxSplatCount();
  6004. this.lastBuildSceneCount = this.scenes.length;
  6005. if (finalBuild && this.scenes.length > 0) {
  6006. this.buildSplatTree(sceneOptions.map(options => options.splatAlphaRemovalThreshold || 1),
  6007. onSplatTreeIndexesUpload, onSplatTreeConstruction)
  6008. .then(() => {
  6009. if (this.onSplatTreeReadyCallback) this.onSplatTreeReadyCallback(this.splatTree);
  6010. });
  6011. }
  6012. this.visible = (this.scenes.length > 0);
  6013. return dataUpdateResults;
  6014. }
  6015. /**
  6016. * Dispose all resources held by the splat mesh
  6017. */
  6018. dispose() {
  6019. this.disposeMeshData();
  6020. this.disposeTextures();
  6021. this.disposeSplatTree();
  6022. if (this.enableDistancesComputationOnGPU) {
  6023. if (this.computeDistancesOnGPUSyncTimeout) {
  6024. clearTimeout(this.computeDistancesOnGPUSyncTimeout);
  6025. this.computeDistancesOnGPUSyncTimeout = null;
  6026. }
  6027. this.disposeDistancesComputationGPUResources();
  6028. }
  6029. this.scenes = [];
  6030. this.distancesTransformFeedback = {
  6031. 'id': null,
  6032. 'vertexShader': null,
  6033. 'fragmentShader': null,
  6034. 'program': null,
  6035. 'centersBuffer': null,
  6036. 'transformIndexesBuffer': null,
  6037. 'outDistancesBuffer': null,
  6038. 'centersLoc': -1,
  6039. 'modelViewProjLoc': -1,
  6040. 'transformIndexesLoc': -1,
  6041. 'transformsLocs': []
  6042. };
  6043. this.renderer = null;
  6044. this.globalSplatIndexToLocalSplatIndexMap = [];
  6045. this.globalSplatIndexToSceneIndexMap = [];
  6046. this.lastBuildSplatCount = 0;
  6047. this.lastBuildScenes = [];
  6048. this.lastBuildMaxSplatCount = 0;
  6049. this.lastBuildSceneCount = 0;
  6050. this.firstRenderTime = -1;
  6051. this.finalBuild = false;
  6052. this.webGLUtils = null;
  6053. this.boundingBox = new THREE.Box3();
  6054. this.calculatedSceneCenter = new THREE.Vector3();
  6055. this.maxSplatDistanceFromSceneCenter = 0;
  6056. this.visibleRegionBufferRadius = 0;
  6057. this.visibleRegionRadius = 0;
  6058. this.visibleRegionFadeStartRadius = 0;
  6059. this.visibleRegionChanging = false;
  6060. this.splatScale = 1.0;
  6061. this.pointCloudModeEnabled = false;
  6062. this.disposed = true;
  6063. this.lastRenderer = null;
  6064. this.visible = false;
  6065. }
  6066. /**
  6067. * Dispose of only the Three.js mesh resources (geometry, material, and texture)
  6068. */
  6069. disposeMeshData() {
  6070. if (this.geometry && this.geometry !== dummyGeometry) {
  6071. this.geometry.dispose();
  6072. this.geometry = null;
  6073. }
  6074. if (this.material) {
  6075. this.material.dispose();
  6076. this.material = null;
  6077. }
  6078. }
  6079. disposeTextures() {
  6080. for (let textureKey in this.splatDataTextures) {
  6081. if (this.splatDataTextures.hasOwnProperty(textureKey)) {
  6082. const textureContainer = this.splatDataTextures[textureKey];
  6083. if (textureContainer.texture) {
  6084. textureContainer.texture.dispose();
  6085. textureContainer.texture = null;
  6086. }
  6087. }
  6088. }
  6089. this.splatDataTextures = null;
  6090. }
  6091. disposeSplatTree() {
  6092. if (this.splatTree) {
  6093. this.splatTree.dispose();
  6094. this.splatTree = null;
  6095. } else if (this.baseSplatTree) {
  6096. this.baseSplatTree.dispose();
  6097. this.baseSplatTree = null;
  6098. }
  6099. }
  6100. getSplatTree() {
  6101. return this.splatTree;
  6102. }
  6103. onSplatTreeReady(callback) {
  6104. this.onSplatTreeReadyCallback = callback;
  6105. }
  6106. /**
  6107. * Get copies of data that are necessary for splat distance computation: splat center positions and splat
  6108. * scene indexes (necessary for applying dynamic scene transformations during distance computation)
  6109. * @param {*} start The index at which to start copying data
  6110. * @param {*} end The index at which to stop copying data
  6111. * @return {object}
  6112. */
  6113. getDataForDistancesComputation(start, end) {
  6114. const centers = this.integerBasedDistancesComputation ?
  6115. this.getIntegerCenters(start, end, true) :
  6116. this.getFloatCenters(start, end, true);
  6117. const sceneIndexes = this.getSceneIndexes(start, end);
  6118. return {
  6119. centers,
  6120. sceneIndexes
  6121. };
  6122. }
  6123. /**
  6124. * Refresh data textures and GPU buffers with splat data from the splat buffers belonging to this mesh.
  6125. * @param {boolean} sinceLastBuildOnly Specify whether or not to only update for splats that have been added since the last build.
  6126. * @return {object}
  6127. */
  6128. refreshGPUDataFromSplatBuffers(sinceLastBuildOnly) {
  6129. const splatCount = this.getSplatCount();
  6130. this.refreshDataTexturesFromSplatBuffers(sinceLastBuildOnly);
  6131. const updateStart = sinceLastBuildOnly ? this.lastBuildSplatCount : 0;
  6132. const { centers, sceneIndexes } = this.getDataForDistancesComputation(updateStart, splatCount - 1);
  6133. if (this.enableDistancesComputationOnGPU) {
  6134. this.refreshGPUBuffersForDistancesComputation(centers, sceneIndexes, sinceLastBuildOnly);
  6135. }
  6136. return {
  6137. 'from': updateStart,
  6138. 'to': splatCount - 1,
  6139. 'count': splatCount - updateStart,
  6140. 'centers': centers,
  6141. 'sceneIndexes': sceneIndexes
  6142. };
  6143. }
  6144. /**
  6145. * Update the GPU buffers that are used for computing splat distances on the GPU.
  6146. * @param {Array<number>} centers Splat center positions
  6147. * @param {Array<number>} sceneIndexes Indexes of the scene to which each splat belongs
  6148. * @param {boolean} sinceLastBuildOnly Specify whether or not to only update for splats that have been added since the last build.
  6149. */
  6150. refreshGPUBuffersForDistancesComputation(centers, sceneIndexes, sinceLastBuildOnly = false) {
  6151. const offset = sinceLastBuildOnly ? this.lastBuildSplatCount : 0;
  6152. this.updateGPUCentersBufferForDistancesComputation(sinceLastBuildOnly, centers, offset);
  6153. this.updateGPUTransformIndexesBufferForDistancesComputation(sinceLastBuildOnly, sceneIndexes, offset);
  6154. }
  6155. /**
  6156. * Refresh data textures with data from the splat buffers for this mesh.
  6157. * @param {boolean} sinceLastBuildOnly Specify whether or not to only update for splats that have been added since the last build.
  6158. */
  6159. refreshDataTexturesFromSplatBuffers(sinceLastBuildOnly) {
  6160. if (!sinceLastBuildOnly) {
  6161. this.setupDataTextures();
  6162. } else {
  6163. this.updateDataTextures();
  6164. }
  6165. this.updateVisibleRegion(sinceLastBuildOnly);
  6166. }
  6167. setupDataTextures() {
  6168. const maxSplatCount = this.getMaxSplatCount();
  6169. const splatCount = this.getSplatCount();
  6170. this.disposeTextures();
  6171. const computeDataTextureSize = (elementsPerTexel, elementsPerSplatl) => {
  6172. const texSize = new THREE.Vector2(4096, 1024);
  6173. while (texSize.x * texSize.y * elementsPerTexel < maxSplatCount * elementsPerSplatl) texSize.y *= 2;
  6174. return texSize;
  6175. };
  6176. const covarianceCompressionLevel = this.getTargetCovarianceCompressionLevel();
  6177. const sphericalHarmonicsCompressionLevel = this.getTargetSphericalHarmonicsCompressionLevel();
  6178. const covariances = new Float32Array(maxSplatCount * COVARIANCES_ELEMENTS_PER_SPLAT);
  6179. const centers = new Float32Array(maxSplatCount * 3);
  6180. const colors = new Uint8Array(maxSplatCount * 4);
  6181. let SphericalHarmonicsArrayType = Float32Array;
  6182. if (sphericalHarmonicsCompressionLevel === 1) SphericalHarmonicsArrayType = Uint16Array;
  6183. else if (sphericalHarmonicsCompressionLevel === 2) SphericalHarmonicsArrayType = Uint8Array;
  6184. const sphericalHarmonicsComponentCount = getSphericalHarmonicsComponentCountForDegree(this.minSphericalHarmonicsDegree);
  6185. let paddedSphericalHarmonicsComponentCount = sphericalHarmonicsComponentCount;
  6186. if (paddedSphericalHarmonicsComponentCount % 2 !== 0) paddedSphericalHarmonicsComponentCount++;
  6187. const sphericalHarmonics = this.minSphericalHarmonicsDegree ?
  6188. new SphericalHarmonicsArrayType(maxSplatCount * sphericalHarmonicsComponentCount) : undefined;
  6189. this.fillSplatDataArrays(covariances, centers, colors, sphericalHarmonics, undefined,
  6190. covarianceCompressionLevel, sphericalHarmonicsCompressionLevel);
  6191. // set up covariances data texture
  6192. const covTexSize = computeDataTextureSize(COVARIANCES_ELEMENTS_PER_TEXEL, 6);
  6193. let CovariancesDataType = covarianceCompressionLevel >= 1 ? Uint16Array : Float32Array;
  6194. let covariancesTextureType = covarianceCompressionLevel >= 1 ? THREE.HalfFloatType : THREE.FloatType;
  6195. const paddedCovariances = new CovariancesDataType(covTexSize.x * covTexSize.y * COVARIANCES_ELEMENTS_PER_TEXEL);
  6196. paddedCovariances.set(covariances);
  6197. const covTex = new THREE.DataTexture(paddedCovariances, covTexSize.x, covTexSize.y, THREE.RGBAFormat, covariancesTextureType);
  6198. covTex.needsUpdate = true;
  6199. this.material.uniforms.covariancesTexture.value = covTex;
  6200. this.material.uniforms.covariancesTextureSize.value.copy(covTexSize);
  6201. // set up centers/colors data texture
  6202. const centersColsTexSize = computeDataTextureSize(CENTER_COLORS_ELEMENTS_PER_TEXEL, 4);
  6203. const paddedCentersCols = new Uint32Array(centersColsTexSize.x * centersColsTexSize.y * CENTER_COLORS_ELEMENTS_PER_TEXEL);
  6204. SplatMesh.updateCenterColorsPaddedData(0, splatCount, centers, colors, paddedCentersCols);
  6205. const centersColsTex = new THREE.DataTexture(paddedCentersCols, centersColsTexSize.x, centersColsTexSize.y,
  6206. THREE.RGBAIntegerFormat, THREE.UnsignedIntType);
  6207. centersColsTex.internalFormat = 'RGBA32UI';
  6208. centersColsTex.needsUpdate = true;
  6209. this.material.uniforms.centersColorsTexture.value = centersColsTex;
  6210. this.material.uniforms.centersColorsTextureSize.value.copy(centersColsTexSize);
  6211. this.material.uniformsNeedUpdate = true;
  6212. this.splatDataTextures = {
  6213. 'baseData': {
  6214. 'covariances': covariances,
  6215. 'centers': centers,
  6216. 'colors': colors,
  6217. 'sphericalHarmonics': sphericalHarmonics
  6218. },
  6219. 'covariances': {
  6220. 'data': paddedCovariances,
  6221. 'texture': covTex,
  6222. 'size': covTexSize,
  6223. 'compressionLevel': covarianceCompressionLevel
  6224. },
  6225. 'centerColors': {
  6226. 'data': paddedCentersCols,
  6227. 'texture': centersColsTex,
  6228. 'size': centersColsTexSize
  6229. }
  6230. };
  6231. if (sphericalHarmonics) {
  6232. const sphericalHarmonicsElementsPerTexel = 4;
  6233. const sphericalHarmonicsTexSize = computeDataTextureSize(sphericalHarmonicsElementsPerTexel,
  6234. paddedSphericalHarmonicsComponentCount);
  6235. const paddedSHArraySize = sphericalHarmonicsTexSize.x * sphericalHarmonicsTexSize.y * sphericalHarmonicsElementsPerTexel;
  6236. const paddedSHArray = new SphericalHarmonicsArrayType(paddedSHArraySize);
  6237. for (let c = 0; c < splatCount; c++) {
  6238. const srcBase = sphericalHarmonicsComponentCount * c;
  6239. const destBase = paddedSphericalHarmonicsComponentCount * c;
  6240. for (let i = 0; i < sphericalHarmonicsComponentCount; i++) {
  6241. paddedSHArray[destBase + i] = sphericalHarmonics[srcBase + i];
  6242. }
  6243. }
  6244. const textureType = sphericalHarmonicsCompressionLevel === 2 ? THREE.UnsignedByteType : THREE.HalfFloatType;
  6245. const sphericalHarmonicsTex = new THREE.DataTexture(paddedSHArray, sphericalHarmonicsTexSize.x,
  6246. sphericalHarmonicsTexSize.y, THREE.RGBAFormat, textureType);
  6247. sphericalHarmonicsTex.needsUpdate = true;
  6248. this.material.uniforms.sphericalHarmonicsTexture.value = sphericalHarmonicsTex;
  6249. this.material.uniforms.sphericalHarmonicsTextureSize.value.copy(sphericalHarmonicsTexSize);
  6250. if (sphericalHarmonicsCompressionLevel === 2) {
  6251. this.material.uniforms.sphericalHarmonics8BitMode.value = 1;
  6252. }
  6253. this.material.uniformsNeedUpdate = true;
  6254. this.splatDataTextures['sphericalHarmonics'] = {
  6255. 'componentCount': sphericalHarmonicsComponentCount,
  6256. 'paddedComponentCount': paddedSphericalHarmonicsComponentCount,
  6257. 'data': paddedSHArray,
  6258. 'texture': sphericalHarmonicsTex,
  6259. 'size': sphericalHarmonicsTexSize,
  6260. 'compressionLevel': sphericalHarmonicsCompressionLevel
  6261. };
  6262. }
  6263. if (this.dynamicMode) {
  6264. const transformIndexesTexSize = computeDataTextureSize(TRANSFORM_INDEXES_ELEMENTS_PER_TEXEL, 4);
  6265. const paddedTransformIndexes = new Uint32Array(transformIndexesTexSize.x *
  6266. transformIndexesTexSize.y * TRANSFORM_INDEXES_ELEMENTS_PER_TEXEL);
  6267. for (let c = 0; c < splatCount; c++) paddedTransformIndexes[c] = this.globalSplatIndexToSceneIndexMap[c];
  6268. const transformIndexesTexture = new THREE.DataTexture(paddedTransformIndexes, transformIndexesTexSize.x,
  6269. transformIndexesTexSize.y, THREE.RedIntegerFormat,
  6270. THREE.UnsignedIntType);
  6271. transformIndexesTexture.internalFormat = 'R32UI';
  6272. transformIndexesTexture.needsUpdate = true;
  6273. this.material.uniforms.transformIndexesTexture.value = transformIndexesTexture;
  6274. this.material.uniforms.transformIndexesTextureSize.value.copy(transformIndexesTexSize);
  6275. this.material.uniformsNeedUpdate = true;
  6276. this.splatDataTextures['tansformIndexes'] = {
  6277. 'data': paddedTransformIndexes,
  6278. 'texture': transformIndexesTexture,
  6279. 'size': transformIndexesTexSize
  6280. };
  6281. }
  6282. }
  6283. updateDataTextures() {
  6284. const splatCount = this.getSplatCount();
  6285. const covarianceCompressionLevel = this.splatDataTextures['covariances'].compressionLevel;
  6286. const sphericalHarmonicsTextureDesc = this.splatDataTextures['sphericalHarmonics'];
  6287. const sphericalHarmonicsCompressionLevel = sphericalHarmonicsTextureDesc ? sphericalHarmonicsTextureDesc.compressionLevel : 0;
  6288. this.fillSplatDataArrays(this.splatDataTextures.baseData.covariances,
  6289. this.splatDataTextures.baseData.centers, this.splatDataTextures.baseData.colors,
  6290. this.splatDataTextures.baseData.sphericalHarmonics, undefined, covarianceCompressionLevel,
  6291. sphericalHarmonicsCompressionLevel, this.lastBuildSplatCount, splatCount - 1, this.lastBuildSplatCount);
  6292. const covariancesTextureDescriptor = this.splatDataTextures['covariances'];
  6293. const paddedCovariances = covariancesTextureDescriptor.data;
  6294. const covariancesTexture = covariancesTextureDescriptor.texture;
  6295. const covarancesStartSplat = this.lastBuildSplatCount * COVARIANCES_ELEMENTS_PER_SPLAT;
  6296. const covariancesEndSplat = splatCount * COVARIANCES_ELEMENTS_PER_SPLAT;
  6297. for (let i = covarancesStartSplat; i < covariancesEndSplat; i++) {
  6298. const covariance = this.splatDataTextures.baseData.covariances[i];
  6299. paddedCovariances[i] = covariance;
  6300. }
  6301. const covariancesTextureProps = this.renderer ? this.renderer.properties.get(covariancesTexture) : null;
  6302. if (!covariancesTextureProps || !covariancesTextureProps.__webglTexture) {
  6303. covariancesTexture.needsUpdate = true;
  6304. } else {
  6305. const covaranceBytesPerElement = covarianceCompressionLevel ? 2 : 4;
  6306. this.updateDataTexture(paddedCovariances, covariancesTextureDescriptor, covariancesTextureProps,
  6307. COVARIANCES_ELEMENTS_PER_TEXEL, COVARIANCES_ELEMENTS_PER_SPLAT, covaranceBytesPerElement,
  6308. this.lastBuildSplatCount, splatCount - 1);
  6309. }
  6310. const centerColorsTextureDescriptor = this.splatDataTextures['centerColors'];
  6311. const paddedCenterColors = centerColorsTextureDescriptor.data;
  6312. const centerColorsTexture = centerColorsTextureDescriptor.texture;
  6313. SplatMesh.updateCenterColorsPaddedData(this.lastBuildSplatCount, splatCount, this.splatDataTextures.baseData.centers,
  6314. this.splatDataTextures.baseData.colors, paddedCenterColors);
  6315. const centerColorsTextureProps = this.renderer ? this.renderer.properties.get(centerColorsTexture) : null;
  6316. if (!centerColorsTextureProps || !centerColorsTextureProps.__webglTexture) {
  6317. centerColorsTexture.needsUpdate = true;
  6318. } else {
  6319. this.updateDataTexture(paddedCenterColors, centerColorsTextureDescriptor, centerColorsTextureProps,
  6320. CENTER_COLORS_ELEMENTS_PER_TEXEL, CENTER_COLORS_ELEMENTS_PER_SPLAT, 4,
  6321. this.lastBuildSplatCount, splatCount - 1);
  6322. }
  6323. if (this.splatDataTextures.baseData.sphericalHarmonics) {
  6324. const sphericalHarmonicsComponentCount = sphericalHarmonicsTextureDesc.componentCount;
  6325. const paddedSphericalHarmonicsComponentCount = sphericalHarmonicsTextureDesc.paddedComponentCount;
  6326. const paddedSHArray = sphericalHarmonicsTextureDesc.data;
  6327. for (let c = this.lastBuildSplatCount; c < splatCount; c++) {
  6328. const srcBase = sphericalHarmonicsComponentCount * c;
  6329. const destBase = paddedSphericalHarmonicsComponentCount * c;
  6330. for (let i = 0; i < sphericalHarmonicsComponentCount; i++) {
  6331. paddedSHArray[destBase + i] = this.splatDataTextures.baseData.sphericalHarmonics[srcBase + i];
  6332. }
  6333. }
  6334. const sphericalHarmonicsTex = sphericalHarmonicsTextureDesc.texture;
  6335. const sphericalHarmonicsTextureProps = this.renderer ? this.renderer.properties.get(sphericalHarmonicsTex) : null;
  6336. if (!sphericalHarmonicsTextureProps || !sphericalHarmonicsTextureProps.__webglTexture) {
  6337. sphericalHarmonicsTex.needsUpdate = true;
  6338. } else {
  6339. const sphericalHarmonicsElementsPerTexel = 4;
  6340. let sphericalHarmonicsBytesPerElement = 4;
  6341. if (sphericalHarmonicsCompressionLevel === 1) sphericalHarmonicsBytesPerElement = 2;
  6342. else if (sphericalHarmonicsCompressionLevel === 2) sphericalHarmonicsBytesPerElement = 1;
  6343. this.updateDataTexture(paddedSHArray, sphericalHarmonicsTextureDesc, sphericalHarmonicsTextureProps,
  6344. sphericalHarmonicsElementsPerTexel, paddedSphericalHarmonicsComponentCount,
  6345. sphericalHarmonicsBytesPerElement, this.lastBuildSplatCount, splatCount - 1);
  6346. }
  6347. }
  6348. if (this.dynamicMode) {
  6349. const transformIndexesTexDesc = this.splatDataTextures['tansformIndexes'];
  6350. const paddedTransformIndexes = transformIndexesTexDesc.data;
  6351. for (let c = this.lastBuildSplatCount; c < splatCount; c++) {
  6352. paddedTransformIndexes[c] = this.globalSplatIndexToSceneIndexMap[c];
  6353. }
  6354. const transformIndexesTexture = transformIndexesTexDesc.texture;
  6355. const transformIndexesTextureProps = this.renderer ? this.renderer.properties.get(transformIndexesTexture) : null;
  6356. if (!transformIndexesTextureProps || !transformIndexesTextureProps.__webglTexture) {
  6357. transformIndexesTexture.needsUpdate = true;
  6358. } else {
  6359. this.updateDataTexture(paddedTransformIndexes, transformIndexesTexDesc, transformIndexesTextureProps, 1, 1, 1,
  6360. this.lastBuildSplatCount, splatCount - 1);
  6361. }
  6362. }
  6363. }
  6364. getTargetCovarianceCompressionLevel() {
  6365. return this.halfPrecisionCovariancesOnGPU ? 1 : 0;
  6366. }
  6367. getTargetSphericalHarmonicsCompressionLevel() {
  6368. return Math.max(1, this.getMaximumSplatBufferCompressionLevel());
  6369. }
  6370. getMaximumSplatBufferCompressionLevel() {
  6371. let maxCompressionLevel;
  6372. for (let i = 0; i < this.scenes.length; i++) {
  6373. const scene = this.getScene(i);
  6374. const splatBuffer = scene.splatBuffer;
  6375. if (i === 0 || splatBuffer.compressionLevel > maxCompressionLevel) {
  6376. maxCompressionLevel = splatBuffer.compressionLevel;
  6377. }
  6378. }
  6379. return maxCompressionLevel;
  6380. }
  6381. getMinimumSplatBufferCompressionLevel() {
  6382. let minCompressionLevel;
  6383. for (let i = 0; i < this.scenes.length; i++) {
  6384. const scene = this.getScene(i);
  6385. const splatBuffer = scene.splatBuffer;
  6386. if (i === 0 || splatBuffer.compressionLevel < minCompressionLevel) {
  6387. minCompressionLevel = splatBuffer.compressionLevel;
  6388. }
  6389. }
  6390. return minCompressionLevel;
  6391. }
  6392. static computeTextureUpdateRegion(startSplat, endSplat, textureWidth, elementsPerTexel, elementsPerSplat) {
  6393. const texelsPerSplat = elementsPerSplat / elementsPerTexel;
  6394. const startSplatTexels = startSplat * texelsPerSplat;
  6395. const startRow = Math.floor(startSplatTexels / textureWidth);
  6396. const startRowElement = startRow * textureWidth * elementsPerTexel;
  6397. const endSplatTexels = endSplat * texelsPerSplat;
  6398. const endRow = Math.floor(endSplatTexels / textureWidth);
  6399. const endRowEndElement = endRow * textureWidth * elementsPerTexel + (textureWidth * elementsPerTexel);
  6400. return {
  6401. 'dataStart': startRowElement,
  6402. 'dataEnd': endRowEndElement,
  6403. 'startRow': startRow,
  6404. 'endRow': endRow
  6405. };
  6406. }
  6407. //局部纹理刷新
  6408. updateDataTexture(paddedData, textureDesc, textureProps, elementsPerTexel, elementsPerSplat, bytesPerElement, from, to) {
  6409. const gl = this.renderer.getContext();
  6410. const updateRegion = SplatMesh.computeTextureUpdateRegion(from, to, textureDesc.size.x, elementsPerTexel, elementsPerSplat);
  6411. const updateElementCount = updateRegion.dataEnd - updateRegion.dataStart;
  6412. const updateDataView = new paddedData.constructor(paddedData.buffer,
  6413. updateRegion.dataStart * bytesPerElement, updateElementCount);
  6414. const updateHeight = updateRegion.endRow - updateRegion.startRow + 1;
  6415. const dataTexture = textureDesc.texture;
  6416. const glType = this.webGLUtils.convert(dataTexture.type);
  6417. const glFormat = this.webGLUtils.convert(dataTexture.format, dataTexture.colorSpace);
  6418. const currentTexture = gl.getParameter(gl.TEXTURE_BINDING_2D);
  6419. gl.bindTexture(gl.TEXTURE_2D, textureProps.__webglTexture);
  6420. gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, updateRegion.startRow, textureDesc.size.x, updateHeight, glFormat, glType, updateDataView);
  6421. //(target, level, xoffset, yoffset, width, height, format, type, pixels);
  6422. gl.bindTexture(gl.TEXTURE_2D, currentTexture);
  6423. }
  6424. static updateCenterColorsPaddedData(to, from, centers, colors, paddedCenterColors) {
  6425. for (let c = to; c < from; c++) {
  6426. const colorsBase = c * 4;
  6427. const centersBase = c * 3;
  6428. const centerColorsBase = c * 4;
  6429. paddedCenterColors[centerColorsBase] = rgbaArrayToInteger(colors, colorsBase);
  6430. paddedCenterColors[centerColorsBase + 1] = uintEncodedFloat(centers[centersBase]);
  6431. paddedCenterColors[centerColorsBase + 2] = uintEncodedFloat(centers[centersBase + 1]);
  6432. paddedCenterColors[centerColorsBase + 3] = uintEncodedFloat(centers[centersBase + 2]);
  6433. }
  6434. }
  6435. updateVisibleRegion(sinceLastBuildOnly) {
  6436. const splatCount = this.getSplatCount();
  6437. const tempCenter = new THREE.Vector3();
  6438. if (!sinceLastBuildOnly) {
  6439. const avgCenter = new THREE.Vector3();
  6440. this.scenes.forEach((scene) => {
  6441. avgCenter.add(scene.splatBuffer.sceneCenter);
  6442. });
  6443. avgCenter.multiplyScalar(1.0 / this.scenes.length);
  6444. this.calculatedSceneCenter.copy(avgCenter);
  6445. this.material.uniforms.sceneCenter.value.copy(this.calculatedSceneCenter);
  6446. this.material.uniformsNeedUpdate = true;
  6447. }
  6448. const startSplatFormMaxDistanceCalc = sinceLastBuildOnly ? this.lastBuildSplatCount : 0;
  6449. for (let i = startSplatFormMaxDistanceCalc; i < splatCount; i++) {
  6450. this.getSplatCenter(i, tempCenter, false);
  6451. const distFromCSceneCenter = tempCenter.sub(this.calculatedSceneCenter).length();
  6452. if (distFromCSceneCenter > this.maxSplatDistanceFromSceneCenter) this.maxSplatDistanceFromSceneCenter = distFromCSceneCenter;
  6453. }
  6454. if (this.maxSplatDistanceFromSceneCenter - this.visibleRegionBufferRadius > VISIBLE_REGION_EXPANSION_DELTA) {
  6455. this.visibleRegionBufferRadius = this.maxSplatDistanceFromSceneCenter;
  6456. this.visibleRegionRadius = Math.max(this.visibleRegionBufferRadius - VISIBLE_REGION_EXPANSION_DELTA, 0.0);
  6457. }
  6458. if (this.finalBuild) this.visibleRegionRadius = this.visibleRegionBufferRadius = this.maxSplatDistanceFromSceneCenter;
  6459. this.updateVisibleRegionFadeDistance();
  6460. }
  6461. updateVisibleRegionFadeDistance(sceneRevealMode = SceneRevealMode.Default) {
  6462. const fastFadeRate = SCENE_FADEIN_RATE_FAST;
  6463. const gradualFadeRate = SCENE_FADEIN_RATE_GRADUAL;
  6464. const defaultFadeInRate = this.finalBuild ? fastFadeRate : gradualFadeRate;
  6465. const fadeInRate = sceneRevealMode === SceneRevealMode.Default ? defaultFadeInRate : gradualFadeRate;
  6466. this.visibleRegionFadeStartRadius = (this.visibleRegionRadius - this.visibleRegionFadeStartRadius) *
  6467. fadeInRate + this.visibleRegionFadeStartRadius;
  6468. const fadeInPercentage = (this.visibleRegionBufferRadius > 0) ?
  6469. (this.visibleRegionFadeStartRadius / this.visibleRegionBufferRadius) : 0;
  6470. const fadeInComplete = fadeInPercentage > 0.99;
  6471. const shaderFadeInComplete = (fadeInComplete || sceneRevealMode === SceneRevealMode.Instant) ? 1 : 0;
  6472. this.material.uniforms.visibleRegionFadeStartRadius.value = this.visibleRegionFadeStartRadius;
  6473. this.material.uniforms.visibleRegionRadius.value = this.visibleRegionRadius;
  6474. this.material.uniforms.firstRenderTime.value = this.firstRenderTime;
  6475. this.material.uniforms.currentTime.value = performance.now();
  6476. this.material.uniforms.fadeInComplete.value = shaderFadeInComplete;
  6477. this.material.uniformsNeedUpdate = true;
  6478. this.visibleRegionChanging = !fadeInComplete;
  6479. }
  6480. /**
  6481. * Set the indexes of splats that should be rendered; should be sorted in desired render order.
  6482. * @param {Uint32Array} globalIndexes Sorted index list of splats to be rendered
  6483. * @param {number} renderSplatCount Total number of splats to be rendered. Necessary because we may not want to render
  6484. * every splat.
  6485. */
  6486. updateRenderIndexes(globalIndexes, renderSplatCount) {//仅渲染前renderSplatCount个点,在前renderSplatCount的顺序是否可以打乱?
  6487. const geometry = this.geometry;
  6488. geometry.attributes.splatIndex.set(globalIndexes);
  6489. geometry.attributes.splatIndex.needsUpdate = true;
  6490. if (renderSplatCount > 0 && this.firstRenderTime === -1) this.firstRenderTime = performance.now();
  6491. geometry.instanceCount = renderSplatCount;
  6492. }//注:假设全部点都渲染的话,是无所谓index的顺序的,可以直接第0个为0,第n个为n……
  6493. /**
  6494. * Update the transforms for each scene in this splat mesh from their individual components (position,
  6495. * quaternion, and scale)
  6496. */
  6497. updateTransforms() {
  6498. for (let i = 0; i < this.scenes.length; i++) {
  6499. const scene = this.getScene(i);
  6500. scene.updateTransform();
  6501. }
  6502. }
  6503. updateUniforms = function() {
  6504. const viewport = new THREE.Vector2();
  6505. return function(renderDimensions, cameraFocalLengthX, cameraFocalLengthY,
  6506. orthographicMode, orthographicZoom, inverseFocalAdjustment) {
  6507. const splatCount = this.getSplatCount();
  6508. if (splatCount > 0) {
  6509. viewport.set(renderDimensions.x * this.devicePixelRatio,
  6510. renderDimensions.y * this.devicePixelRatio);
  6511. this.material.uniforms.viewport.value.copy(viewport);
  6512. this.material.uniforms.basisViewport.value.set(1.0 / viewport.x, 1.0 / viewport.y);
  6513. this.material.uniforms.focal.value.set(cameraFocalLengthX, cameraFocalLengthY);
  6514. this.material.uniforms.orthographicMode.value = orthographicMode ? 1 : 0;
  6515. this.material.uniforms.orthoZoom.value = orthographicZoom;
  6516. this.material.uniforms.inverseFocalAdjustment.value = inverseFocalAdjustment;
  6517. if (this.dynamicMode) {
  6518. for (let i = 0; i < this.scenes.length; i++) {
  6519. this.material.uniforms.transforms.value[i].copy(this.getScene(i).transform);
  6520. }
  6521. }
  6522. this.material.uniformsNeedUpdate = true;
  6523. }
  6524. };
  6525. }();
  6526. setSplatScale(splatScale = 1) {
  6527. this.splatScale = splatScale;
  6528. this.material.uniforms.splatScale.value = splatScale;
  6529. this.material.uniformsNeedUpdate = true;
  6530. }
  6531. getSplatScale() {
  6532. return this.splatScale;
  6533. }
  6534. setPointCloudModeEnabled(enabled) {
  6535. this.pointCloudModeEnabled = enabled;
  6536. this.material.uniforms.pointCloudModeEnabled.value = enabled ? 1 : 0;
  6537. this.material.uniformsNeedUpdate = true;
  6538. }
  6539. getPointCloudModeEnabled() {
  6540. return this.pointCloudModeEnabled;
  6541. }
  6542. getSplatDataTextures() {
  6543. return this.splatDataTextures;
  6544. }
  6545. getSplatCount() {
  6546. return SplatMesh.getTotalSplatCountForScenes(this.scenes);
  6547. }
  6548. static getTotalSplatCountForScenes(scenes) {
  6549. let totalSplatCount = 0;
  6550. for (let scene of scenes) {
  6551. if (scene && scene.splatBuffer) totalSplatCount += scene.splatBuffer.getSplatCount();
  6552. }
  6553. return totalSplatCount;
  6554. }
  6555. static getTotalSplatCountForSplatBuffers(splatBuffers) {
  6556. let totalSplatCount = 0;
  6557. for (let splatBuffer of splatBuffers) totalSplatCount += splatBuffer.getSplatCount();
  6558. return totalSplatCount;
  6559. }
  6560. getMaxSplatCount() {
  6561. return SplatMesh.getTotalMaxSplatCountForScenes(this.scenes);
  6562. }
  6563. static getTotalMaxSplatCountForScenes(scenes) {
  6564. let totalSplatCount = 0;
  6565. for (let scene of scenes) {
  6566. if (scene && scene.splatBuffer) totalSplatCount += scene.splatBuffer.getMaxSplatCount();
  6567. }
  6568. return totalSplatCount;
  6569. }
  6570. static getTotalMaxSplatCountForSplatBuffers(splatBuffers) {
  6571. let totalSplatCount = 0;
  6572. for (let splatBuffer of splatBuffers) totalSplatCount += splatBuffer.getMaxSplatCount();
  6573. return totalSplatCount;
  6574. }
  6575. disposeDistancesComputationGPUResources() {
  6576. if (!this.renderer) return;
  6577. const gl = this.renderer.getContext();
  6578. if (this.distancesTransformFeedback.vao) {
  6579. gl.deleteVertexArray(this.distancesTransformFeedback.vao);
  6580. this.distancesTransformFeedback.vao = null;
  6581. }
  6582. if (this.distancesTransformFeedback.program) {
  6583. gl.deleteProgram(this.distancesTransformFeedback.program);
  6584. gl.deleteShader(this.distancesTransformFeedback.vertexShader);
  6585. gl.deleteShader(this.distancesTransformFeedback.fragmentShader);
  6586. this.distancesTransformFeedback.program = null;
  6587. this.distancesTransformFeedback.vertexShader = null;
  6588. this.distancesTransformFeedback.fragmentShader = null;
  6589. }
  6590. this.disposeDistancesComputationGPUBufferResources();
  6591. if (this.distancesTransformFeedback.id) {
  6592. gl.deleteTransformFeedback(this.distancesTransformFeedback.id);
  6593. this.distancesTransformFeedback.id = null;
  6594. }
  6595. }
  6596. disposeDistancesComputationGPUBufferResources() {
  6597. if (!this.renderer) return;
  6598. const gl = this.renderer.getContext();
  6599. if (this.distancesTransformFeedback.centersBuffer) {
  6600. this.distancesTransformFeedback.centersBuffer = null;
  6601. gl.deleteBuffer(this.distancesTransformFeedback.centersBuffer);
  6602. }
  6603. if (this.distancesTransformFeedback.outDistancesBuffer) {
  6604. gl.deleteBuffer(this.distancesTransformFeedback.outDistancesBuffer);
  6605. this.distancesTransformFeedback.outDistancesBuffer = null;
  6606. }
  6607. }
  6608. /**
  6609. * Set the Three.js renderer used by this splat mesh
  6610. * @param {THREE.WebGLRenderer} renderer Instance of THREE.WebGLRenderer
  6611. */
  6612. setRenderer(renderer) {
  6613. if (renderer !== this.renderer) {
  6614. this.renderer = renderer;
  6615. const gl = this.renderer.getContext();
  6616. const extensions = new WebGLExtensions(gl);
  6617. const capabilities = new WebGLCapabilities(gl, extensions, {});
  6618. extensions.init(capabilities);
  6619. this.webGLUtils = new THREE.WebGLUtils(gl, extensions, capabilities);
  6620. if (this.enableDistancesComputationOnGPU && this.getSplatCount() > 0) {
  6621. this.setupDistancesComputationTransformFeedback();
  6622. const { centers, sceneIndexes } = this.getDataForDistancesComputation(0, this.getSplatCount() - 1);
  6623. this.refreshGPUBuffersForDistancesComputation(centers, sceneIndexes);
  6624. }
  6625. }
  6626. }
  6627. setupDistancesComputationTransformFeedback = function() {
  6628. let currentMaxSplatCount;
  6629. return function() {
  6630. const maxSplatCount = this.getMaxSplatCount();
  6631. if (!this.renderer) return;
  6632. const rebuildGPUObjects = (this.lastRenderer !== this.renderer);
  6633. const rebuildBuffers = currentMaxSplatCount !== maxSplatCount;
  6634. if (!rebuildGPUObjects && !rebuildBuffers) return;
  6635. if (rebuildGPUObjects) {
  6636. this.disposeDistancesComputationGPUResources();
  6637. } else if (rebuildBuffers) {
  6638. this.disposeDistancesComputationGPUBufferResources();
  6639. }
  6640. const gl = this.renderer.getContext();
  6641. const createShader = (gl, type, source) => {
  6642. const shader = gl.createShader(type);
  6643. if (!shader) {
  6644. console.error('Fatal error: gl could not create a shader object.');
  6645. return null;
  6646. }
  6647. gl.shaderSource(shader, source);
  6648. gl.compileShader(shader);
  6649. const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  6650. if (!compiled) {
  6651. let typeName = 'unknown';
  6652. if (type === gl.VERTEX_SHADER) typeName = 'vertex shader';
  6653. else if (type === gl.FRAGMENT_SHADER) typeName = 'fragement shader';
  6654. const errors = gl.getShaderInfoLog(shader);
  6655. console.error('Failed to compile ' + typeName + ' with these errors:' + errors);
  6656. gl.deleteShader(shader);
  6657. return null;
  6658. }
  6659. return shader;
  6660. };
  6661. let vsSource;
  6662. if (this.integerBasedDistancesComputation) {
  6663. vsSource =
  6664. `#version 300 es
  6665. in ivec4 center;
  6666. flat out int distance;`;
  6667. if (this.dynamicMode) {
  6668. vsSource += `
  6669. in uint transformIndex;
  6670. uniform ivec4 transforms[${Constants.MaxScenes}];
  6671. void main(void) {
  6672. ivec4 transform = transforms[transformIndex];
  6673. distance = center.x * transform.x + center.y * transform.y + center.z * transform.z + transform.w * center.w;
  6674. }
  6675. `;
  6676. } else {
  6677. vsSource += `
  6678. uniform ivec3 modelViewProj;
  6679. void main(void) {
  6680. distance = center.x * modelViewProj.x + center.y * modelViewProj.y + center.z * modelViewProj.z;
  6681. }
  6682. `;
  6683. }
  6684. } else {
  6685. vsSource =
  6686. `#version 300 es
  6687. in vec4 center;
  6688. flat out float distance;`;
  6689. if (this.dynamicMode) {
  6690. vsSource += `
  6691. in uint transformIndex;
  6692. uniform mat4 transforms[${Constants.MaxScenes}];
  6693. void main(void) {
  6694. vec4 transformedCenter = transforms[transformIndex] * vec4(center.xyz, 1.0);
  6695. distance = transformedCenter.z;
  6696. }
  6697. `;
  6698. } else {
  6699. vsSource += `
  6700. uniform vec3 modelViewProj;
  6701. void main(void) {
  6702. distance = center.x * modelViewProj.x + center.y * modelViewProj.y + center.z * modelViewProj.z;
  6703. }
  6704. `;
  6705. }
  6706. }
  6707. const fsSource =
  6708. `#version 300 es
  6709. precision lowp float;
  6710. out vec4 fragColor;
  6711. void main(){}
  6712. `;
  6713. const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
  6714. const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
  6715. const currentProgramDeleted = currentProgram ? gl.getProgramParameter(currentProgram, gl.DELETE_STATUS) : false;
  6716. if (rebuildGPUObjects) {
  6717. this.distancesTransformFeedback.vao = gl.createVertexArray();
  6718. }
  6719. gl.bindVertexArray(this.distancesTransformFeedback.vao);
  6720. if (rebuildGPUObjects) {
  6721. const program = gl.createProgram();
  6722. const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
  6723. const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
  6724. if (!vertexShader || !fragmentShader) {
  6725. throw new Error('Could not compile shaders for distances computation on GPU.');
  6726. }
  6727. gl.attachShader(program, vertexShader);
  6728. gl.attachShader(program, fragmentShader);
  6729. gl.transformFeedbackVaryings(program, ['distance'], gl.SEPARATE_ATTRIBS);
  6730. gl.linkProgram(program);
  6731. const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  6732. if (!linked) {
  6733. const error = gl.getProgramInfoLog(program);
  6734. console.error('Fatal error: Failed to link program: ' + error);
  6735. gl.deleteProgram(program);
  6736. gl.deleteShader(fragmentShader);
  6737. gl.deleteShader(vertexShader);
  6738. throw new Error('Could not link shaders for distances computation on GPU.');
  6739. }
  6740. this.distancesTransformFeedback.program = program;
  6741. this.distancesTransformFeedback.vertexShader = vertexShader;
  6742. this.distancesTransformFeedback.vertexShader = fragmentShader;
  6743. }
  6744. gl.useProgram(this.distancesTransformFeedback.program);
  6745. this.distancesTransformFeedback.centersLoc =
  6746. gl.getAttribLocation(this.distancesTransformFeedback.program, 'center');
  6747. if (this.dynamicMode) {
  6748. this.distancesTransformFeedback.transformIndexesLoc =
  6749. gl.getAttribLocation(this.distancesTransformFeedback.program, 'transformIndex');
  6750. for (let i = 0; i < this.scenes.length; i++) {
  6751. this.distancesTransformFeedback.transformsLocs[i] =
  6752. gl.getUniformLocation(this.distancesTransformFeedback.program, `transforms[${i}]`);
  6753. }
  6754. } else {
  6755. this.distancesTransformFeedback.modelViewProjLoc =
  6756. gl.getUniformLocation(this.distancesTransformFeedback.program, 'modelViewProj');
  6757. }
  6758. if (rebuildGPUObjects || rebuildBuffers) {
  6759. this.distancesTransformFeedback.centersBuffer = gl.createBuffer();
  6760. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.centersBuffer);
  6761. gl.enableVertexAttribArray(this.distancesTransformFeedback.centersLoc);
  6762. if (this.integerBasedDistancesComputation) {
  6763. gl.vertexAttribIPointer(this.distancesTransformFeedback.centersLoc, 4, gl.INT, 0, 0);
  6764. } else {
  6765. gl.vertexAttribPointer(this.distancesTransformFeedback.centersLoc, 4, gl.FLOAT, false, 0, 0);
  6766. }
  6767. if (this.dynamicMode) {
  6768. this.distancesTransformFeedback.transformIndexesBuffer = gl.createBuffer();
  6769. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.transformIndexesBuffer);
  6770. gl.enableVertexAttribArray(this.distancesTransformFeedback.transformIndexesLoc);
  6771. gl.vertexAttribIPointer(this.distancesTransformFeedback.transformIndexesLoc, 1, gl.UNSIGNED_INT, 0, 0);
  6772. }
  6773. }
  6774. if (rebuildGPUObjects || rebuildBuffers) {
  6775. this.distancesTransformFeedback.outDistancesBuffer = gl.createBuffer();
  6776. }
  6777. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.outDistancesBuffer);
  6778. gl.bufferData(gl.ARRAY_BUFFER, maxSplatCount * 4, gl.STATIC_READ);
  6779. if (rebuildGPUObjects) {
  6780. this.distancesTransformFeedback.id = gl.createTransformFeedback();
  6781. }
  6782. gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, this.distancesTransformFeedback.id);
  6783. gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, this.distancesTransformFeedback.outDistancesBuffer);
  6784. if (currentProgram && currentProgramDeleted !== true) gl.useProgram(currentProgram);
  6785. if (currentVao) gl.bindVertexArray(currentVao);
  6786. this.lastRenderer = this.renderer;
  6787. currentMaxSplatCount = maxSplatCount;
  6788. };
  6789. }();
  6790. /**
  6791. * Refresh GPU buffers used for computing splat distances with centers data from the scenes for this mesh.
  6792. * @param {boolean} isUpdate Specify whether or not to update the GPU buffer or to initialize & fill
  6793. * @param {Array<number>} centers The splat centers data
  6794. * @param {number} offsetSplats Offset in the GPU buffer at which to start updating data, specified in splats
  6795. */
  6796. updateGPUCentersBufferForDistancesComputation(isUpdate, centers, offsetSplats) {
  6797. if (!this.renderer) return;
  6798. const gl = this.renderer.getContext();
  6799. const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
  6800. gl.bindVertexArray(this.distancesTransformFeedback.vao);
  6801. const ArrayType = this.integerBasedDistancesComputation ? Uint32Array : Float32Array;
  6802. const attributeBytesPerCenter = 16;
  6803. const subBufferOffset = offsetSplats * attributeBytesPerCenter;
  6804. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.centersBuffer);
  6805. if (isUpdate) {
  6806. gl.bufferSubData(gl.ARRAY_BUFFER, subBufferOffset, centers);
  6807. } else {
  6808. const maxArray = new ArrayType(this.getMaxSplatCount() * attributeBytesPerCenter);
  6809. maxArray.set(centers);
  6810. gl.bufferData(gl.ARRAY_BUFFER, maxArray, gl.STATIC_DRAW);
  6811. }
  6812. gl.bindBuffer(gl.ARRAY_BUFFER, null);
  6813. if (currentVao) gl.bindVertexArray(currentVao);
  6814. }
  6815. /**
  6816. * Refresh GPU buffers used for pre-computing splat distances with centers data from the scenes for this mesh.
  6817. * @param {boolean} isUpdate Specify whether or not to update the GPU buffer or to initialize & fill
  6818. * @param {Array<number>} transformIndexes The splat transform indexes
  6819. * @param {number} offsetSplats Offset in the GPU buffer at which to start updating data, specified in splats
  6820. */
  6821. updateGPUTransformIndexesBufferForDistancesComputation(isUpdate, transformIndexes, offsetSplats) {
  6822. if (!this.renderer || !this.dynamicMode) return;
  6823. const gl = this.renderer.getContext();
  6824. const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
  6825. gl.bindVertexArray(this.distancesTransformFeedback.vao);
  6826. const subBufferOffset = offsetSplats * 4;
  6827. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.transformIndexesBuffer);
  6828. if (isUpdate) {
  6829. gl.bufferSubData(gl.ARRAY_BUFFER, subBufferOffset, transformIndexes);
  6830. } else {
  6831. const maxArray = new Uint32Array(this.getMaxSplatCount() * 4);
  6832. maxArray.set(transformIndexes);
  6833. gl.bufferData(gl.ARRAY_BUFFER, maxArray, gl.STATIC_DRAW);
  6834. }
  6835. gl.bindBuffer(gl.ARRAY_BUFFER, null);
  6836. if (currentVao) gl.bindVertexArray(currentVao);
  6837. }
  6838. /**
  6839. * Get a typed array containing a mapping from global splat indexes to their scene index.
  6840. * @param {number} start Starting splat index to store
  6841. * @param {number} end Ending splat index to store
  6842. * @return {Uint32Array}
  6843. */
  6844. getSceneIndexes(start, end) {
  6845. let sceneIndexes;
  6846. const fillCount = end - start + 1;
  6847. sceneIndexes = new Uint32Array(fillCount);
  6848. for (let i = start; i <= end; i++) {
  6849. sceneIndexes[i] = this.globalSplatIndexToSceneIndexMap[i];
  6850. }
  6851. return sceneIndexes;
  6852. }
  6853. /**
  6854. * Fill 'array' with the transforms for each scene in this splat mesh.
  6855. * @param {Array} array Empty array to be filled with scene transforms. If not empty, contents will be overwritten.
  6856. */
  6857. fillTransformsArray = function() {
  6858. const tempArray = [];
  6859. return function(array) {
  6860. if (tempArray.length !== array.length) tempArray.length = array.length;
  6861. for (let i = 0; i < this.scenes.length; i++) {
  6862. const sceneTransform = this.getScene(i).transform;
  6863. const sceneTransformElements = sceneTransform.elements;
  6864. for (let j = 0; j < 16; j++) {
  6865. tempArray[i * 16 + j] = sceneTransformElements[j];
  6866. }
  6867. }
  6868. array.set(tempArray);
  6869. };
  6870. }();
  6871. computeDistancesOnGPU = function() {
  6872. const tempMatrix = new THREE.Matrix4();
  6873. return function(modelViewProjMatrix, outComputedDistances) {
  6874. if (!this.renderer) return;
  6875. // console.time("gpu_compute_distances");
  6876. const gl = this.renderer.getContext();
  6877. const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
  6878. const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
  6879. const currentProgramDeleted = currentProgram ? gl.getProgramParameter(currentProgram, gl.DELETE_STATUS) : false;
  6880. gl.bindVertexArray(this.distancesTransformFeedback.vao);
  6881. gl.useProgram(this.distancesTransformFeedback.program);
  6882. gl.enable(gl.RASTERIZER_DISCARD);
  6883. if (this.dynamicMode) {
  6884. for (let i = 0; i < this.scenes.length; i++) {
  6885. tempMatrix.copy(this.getScene(i).transform);
  6886. tempMatrix.premultiply(modelViewProjMatrix);
  6887. if (this.integerBasedDistancesComputation) {
  6888. const iTempMatrix = SplatMesh.getIntegerMatrixArray(tempMatrix);
  6889. const iTransform = [iTempMatrix[2], iTempMatrix[6], iTempMatrix[10], iTempMatrix[14]];
  6890. gl.uniform4i(this.distancesTransformFeedback.transformsLocs[i], iTransform[0], iTransform[1],
  6891. iTransform[2], iTransform[3]);
  6892. } else {
  6893. gl.uniformMatrix4fv(this.distancesTransformFeedback.transformsLocs[i], false, tempMatrix.elements);
  6894. }
  6895. }
  6896. } else {
  6897. if (this.integerBasedDistancesComputation) {
  6898. const iViewProjMatrix = SplatMesh.getIntegerMatrixArray(modelViewProjMatrix);
  6899. const iViewProj = [iViewProjMatrix[2], iViewProjMatrix[6], iViewProjMatrix[10]];
  6900. gl.uniform3i(this.distancesTransformFeedback.modelViewProjLoc, iViewProj[0], iViewProj[1], iViewProj[2]);
  6901. } else {
  6902. const viewProj = [modelViewProjMatrix.elements[2], modelViewProjMatrix.elements[6], modelViewProjMatrix.elements[10]];
  6903. gl.uniform3f(this.distancesTransformFeedback.modelViewProjLoc, viewProj[0], viewProj[1], viewProj[2]);
  6904. }
  6905. }
  6906. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.centersBuffer);
  6907. gl.enableVertexAttribArray(this.distancesTransformFeedback.centersLoc);
  6908. if (this.integerBasedDistancesComputation) {
  6909. gl.vertexAttribIPointer(this.distancesTransformFeedback.centersLoc, 4, gl.INT, 0, 0);
  6910. } else {
  6911. gl.vertexAttribPointer(this.distancesTransformFeedback.centersLoc, 4, gl.FLOAT, false, 0, 0);
  6912. }
  6913. if (this.dynamicMode) {
  6914. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.transformIndexesBuffer);
  6915. gl.enableVertexAttribArray(this.distancesTransformFeedback.transformIndexesLoc);
  6916. gl.vertexAttribIPointer(this.distancesTransformFeedback.transformIndexesLoc, 1, gl.UNSIGNED_INT, 0, 0);
  6917. }
  6918. gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, this.distancesTransformFeedback.id);
  6919. gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, this.distancesTransformFeedback.outDistancesBuffer);
  6920. gl.beginTransformFeedback(gl.POINTS);
  6921. gl.drawArrays(gl.POINTS, 0, this.getSplatCount());
  6922. gl.endTransformFeedback();
  6923. gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
  6924. gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
  6925. gl.disable(gl.RASTERIZER_DISCARD);
  6926. const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
  6927. gl.flush();
  6928. const promise = new Promise((resolve) => {
  6929. const checkSync = () => {
  6930. if (this.disposed) {
  6931. resolve();
  6932. } else {
  6933. const timeout = 0;
  6934. const bitflags = 0;
  6935. const status = gl.clientWaitSync(sync, bitflags, timeout);
  6936. switch (status) {
  6937. case gl.TIMEOUT_EXPIRED:
  6938. this.computeDistancesOnGPUSyncTimeout = setTimeout(checkSync);
  6939. return this.computeDistancesOnGPUSyncTimeout;
  6940. case gl.WAIT_FAILED:
  6941. throw new Error('should never get here');
  6942. default:
  6943. this.computeDistancesOnGPUSyncTimeout = null;
  6944. gl.deleteSync(sync);
  6945. const currentVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
  6946. gl.bindVertexArray(this.distancesTransformFeedback.vao);
  6947. gl.bindBuffer(gl.ARRAY_BUFFER, this.distancesTransformFeedback.outDistancesBuffer);
  6948. gl.getBufferSubData(gl.ARRAY_BUFFER, 0, outComputedDistances);
  6949. gl.bindBuffer(gl.ARRAY_BUFFER, null);
  6950. if (currentVao) gl.bindVertexArray(currentVao);
  6951. // console.timeEnd("gpu_compute_distances");
  6952. resolve();
  6953. }
  6954. }
  6955. };
  6956. this.computeDistancesOnGPUSyncTimeout = setTimeout(checkSync);
  6957. });
  6958. if (currentProgram && currentProgramDeleted !== true) gl.useProgram(currentProgram);
  6959. if (currentVao) gl.bindVertexArray(currentVao);
  6960. return promise;
  6961. };
  6962. }();
  6963. /**
  6964. * Given a global splat index, return corresponding local data (splat buffer, index of splat in that splat
  6965. * buffer, and the corresponding transform)
  6966. * @param {number} globalIndex Global splat index
  6967. * @param {object} paramsObj Object in which to store local data
  6968. * @param {boolean} returnSceneTransform By default, the transform of the scene to which the splat at 'globalIndex' belongs will be
  6969. * returned via the 'sceneTransform' property of 'paramsObj' only if the splat mesh is static.
  6970. * If 'returnSceneTransform' is true, the 'sceneTransform' property will always contain the scene
  6971. * transform, and if 'returnSceneTransform' is false, the 'sceneTransform' property will always
  6972. * be null.
  6973. */
  6974. getLocalSplatParameters(globalIndex, paramsObj, returnSceneTransform) {
  6975. if (returnSceneTransform === undefined || returnSceneTransform === null) {
  6976. returnSceneTransform = this.dynamicMode ? false : true;
  6977. }
  6978. paramsObj.splatBuffer = this.getSplatBufferForSplat(globalIndex);
  6979. paramsObj.localIndex = this.getSplatLocalIndex(globalIndex);
  6980. paramsObj.sceneTransform = returnSceneTransform ? this.getSceneTransformForSplat(globalIndex) : null;
  6981. }
  6982. /**
  6983. * Fill arrays with splat data and apply transforms if appropriate. Each array is optional.
  6984. * @param {Float32Array} covariances Target storage for splat covariances
  6985. * @param {Float32Array} centers Target storage for splat centers
  6986. * @param {Uint8Array} colors Target storage for splat colors
  6987. * @param {Float32Array} sphericalHarmonics Target storage for spherical harmonics
  6988. * @param {boolean} applySceneTransform By default, scene transforms are applied to relevant splat data only if the splat mesh is
  6989. * static. If 'applySceneTransform' is true, scene transforms will always be applied and if
  6990. * it is false, they will never be applied. If undefined, the default behavior will apply.
  6991. * @param {number} covarianceCompressionLevel The compression level for covariances in the destination array
  6992. * @param {number} sphericalHarmonicsCompressionLevel The compression level for spherical harmonics in the destination array
  6993. * @param {number} srcStart The start location from which to pull source data
  6994. * @param {number} srcEnd The end location from which to pull source data
  6995. * @param {number} destStart The start location from which to write data
  6996. */
  6997. fillSplatDataArrays(covariances, centers, colors, sphericalHarmonics, applySceneTransform,
  6998. covarianceCompressionLevel = 0, sphericalHarmonicsCompressionLevel = 1, srcStart, srcEnd, destStart = 0) {
  6999. for (let i = 0; i < this.scenes.length; i++) {
  7000. if (applySceneTransform === undefined || applySceneTransform === null) {
  7001. applySceneTransform = this.dynamicMode ? false : true;
  7002. }
  7003. const scene = this.getScene(i);
  7004. const splatBuffer = scene.splatBuffer;
  7005. const sceneTransform = applySceneTransform ? scene.transform : null;
  7006. if (covariances) {
  7007. splatBuffer.fillSplatCovarianceArray(covariances, sceneTransform,
  7008. srcStart, srcEnd, destStart, covarianceCompressionLevel);
  7009. }
  7010. if (centers) splatBuffer.fillSplatCenterArray(centers, sceneTransform, srcStart, srcEnd, destStart);
  7011. if (colors) splatBuffer.fillSplatColorArray(colors, scene.minimumAlpha, srcStart, srcEnd, destStart);
  7012. if (sphericalHarmonics) {
  7013. splatBuffer.fillSphericalHarmonicsArray(sphericalHarmonics, this.minSphericalHarmonicsDegree,
  7014. sceneTransform, srcStart, srcEnd, destStart, sphericalHarmonicsCompressionLevel);
  7015. }
  7016. destStart += splatBuffer.getSplatCount();
  7017. }
  7018. }
  7019. /**
  7020. * Convert splat centers, which are floating point values, to an array of integers and multiply
  7021. * each by 1000. Centers will get transformed as appropriate before conversion to integer.
  7022. * @param {number} start The index at which to start retrieving data
  7023. * @param {number} end The index at which to stop retrieving data
  7024. * @param {boolean} padFour Enforce alignment of 4 by inserting a 1 after every 3 values
  7025. * @return {Int32Array}
  7026. */
  7027. getIntegerCenters(start, end, padFour = false) {
  7028. const splatCount = end - start + 1;
  7029. const floatCenters = new Float32Array(splatCount * 3);
  7030. this.fillSplatDataArrays(null, floatCenters, null, null, undefined, undefined, undefined, start);
  7031. let intCenters;
  7032. let componentCount = padFour ? 4 : 3;
  7033. intCenters = new Int32Array(splatCount * componentCount);
  7034. for (let i = 0; i < splatCount; i++) {
  7035. for (let t = 0; t < 3; t++) {
  7036. intCenters[i * componentCount + t] = Math.round(floatCenters[i * 3 + t] * 1000.0);
  7037. }
  7038. if (padFour) intCenters[i * componentCount + 3] = 1000;
  7039. }
  7040. return intCenters;
  7041. }
  7042. /**
  7043. * Returns an array of splat centers, transformed as appropriate, optionally padded.
  7044. * @param {number} start The index at which to start retrieving data
  7045. * @param {number} end The index at which to stop retrieving data
  7046. * @param {boolean} padFour Enforce alignment of 4 by inserting a 1 after every 3 values
  7047. * @return {Float32Array}
  7048. */
  7049. getFloatCenters(start, end, padFour = false) {
  7050. const splatCount = end - start + 1;
  7051. const floatCenters = new Float32Array(splatCount * 3);
  7052. this.fillSplatDataArrays(null, floatCenters, null, null, undefined, undefined, undefined, start);
  7053. if (!padFour) return floatCenters;
  7054. let paddedFloatCenters = new Float32Array(splatCount * 4);
  7055. for (let i = 0; i < splatCount; i++) {
  7056. for (let t = 0; t < 3; t++) {
  7057. paddedFloatCenters[i * 4 + t] = floatCenters[i * 3 + t];
  7058. }
  7059. paddedFloatCenters[i * 4 + 3] = 1.0;
  7060. }
  7061. return paddedFloatCenters;
  7062. }
  7063. /**
  7064. * Get the center for a splat, transformed as appropriate.
  7065. * @param {number} globalIndex Global index of splat
  7066. * @param {THREE.Vector3} outCenter THREE.Vector3 instance in which to store splat center
  7067. * @param {boolean} applySceneTransform By default, if the splat mesh is static, the transform of the scene to which the splat at
  7068. * 'globalIndex' belongs will be applied to the splat center. If 'applySceneTransform' is true,
  7069. * the scene transform will always be applied and if 'applySceneTransform' is false, the
  7070. * scene transform will never be applied. If undefined, the default behavior will apply.
  7071. */
  7072. getSplatCenter = function() {
  7073. const paramsObj = {};
  7074. return function(globalIndex, outCenter, applySceneTransform) {
  7075. this.getLocalSplatParameters(globalIndex, paramsObj, applySceneTransform);
  7076. paramsObj.splatBuffer.getSplatCenter(paramsObj.localIndex, outCenter, paramsObj.sceneTransform);
  7077. };
  7078. }();
  7079. /**
  7080. * Get the scale and rotation for a splat, transformed as appropriate.
  7081. * @param {number} globalIndex Global index of splat
  7082. * @param {THREE.Vector3} outScale THREE.Vector3 instance in which to store splat scale
  7083. * @param {THREE.Quaternion} outRotation THREE.Quaternion instance in which to store splat rotation
  7084. * @param {boolean} applySceneTransform By default, if the splat mesh is static, the transform of the scene to which the splat at
  7085. * 'globalIndex' belongs will be applied to the splat scale and rotation. If
  7086. * 'applySceneTransform' is true, the scene transform will always be applied and if
  7087. * 'applySceneTransform' is false, the scene transform will never be applied. If undefined,
  7088. * the default behavior will apply.
  7089. */
  7090. getSplatScaleAndRotation = function() {
  7091. const paramsObj = {};
  7092. return function(globalIndex, outScale, outRotation, applySceneTransform) {
  7093. this.getLocalSplatParameters(globalIndex, paramsObj, applySceneTransform);
  7094. paramsObj.splatBuffer.getSplatScaleAndRotation(paramsObj.localIndex, outScale, outRotation, paramsObj.sceneTransform);
  7095. };
  7096. }();
  7097. /**
  7098. * Get the color for a splat.
  7099. * @param {number} globalIndex Global index of splat
  7100. * @param {THREE.Vector4} outColor THREE.Vector4 instance in which to store splat color
  7101. */
  7102. getSplatColor = function() {
  7103. const paramsObj = {};
  7104. return function(globalIndex, outColor) {
  7105. this.getLocalSplatParameters(globalIndex, paramsObj);
  7106. paramsObj.splatBuffer.getSplatColor(paramsObj.localIndex, outColor);
  7107. };
  7108. }();
  7109. /**
  7110. * Store the transform of the scene at 'sceneIndex' in 'outTransform'.
  7111. * @param {number} sceneIndex Index of the desired scene
  7112. * @param {THREE.Matrix4} outTransform Instance of THREE.Matrix4 in which to store the scene's transform
  7113. */
  7114. getSceneTransform(sceneIndex, outTransform) {
  7115. const scene = this.getScene(sceneIndex);
  7116. scene.updateTransform();
  7117. outTransform.copy(scene.transform);
  7118. }
  7119. /**
  7120. * Get the scene at 'sceneIndex'.
  7121. * @param {number} sceneIndex Index of the desired scene
  7122. * @return {SplatScene}
  7123. */
  7124. getScene(sceneIndex) {
  7125. if (sceneIndex < 0 || sceneIndex >= this.scenes.length) {
  7126. throw new Error('SplatMesh::getScene() -> Invalid scene index.');
  7127. }
  7128. return this.scenes[sceneIndex];
  7129. }
  7130. getSplatBufferForSplat(globalIndex) {
  7131. return this.getScene(this.globalSplatIndexToSceneIndexMap[globalIndex]).splatBuffer;
  7132. }
  7133. getSceneIndexForSplat(globalIndex) {
  7134. return this.globalSplatIndexToSceneIndexMap[globalIndex];
  7135. }
  7136. getSceneTransformForSplat(globalIndex) {
  7137. return this.getScene(this.globalSplatIndexToSceneIndexMap[globalIndex]).transform;
  7138. }
  7139. getSplatLocalIndex(globalIndex) {
  7140. return this.globalSplatIndexToLocalSplatIndexMap[globalIndex];
  7141. }
  7142. static getIntegerMatrixArray(matrix) {
  7143. const matrixElements = matrix.elements;
  7144. const intMatrixArray = [];
  7145. for (let i = 0; i < 16; i++) {
  7146. intMatrixArray[i] = Math.round(matrixElements[i] * 1000.0);
  7147. }
  7148. return intMatrixArray;
  7149. }
  7150. }
  7151. var SorterWasm = "AGFzbQEAAAAADAZkeWxpbmsAAAAAAAEbA2AAAGAQf39/f39/f39/f39/f39/fwBgAAF/AhIBA2VudgZtZW1vcnkCAwCAgAQDBAMAAQIHOQMRX193YXNtX2NhbGxfY3RvcnMAAAtzb3J0SW5kZXhlcwABE2Vtc2NyaXB0ZW5fdGxzX2luaXQAAgrHEAMDAAELuxAFAXwDewJ/A30CfiALIAprIQwCQCAOBEAgDQRAQfj///8HIQ5BiICAgHghDSALIAxNDQIgDCEBA0AgAyABQQJ0IgVqIAIgACAFaigCAEECdGooAgAiBTYCACAFIA4gBSAOSBshDiAFIA0gBSANShshDSABQQFqIgEgC0cNAAsMAgsgDwRAQfj///8HIQ5BiICAgHghDSALIAxNDQJBfyEPIAwhAgNAIA8gByAAIAJBAnQiFGooAgAiFUECdGooAgAiCkcEQAJ+IAX9CQIIIAggCkEGdGoiD/0JAgAgDyoCEP0gASAPKgIg/SACIA8qAjD9IAP95gEgBf0JAhggD/0JAgQgDyoCFP0gASAPKgIk/SACIA8qAjT9IAP95gH95AEgBf0JAiggD/0JAgggDyoCGP0gASAPKgIo/SACIA8qAjj9IAP95gH95AEgBf0JAjggD/0JAgwgDyoCHP0gASAPKgIs/SACIA8qAjz9IAP95gH95AEiEf0fArv9FCAR/R8Du/0iAf0MAAAAAABAj0AAAAAAAECPQCIS/fIBIhP9IQEiEJlEAAAAAAAA4ENjBEAgELAMAQtCgICAgICAgICAfwshGQJ+IBP9IQAiEJlEAAAAAAAA4ENjBEAgELAMAQtCgICAgICAgICAfwv9EiETAn4gEf0fALv9FCAR/R8Bu/0iASAS/fIBIhH9IQEiEJlEAAAAAAAA4ENjBEAgELAMAQtCgICAgICAgICAfwshGiATIBn9HgEhEgJ+IBH9IQAiEJlEAAAAAAAA4ENjBEAgELAMAQtCgICAgICAgICAfwv9EiAa/R4BIBL9DQABAgMICQoLEBESExgZGhshEiAKIQ8LIAMgFGogASAVQQR0av0AAAAgEv21ASIR/RsAIBH9GwFqIBH9GwJqIBH9GwNqIgo2AgAgCiAOIAogDkgbIQ4gCiANIAogDUobIQ0gAkEBaiICIAtHDQALDAILAn8gBSoCGLtEAAAAAABAj0CiIhCZRAAAAAAAAOBBYwRAIBCqDAELQYCAgIB4CyEKAn8gBSoCCLtEAAAAAABAj0CiIhCZRAAAAAAAAOBBYwRAIBCqDAELQYCAgIB4CyECAn8gBSoCKLtEAAAAAABAj0CiIhCZRAAAAAAAAOBBYwRAIBCqDAELQYCAgIB4CyEFQfj///8HIQ5BiICAgHghDSALIAxNDQEgAv0RIAr9HAEgBf0cAiESIAwhBQNAIAMgBUECdCICaiABIAAgAmooAgBBBHRq/QAAACAS/bUBIhH9GwAgEf0bAWogEf0bAmoiAjYCACACIA4gAiAOSBshDiACIA0gAiANShshDSAFQQFqIgUgC0cNAAsMAQsgDQRAQfj///8HIQ5BiICAgHghDSALIAxNDQEgDCEBA0AgAyABQQJ0IgVqAn8gAiAAIAVqKAIAQQJ0aioCALtEAAAAAAAAsECiIhCZRAAAAAAAAOBBYwRAIBCqDAELQYCAgIB4CyIKNgIAIAogDiAKIA5IGyEOIAogDSAKIA1KGyENIAFBAWoiASALRw0ACwwBCwJAIA9FBEAgCyAMSw0BQYiAgIB4IQ1B+P///wchDgwCC0H4////ByEOQYiAgIB4IQ0gCyAMTQ0BQX8hDyAMIQIDQCAPIAcgACACQQJ0IhRqKAIAQQJ0IhVqKAIAIgpHBEAgBf0JAgggCCAKQQZ0aiIP/QkCACAPKgIQ/SABIA8qAiD9IAIgDyoCMP0gA/3mASAF/QkCGCAP/QkCBCAPKgIU/SABIA8qAiT9IAIgDyoCNP0gA/3mAf3kASAF/QkCKCAP/QkCCCAPKgIY/SABIA8qAij9IAIgDyoCOP0gA/3mAf3kASAF/QkCOCAP/QkCDCAPKgIc/SABIA8qAiz9IAIgDyoCPP0gA/3mAf3kASERIAohDwsgAyAUagJ/IBEgASAVQQJ0IgpqKQIA/RL95gEiEv0fACAS/R8BkiARIBH9DQgJCgsMDQ4PAAAAAAAAAAAgASAKQQhyaikCAP0S/eYBIhL9HwCSIBL9HwGSu0QAAAAAAACwQKIiEJlEAAAAAAAA4EFjBEAgEKoMAQtBgICAgHgLIgo2AgAgCiAOIAogDkgbIQ4gCiANIAogDUobIQ0gAkEBaiICIAtHDQALDAELIAUqAighFiAFKgIYIRcgBSoCCCEYQfj///8HIQ5BiICAgHghDSAMIQUDQAJ/IBggASAAIAVBAnQiB2ooAgBBBHRqIgIqAgCUIBcgAioCBJSSIBYgAioCCJSSu0QAAAAAAACwQKIiEJlEAAAAAAAA4EFjBEAgEKoMAQtBgICAgHgLIQogAyAHaiAKNgIAIAogDiAKIA5IGyEOIAogDSAKIA1KGyENIAVBAWoiBSALRw0ACwsgCyAMSwRAIAlBAWuzIA2yIA6yk5UhFiAMIQ0DQAJ/IBYgAyANQQJ0aiIBKAIAIA5rspQiF4tDAAAAT10EQCAXqAwBC0GAgICAeAshCiABIAo2AgAgBCAKQQJ0aiIBIAEoAgBBAWo2AgAgDUEBaiINIAtHDQALCyAJQQJPBEAgBCgCACENQQEhDgNAIAQgDkECdGoiASABKAIAIA1qIg02AgAgDkEBaiIOIAlHDQALCyAMQQBKBEAgDCEOA0AgBiAOQQFrIgFBAnQiAmogACACaigCADYCACAOQQFKIQIgASEOIAINAAsLIAsgDEoEQCALIQ4DQCAGIAsgBCADIA5BAWsiDkECdCIBaigCAEECdGoiAigCACIFa0ECdGogACABaigCADYCACACIAVBAWs2AgAgDCAOSA0ACwsLBABBAAs=";
  7152. //sortIndexes 按照深度排序
  7153. function sortWorker(self) {
  7154. let wasmInstance;
  7155. let wasmMemory;
  7156. let useSharedMemory;
  7157. let integerBasedSort;
  7158. let dynamicMode;
  7159. let splatCount;
  7160. let indexesToSortOffset; //存储首地址
  7161. let sortedIndexesOffset;
  7162. let transformIndexesOffset;
  7163. let transformsOffset;
  7164. let precomputedDistancesOffset;
  7165. let mappedDistancesOffset;
  7166. let frequenciesOffset;
  7167. let centersOffset;
  7168. let modelViewProjOffset;
  7169. let countsZero;
  7170. let sortedIndexesOut;
  7171. let Constants;
  7172. function sort(splatSortCount, splatRenderCount, modelViewProj,
  7173. usePrecomputedDistances, copyIndexesToSort, copyPrecomputedDistances, copyTransforms) {
  7174. const sortStartTime = performance.now();
  7175. if (!useSharedMemory) {
  7176. const indexesToSort = new Uint32Array(wasmMemory, indexesToSortOffset, copyIndexesToSort.byteLength / Constants.BytesPerInt);
  7177. indexesToSort.set(copyIndexesToSort);
  7178. const transforms = new Float32Array(wasmMemory, transformsOffset, copyTransforms.byteLength / Constants.BytesPerFloat);
  7179. transforms.set(copyTransforms);
  7180. if (usePrecomputedDistances) {
  7181. let precomputedDistances;
  7182. if (integerBasedSort) {
  7183. precomputedDistances = new Int32Array(wasmMemory, precomputedDistancesOffset,
  7184. copyPrecomputedDistances.byteLength / Constants.BytesPerInt);
  7185. } else {
  7186. precomputedDistances = new Float32Array(wasmMemory, precomputedDistancesOffset,
  7187. copyPrecomputedDistances.byteLength / Constants.BytesPerFloat);
  7188. }
  7189. precomputedDistances.set(copyPrecomputedDistances);
  7190. }
  7191. }
  7192. if (!countsZero) countsZero = new Uint32Array(Constants.DepthMapRange);
  7193. new Float32Array(wasmMemory, modelViewProjOffset, 16).set(modelViewProj);//由于新数组已经与 WebAssembly 内存关联,实际上是将 modelViewProj 的数据复制到了 WebAssembly 内存的指定位置。
  7194. new Uint32Array(wasmMemory, frequenciesOffset, Constants.DepthMapRange).set(countsZero);
  7195. wasmInstance.exports.sortIndexes(indexesToSortOffset, centersOffset, precomputedDistancesOffset,
  7196. mappedDistancesOffset, frequenciesOffset, modelViewProjOffset,
  7197. sortedIndexesOffset, transformIndexesOffset, transformsOffset,
  7198. Constants.DepthMapRange, splatSortCount, splatRenderCount,
  7199. splatCount, usePrecomputedDistances, integerBasedSort,
  7200. dynamicMode);
  7201. const sortMessage = {
  7202. 'sortDone': true,
  7203. 'splatSortCount': splatSortCount,
  7204. 'splatRenderCount': splatRenderCount,
  7205. 'sortTime': 0
  7206. };
  7207. if (!useSharedMemory) {
  7208. const sortedIndexes = new Uint32Array(wasmMemory, sortedIndexesOffset, splatRenderCount);
  7209. if (!sortedIndexesOut || sortedIndexesOut.length < splatRenderCount) {
  7210. sortedIndexesOut = new Uint32Array(splatRenderCount);
  7211. }
  7212. sortedIndexesOut.set(sortedIndexes);
  7213. sortMessage.sortedIndexes = sortedIndexesOut;
  7214. }
  7215. const sortEndTime = performance.now();
  7216. sortMessage.sortTime = sortEndTime - sortStartTime;
  7217. self.postMessage(sortMessage);
  7218. }
  7219. self.onmessage = (e) => {
  7220. if (e.data.centers) {//填入坐标数据
  7221. centers = e.data.centers;
  7222. transformIndexes = e.data.transformIndexes;
  7223. if (integerBasedSort) {
  7224. new Int32Array(wasmMemory, centersOffset + e.data.range.from * Constants.BytesPerInt * 4,
  7225. e.data.range.count * 4).set(new Int32Array(centers));
  7226. } else {
  7227. new Float32Array(wasmMemory, centersOffset + e.data.range.from * Constants.BytesPerFloat * 4,
  7228. e.data.range.count * 4).set(new Float32Array(centers));
  7229. }
  7230. if (dynamicMode) {
  7231. new Uint32Array(wasmMemory, transformIndexesOffset + e.data.range.from * 4,
  7232. e.data.range.count).set(new Uint32Array(transformIndexes));
  7233. }
  7234. self.postMessage({
  7235. 'centerDataSet': true,
  7236. });
  7237. } else if (e.data.sort) {//请求sort
  7238. const renderCount = e.data.sort.splatRenderCount || 0;
  7239. const sortCount = e.data.sort.splatSortCount || 0;
  7240. const usePrecomputedDistances = e.data.sort.usePrecomputedDistances;
  7241. let copyIndexesToSort;
  7242. let copyPrecomputedDistances;
  7243. let copyTransforms;
  7244. if (!useSharedMemory) {
  7245. copyIndexesToSort = e.data.sort.indexesToSort;
  7246. copyTransforms = e.data.sort.transforms;
  7247. if (usePrecomputedDistances) copyPrecomputedDistances = e.data.sort.precomputedDistances;
  7248. }
  7249. sort(sortCount, renderCount, e.data.sort.modelViewProj, usePrecomputedDistances,
  7250. copyIndexesToSort, copyPrecomputedDistances, copyTransforms);
  7251. } else if (e.data.init) {//初始化 分配空间
  7252. // Yep, this is super hacky and gross :(
  7253. Constants = e.data.init.Constants;
  7254. splatCount = e.data.init.splatCount;
  7255. useSharedMemory = e.data.init.useSharedMemory;
  7256. integerBasedSort = e.data.init.integerBasedSort;
  7257. dynamicMode = e.data.init.dynamicMode;
  7258. const CENTERS_BYTES_PER_ENTRY = integerBasedSort ? (Constants.BytesPerInt * 4) : (Constants.BytesPerFloat * 4);
  7259. const sorterWasmBytes = new Uint8Array(e.data.init.sorterWasmBytes);
  7260. const matrixSize = 16 * Constants.BytesPerFloat;
  7261. const memoryRequiredForIndexesToSort = splatCount * Constants.BytesPerInt;
  7262. const memoryRequiredForCenters = splatCount * CENTERS_BYTES_PER_ENTRY;
  7263. const memoryRequiredForModelViewProjectionMatrix = matrixSize;
  7264. const memoryRequiredForPrecomputedDistances = integerBasedSort ?
  7265. (splatCount * Constants.BytesPerInt) : (splatCount * Constants.BytesPerFloat);
  7266. const memoryRequiredForMappedDistances = splatCount * Constants.BytesPerInt;
  7267. const memoryRequiredForSortedIndexes = splatCount * Constants.BytesPerInt;
  7268. const memoryRequiredForIntermediateSortBuffers = Constants.DepthMapRange * Constants.BytesPerInt * 2;
  7269. const memoryRequiredforTransformIndexes = dynamicMode ? (splatCount * Constants.BytesPerInt) : 0;
  7270. const memoryRequiredforTransforms = dynamicMode ? (Constants.MaxScenes * matrixSize) : 0;
  7271. const extraMemory = Constants.MemoryPageSize * 32;
  7272. const totalRequiredMemory = memoryRequiredForIndexesToSort +
  7273. memoryRequiredForCenters +
  7274. memoryRequiredForModelViewProjectionMatrix +
  7275. memoryRequiredForPrecomputedDistances +
  7276. memoryRequiredForMappedDistances +
  7277. memoryRequiredForIntermediateSortBuffers +
  7278. memoryRequiredForSortedIndexes +
  7279. memoryRequiredforTransformIndexes +
  7280. memoryRequiredforTransforms +
  7281. extraMemory;
  7282. const totalPagesRequired = Math.floor(totalRequiredMemory / Constants.MemoryPageSize ) + 1;
  7283. const sorterWasmImport = {
  7284. module: {},
  7285. env: {
  7286. memory: new WebAssembly.Memory({
  7287. initial: totalPagesRequired * 2, //创建一个初始大小为 .. 页(每页64KiB)的内存实例
  7288. maximum: totalPagesRequired * 4,
  7289. shared: true,
  7290. }),
  7291. }
  7292. };
  7293. WebAssembly.compile(sorterWasmBytes)
  7294. .then((wasmModule) => {
  7295. return WebAssembly.instantiate(wasmModule, sorterWasmImport);
  7296. })
  7297. .then((instance) => {
  7298. wasmInstance = instance;
  7299. indexesToSortOffset = 0;
  7300. centersOffset = indexesToSortOffset + memoryRequiredForIndexesToSort;
  7301. modelViewProjOffset = centersOffset + memoryRequiredForCenters;
  7302. precomputedDistancesOffset = modelViewProjOffset + memoryRequiredForModelViewProjectionMatrix;
  7303. mappedDistancesOffset = precomputedDistancesOffset + memoryRequiredForPrecomputedDistances;
  7304. frequenciesOffset = mappedDistancesOffset + memoryRequiredForMappedDistances;
  7305. sortedIndexesOffset = frequenciesOffset + memoryRequiredForIntermediateSortBuffers;
  7306. transformIndexesOffset = sortedIndexesOffset + memoryRequiredForSortedIndexes;
  7307. transformsOffset = transformIndexesOffset + memoryRequiredforTransformIndexes;
  7308. wasmMemory = sorterWasmImport.env.memory.buffer; //SharedArrayBuffer
  7309. if (useSharedMemory) {
  7310. self.postMessage({
  7311. 'sortSetupPhase1Complete': true,
  7312. 'indexesToSortBuffer': wasmMemory,
  7313. 'indexesToSortOffset': indexesToSortOffset,
  7314. 'sortedIndexesBuffer': wasmMemory, //整理好的indexes
  7315. 'sortedIndexesOffset': sortedIndexesOffset,
  7316. 'precomputedDistancesBuffer': wasmMemory,
  7317. 'precomputedDistancesOffset': precomputedDistancesOffset,
  7318. 'transformsBuffer': wasmMemory,
  7319. 'transformsOffset': transformsOffset
  7320. });
  7321. } else {
  7322. self.postMessage({
  7323. 'sortSetupPhase1Complete': true
  7324. });
  7325. }
  7326. });
  7327. }
  7328. };
  7329. }
  7330. function createSortWorker(splatCount, useSharedMemory, integerBasedSort, dynamicMode) {
  7331. const worker = new Worker(
  7332. URL.createObjectURL(
  7333. new Blob(['(', sortWorker.toString(), ')(self)'], {
  7334. type: 'application/javascript',
  7335. }),
  7336. ),
  7337. );
  7338. const sorterWasmBinaryString = atob(SorterWasm);//二进制代码
  7339. const sorterWasmBytes = new Uint8Array(sorterWasmBinaryString.length);
  7340. for (let i = 0; i < sorterWasmBinaryString.length; i++) {
  7341. sorterWasmBytes[i] = sorterWasmBinaryString.charCodeAt(i);
  7342. }
  7343. worker.postMessage({
  7344. 'init': {
  7345. 'sorterWasmBytes': sorterWasmBytes.buffer,
  7346. 'splatCount': splatCount,
  7347. 'useSharedMemory': useSharedMemory,
  7348. 'integerBasedSort': integerBasedSort,
  7349. 'dynamicMode': dynamicMode,
  7350. // Super hacky
  7351. 'Constants': {
  7352. 'BytesPerFloat': Constants.BytesPerFloat,
  7353. 'BytesPerInt': Constants.BytesPerInt,
  7354. 'DepthMapRange': Constants.DepthMapRange,
  7355. 'MemoryPageSize': Constants.MemoryPageSize,
  7356. 'MaxScenes': Constants.MaxScenes
  7357. }
  7358. }
  7359. });
  7360. return worker;
  7361. }
  7362. /*
  7363. 相关函数见 setupSortWorker updateSplatSort updateRenderIndexes addSplatBuffers
  7364. 为什么要手动排序?shader无法计算准确的深度吗
  7365. */
  7366. const WebXRMode = {
  7367. None: 0,
  7368. VR: 1,
  7369. AR: 2
  7370. };
  7371. /*
  7372. Copyright © 2010-2024 three.js authors & Mark Kellogg
  7373. Permission is hereby granted, free of charge, to any person obtaining a copy
  7374. of this software and associated documentation files (the "Software"), to deal
  7375. in the Software without restriction, including without limitation the rights
  7376. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7377. copies of the Software, and to permit persons to whom the Software is
  7378. furnished to do so, subject to the following conditions:
  7379. The above copyright notice and this permission notice shall be included in
  7380. all copies or substantial portions of the Software.
  7381. */
  7382. class VRButton {
  7383. static createButton( renderer ) {
  7384. const button = document.createElement( 'button' );
  7385. function showEnterVR( /* device */ ) {
  7386. let currentSession = null;
  7387. async function onSessionStarted( session ) {
  7388. session.addEventListener( 'end', onSessionEnded );
  7389. await renderer.xr.setSession( session );
  7390. button.textContent = 'EXIT VR';
  7391. currentSession = session;
  7392. }
  7393. function onSessionEnded( /* event */ ) {
  7394. currentSession.removeEventListener( 'end', onSessionEnded );
  7395. button.textContent = 'ENTER VR';
  7396. currentSession = null;
  7397. }
  7398. //
  7399. button.style.display = '';
  7400. button.style.cursor = 'pointer';
  7401. button.style.left = 'calc(50% - 50px)';
  7402. button.style.width = '100px';
  7403. button.textContent = 'ENTER VR';
  7404. // WebXR's requestReferenceSpace only works if the corresponding feature
  7405. // was requested at session creation time. For simplicity, just ask for
  7406. // the interesting ones as optional features, but be aware that the
  7407. // requestReferenceSpace call will fail if it turns out to be unavailable.
  7408. // ('local' is always available for immersive sessions and doesn't need to
  7409. // be requested separately.)
  7410. const sessionInit = { optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers'] };
  7411. button.onmouseenter = function() {
  7412. button.style.opacity = '1.0';
  7413. };
  7414. button.onmouseleave = function() {
  7415. button.style.opacity = '0.5';
  7416. };
  7417. button.onclick = function() {
  7418. if ( currentSession === null ) {
  7419. navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted );
  7420. } else {
  7421. currentSession.end();
  7422. if ( navigator.xr.offerSession !== undefined ) {
  7423. navigator.xr.offerSession( 'immersive-vr', sessionInit )
  7424. .then( onSessionStarted )
  7425. .catch( ( err ) => {
  7426. console.warn( err );
  7427. } );
  7428. }
  7429. }
  7430. };
  7431. if ( navigator.xr.offerSession !== undefined ) {
  7432. navigator.xr.offerSession( 'immersive-vr', sessionInit )
  7433. .then( onSessionStarted )
  7434. .catch( ( err ) => {
  7435. console.warn( err );
  7436. } );
  7437. }
  7438. }
  7439. function disableButton() {
  7440. button.style.display = '';
  7441. button.style.cursor = 'auto';
  7442. button.style.left = 'calc(50% - 75px)';
  7443. button.style.width = '150px';
  7444. button.onmouseenter = null;
  7445. button.onmouseleave = null;
  7446. button.onclick = null;
  7447. }
  7448. function showWebXRNotFound() {
  7449. disableButton();
  7450. button.textContent = 'VR NOT SUPPORTED';
  7451. }
  7452. function showVRNotAllowed( exception ) {
  7453. disableButton();
  7454. console.warn( 'Exception when trying to call xr.isSessionSupported', exception );
  7455. button.textContent = 'VR NOT ALLOWED';
  7456. }
  7457. function stylizeElement( element ) {
  7458. element.style.position = 'absolute';
  7459. element.style.bottom = '20px';
  7460. element.style.padding = '12px 6px';
  7461. element.style.border = '1px solid #fff';
  7462. element.style.borderRadius = '4px';
  7463. element.style.background = 'rgba(0,0,0,0.1)';
  7464. element.style.color = '#fff';
  7465. element.style.font = 'normal 13px sans-serif';
  7466. element.style.textAlign = 'center';
  7467. element.style.opacity = '0.5';
  7468. element.style.outline = 'none';
  7469. element.style.zIndex = '999';
  7470. }
  7471. if ( 'xr' in navigator ) {
  7472. button.id = 'VRButton';
  7473. button.style.display = 'none';
  7474. stylizeElement( button );
  7475. navigator.xr.isSessionSupported( 'immersive-vr' ).then( function( supported ) {
  7476. supported ? showEnterVR() : showWebXRNotFound();
  7477. if ( supported && VRButton.xrSessionIsGranted ) {
  7478. button.click();
  7479. }
  7480. } ).catch( showVRNotAllowed );
  7481. return button;
  7482. } else {
  7483. const message = document.createElement( 'a' );
  7484. if ( window.isSecureContext === false ) {
  7485. message.href = document.location.href.replace( /^http:/, 'https:' );
  7486. message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message
  7487. } else {
  7488. message.href = 'https://immersiveweb.dev/';
  7489. message.innerHTML = 'WEBXR NOT AVAILABLE';
  7490. }
  7491. message.style.left = 'calc(50% - 90px)';
  7492. message.style.width = '180px';
  7493. message.style.textDecoration = 'none';
  7494. stylizeElement( message );
  7495. return message;
  7496. }
  7497. }
  7498. static registerSessionGrantedListener() {
  7499. if ( typeof navigator !== 'undefined' && 'xr' in navigator ) {
  7500. // WebXRViewer (based on Firefox) has a bug where addEventListener
  7501. // throws a silent exception and aborts execution entirely.
  7502. if ( /WebXRViewer\//i.test( navigator.userAgent ) ) return;
  7503. navigator.xr.addEventListener( 'sessiongranted', () => {
  7504. VRButton.xrSessionIsGranted = true;
  7505. } );
  7506. }
  7507. }
  7508. }
  7509. VRButton.xrSessionIsGranted = false;
  7510. VRButton.registerSessionGrantedListener();
  7511. /*
  7512. Copyright © 2010-2024 three.js authors & Mark Kellogg
  7513. Permission is hereby granted, free of charge, to any person obtaining a copy
  7514. of this software and associated documentation files (the "Software"), to deal
  7515. in the Software without restriction, including without limitation the rights
  7516. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7517. copies of the Software, and to permit persons to whom the Software is
  7518. furnished to do so, subject to the following conditions:
  7519. The above copyright notice and this permission notice shall be included in
  7520. all copies or substantial portions of the Software.
  7521. */
  7522. class ARButton {
  7523. static createButton( renderer, sessionInit = {} ) {
  7524. const button = document.createElement( 'button' );
  7525. function showStartAR( /* device */ ) {
  7526. if ( sessionInit.domOverlay === undefined ) {
  7527. const overlay = document.createElement( 'div' );
  7528. overlay.style.display = 'none';
  7529. document.body.appendChild( overlay );
  7530. const svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
  7531. svg.setAttribute( 'width', 38 );
  7532. svg.setAttribute( 'height', 38 );
  7533. svg.style.position = 'absolute';
  7534. svg.style.right = '20px';
  7535. svg.style.top = '20px';
  7536. svg.addEventListener( 'click', function() {
  7537. currentSession.end();
  7538. } );
  7539. overlay.appendChild( svg );
  7540. const path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
  7541. path.setAttribute( 'd', 'M 12,12 L 28,28 M 28,12 12,28' );
  7542. path.setAttribute( 'stroke', '#fff' );
  7543. path.setAttribute( 'stroke-width', 2 );
  7544. svg.appendChild( path );
  7545. if ( sessionInit.optionalFeatures === undefined ) {
  7546. sessionInit.optionalFeatures = [];
  7547. }
  7548. sessionInit.optionalFeatures.push( 'dom-overlay' );
  7549. sessionInit.domOverlay = { root: overlay };
  7550. }
  7551. //
  7552. let currentSession = null;
  7553. async function onSessionStarted( session ) {
  7554. session.addEventListener( 'end', onSessionEnded );
  7555. renderer.xr.setReferenceSpaceType( 'local' );
  7556. await renderer.xr.setSession( session );
  7557. button.textContent = 'STOP AR';
  7558. sessionInit.domOverlay.root.style.display = '';
  7559. currentSession = session;
  7560. }
  7561. function onSessionEnded( /* event */ ) {
  7562. currentSession.removeEventListener( 'end', onSessionEnded );
  7563. button.textContent = 'START AR';
  7564. sessionInit.domOverlay.root.style.display = 'none';
  7565. currentSession = null;
  7566. }
  7567. //
  7568. button.style.display = '';
  7569. button.style.cursor = 'pointer';
  7570. button.style.left = 'calc(50% - 50px)';
  7571. button.style.width = '100px';
  7572. button.textContent = 'START AR';
  7573. button.onmouseenter = function() {
  7574. button.style.opacity = '1.0';
  7575. };
  7576. button.onmouseleave = function() {
  7577. button.style.opacity = '0.5';
  7578. };
  7579. button.onclick = function() {
  7580. if ( currentSession === null ) {
  7581. navigator.xr.requestSession( 'immersive-ar', sessionInit ).then( onSessionStarted );
  7582. } else {
  7583. currentSession.end();
  7584. if ( navigator.xr.offerSession !== undefined ) {
  7585. navigator.xr.offerSession( 'immersive-ar', sessionInit )
  7586. .then( onSessionStarted )
  7587. .catch( ( err ) => {
  7588. console.warn( err );
  7589. } );
  7590. }
  7591. }
  7592. };
  7593. if ( navigator.xr.offerSession !== undefined ) {
  7594. navigator.xr.offerSession( 'immersive-ar', sessionInit )
  7595. .then( onSessionStarted )
  7596. .catch( ( err ) => {
  7597. console.warn( err );
  7598. } );
  7599. }
  7600. }
  7601. function disableButton() {
  7602. button.style.display = '';
  7603. button.style.cursor = 'auto';
  7604. button.style.left = 'calc(50% - 75px)';
  7605. button.style.width = '150px';
  7606. button.onmouseenter = null;
  7607. button.onmouseleave = null;
  7608. button.onclick = null;
  7609. }
  7610. function showARNotSupported() {
  7611. disableButton();
  7612. button.textContent = 'AR NOT SUPPORTED';
  7613. }
  7614. function showARNotAllowed( exception ) {
  7615. disableButton();
  7616. console.warn( 'Exception when trying to call xr.isSessionSupported', exception );
  7617. button.textContent = 'AR NOT ALLOWED';
  7618. }
  7619. function stylizeElement( element ) {
  7620. element.style.position = 'absolute';
  7621. element.style.bottom = '20px';
  7622. element.style.padding = '12px 6px';
  7623. element.style.border = '1px solid #fff';
  7624. element.style.borderRadius = '4px';
  7625. element.style.background = 'rgba(0,0,0,0.1)';
  7626. element.style.color = '#fff';
  7627. element.style.font = 'normal 13px sans-serif';
  7628. element.style.textAlign = 'center';
  7629. element.style.opacity = '0.5';
  7630. element.style.outline = 'none';
  7631. element.style.zIndex = '999';
  7632. }
  7633. if ( 'xr' in navigator ) {
  7634. button.id = 'ARButton';
  7635. button.style.display = 'none';
  7636. stylizeElement( button );
  7637. navigator.xr.isSessionSupported( 'immersive-ar' ).then( function( supported ) {
  7638. supported ? showStartAR() : showARNotSupported();
  7639. } ).catch( showARNotAllowed );
  7640. return button;
  7641. } else {
  7642. const message = document.createElement( 'a' );
  7643. if ( window.isSecureContext === false ) {
  7644. message.href = document.location.href.replace( /^http:/, 'https:' );
  7645. message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message
  7646. } else {
  7647. message.href = 'https://immersiveweb.dev/';
  7648. message.innerHTML = 'WEBXR NOT AVAILABLE';
  7649. }
  7650. message.style.left = 'calc(50% - 90px)';
  7651. message.style.width = '180px';
  7652. message.style.textDecoration = 'none';
  7653. stylizeElement( message );
  7654. return message;
  7655. }
  7656. }
  7657. }
  7658. const RenderMode = {
  7659. Always: 0,
  7660. OnChange: 1,
  7661. Never: 2
  7662. };
  7663. const THREE_CAMERA_FOV = 50;
  7664. const MINIMUM_DISTANCE_TO_NEW_FOCAL_POINT = .75;
  7665. const MIN_SPLAT_COUNT_TO_SHOW_SPLAT_TREE_LOADING_SPINNER = 1500000;
  7666. const FOCUS_MARKER_FADE_IN_SPEED = 10.0;
  7667. const FOCUS_MARKER_FADE_OUT_SPEED = 2.5;
  7668. const CONSECUTIVE_RENDERED_FRAMES_FOR_FPS_CALCULATION = 60;
  7669. /**
  7670. * Viewer: Manages the rendering of splat scenes. Manages an instance of SplatMesh as well as a web worker
  7671. * that performs the sort for its splats.
  7672. */
  7673. class Viewer {
  7674. constructor(options = {}) {
  7675. // The natural 'up' vector for viewing the scene (only has an effect when used with orbit controls and
  7676. // when the viewer uses its own camera).
  7677. if (!options.cameraUp) options.cameraUp = [0, 1, 0];
  7678. this.cameraUp = new THREE.Vector3().fromArray(options.cameraUp);
  7679. // The camera's initial position (only used when the viewer uses its own camera).
  7680. if (!options.initialCameraPosition) options.initialCameraPosition = [0, 10, 15];
  7681. this.initialCameraPosition = new THREE.Vector3().fromArray(options.initialCameraPosition);
  7682. // The initial focal point of the camera and center of the camera's orbit (only used when the viewer uses its own camera).
  7683. if (!options.initialCameraLookAt) options.initialCameraLookAt = [0, 0, 0];
  7684. this.initialCameraLookAt = new THREE.Vector3().fromArray(options.initialCameraLookAt);
  7685. // 'dropInMode' is a flag that is used internally to support the usage of the viewer as a Three.js scene object
  7686. this.dropInMode = options.dropInMode || false;
  7687. // If 'selfDrivenMode' is true, the viewer manages its own update/animation loop via requestAnimationFrame()
  7688. if (options.selfDrivenMode === undefined || options.selfDrivenMode === null) options.selfDrivenMode = true;
  7689. this.selfDrivenMode = options.selfDrivenMode && !this.dropInMode;
  7690. this.selfDrivenUpdateFunc = this.selfDrivenUpdate.bind(this);
  7691. // If 'useBuiltInControls' is true, the viewer will create its own instance of OrbitControls and attach to the camera
  7692. if (options.useBuiltInControls === undefined) options.useBuiltInControls = true;
  7693. this.useBuiltInControls = options.useBuiltInControls;
  7694. // parent element of the Three.js renderer canvas
  7695. this.rootElement = options.rootElement;
  7696. // Tells the viewer to pretend the device pixel ratio is 1, which can boost performance on devices where it is larger,
  7697. // at a small cost to visual quality
  7698. this.ignoreDevicePixelRatio = options.ignoreDevicePixelRatio || false;
  7699. this.devicePixelRatio = this.ignoreDevicePixelRatio ? 1 : window.devicePixelRatio;
  7700. // Tells the viewer to use 16-bit floating point values when storing splat covariance data in textures, instead of 32-bit
  7701. this.halfPrecisionCovariancesOnGPU = options.halfPrecisionCovariancesOnGPU || false;
  7702. // If 'threeScene' is valid, it will be rendered by the viewer along with the splat mesh
  7703. this.threeScene = options.threeScene;
  7704. // Allows for usage of an external Three.js renderer
  7705. this.renderer = options.renderer;
  7706. // Allows for usage of an external Three.js camera
  7707. this.camera = options.camera;
  7708. // If 'gpuAcceleratedSort' is true, a partially GPU-accelerated approach to sorting splats will be used.
  7709. // Currently this means pre-computing splat distances from the camera on the GPU
  7710. this.gpuAcceleratedSort = options.gpuAcceleratedSort || false;
  7711. // if 'integerBasedSort' is true, the integer version of splat centers as well as other values used to calculate
  7712. // splat distances are used instead of the float version. This speeds up computation, but introduces the possibility of
  7713. // overflow in larger scenes.
  7714. if (options.integerBasedSort === undefined || options.integerBasedSort === null) {
  7715. options.integerBasedSort = true;
  7716. }
  7717. this.integerBasedSort = options.integerBasedSort;
  7718. // If 'sharedMemoryForWorkers' is true, a SharedArrayBuffer will be used to communicate with web workers. This method
  7719. // is faster than copying memory to or from web workers, but comes with security implications as outlined here:
  7720. // https://web.dev/articles/cross-origin-isolation-guide
  7721. // If enabled, it requires specific CORS headers to be present in the response from the server that is sent when
  7722. // loading the application. More information is available in the README.
  7723. if (options.sharedMemoryForWorkers === undefined || options.sharedMemoryForWorkers === null) options.sharedMemoryForWorkers = true;
  7724. this.sharedMemoryForWorkers = options.sharedMemoryForWorkers;
  7725. // if 'dynamicScene' is true, it tells the viewer to assume scene elements are not stationary or that the number of splats in the
  7726. // scene may change. This prevents optimizations that depend on a static scene from being made. Additionally, if 'dynamicScene' is
  7727. // true it tells the splat mesh to not apply scene tranforms to splat data that is returned by functions like
  7728. // SplatMesh.getSplatCenter() by default.
  7729. this.dynamicScene = !!options.dynamicScene;
  7730. // When true, will perform additional steps during rendering to address artifacts caused by the rendering of gaussians at a
  7731. // substantially different resolution than that at which they were rendered during training. This will only work correctly
  7732. // for models that were trained using a process that utilizes this compensation calculation. For more details:
  7733. // https://github.com/nerfstudio-project/gsplat/pull/117
  7734. // https://github.com/graphdeco-inria/gaussian-splatting/issues/294#issuecomment-1772688093
  7735. this.antialiased = options.antialiased || false;
  7736. this.webXRMode = options.webXRMode || WebXRMode.None;
  7737. if (this.webXRMode !== WebXRMode.None) {
  7738. this.gpuAcceleratedSort = false;
  7739. }
  7740. this.webXRActive = false;
  7741. // if 'renderMode' is RenderMode.Always, then the viewer will rrender the scene on every update. If it is RenderMode.OnChange,
  7742. // it will only render when something in the scene has changed.
  7743. this.renderMode = options.renderMode || RenderMode.Always;
  7744. // SceneRevealMode.Default results in a nice, slow fade-in effect for progressively loaded scenes,
  7745. // and a fast fade-in for non progressively loaded scenes.
  7746. // SceneRevealMode.Gradual will force a slow fade-in for all scenes.
  7747. // SceneRevealMode.Instant will force all loaded scene data to be immediately visible.
  7748. this.sceneRevealMode = options.sceneRevealMode || SceneRevealMode.Default;
  7749. // Hacky, experimental, non-scientific parameter for tweaking focal length related calculations. For scenes with very
  7750. // small gaussians, small details, and small dimensions -- increasing this value can help improve visual quality.
  7751. this.focalAdjustment = options.focalAdjustment || 1.0;
  7752. // Specify the maximum screen-space splat size, can help deal with large splats that get too unwieldy
  7753. this.maxScreenSpaceSplatSize = options.maxScreenSpaceSplatSize || 2048;
  7754. // The verbosity of console logging
  7755. this.logLevel = options.logLevel || LogLevel.None;
  7756. // Degree of spherical harmonics to utilize in rendering splats (assuming the data is present in the splat scene).
  7757. // Valid values are 0 - 3. Default value is 0.
  7758. this.sphericalHarmonicsDegree = options.sphericalHarmonicsDegree || 0;
  7759. this.createSplatMesh();
  7760. this.controls = null;
  7761. this.perspectiveControls = null;
  7762. this.orthographicControls = null;
  7763. this.orthographicCamera = null;
  7764. this.perspectiveCamera = null;
  7765. this.showMeshCursor = false;
  7766. this.showControlPlane = false;
  7767. this.showInfo = false;
  7768. this.sceneHelper = null;
  7769. this.sortWorker = null;
  7770. this.sortRunning = false;
  7771. this.splatRenderCount = 0;
  7772. this.sortWorkerIndexesToSort = null;
  7773. this.sortWorkerSortedIndexes = null;
  7774. this.sortWorkerPrecomputedDistances = null;
  7775. this.sortWorkerTransforms = null;
  7776. this.runAfterFirstSort = [];
  7777. this.selfDrivenModeRunning = false;
  7778. this.splatRenderReady = false;
  7779. this.raycaster = new Raycaster();
  7780. this.infoPanel = null;
  7781. this.startInOrthographicMode = false;
  7782. this.currentFPS = 0;
  7783. this.lastSortTime = 0;
  7784. this.consecutiveRenderFrames = 0;
  7785. this.previousCameraTarget = new THREE.Vector3();
  7786. this.nextCameraTarget = new THREE.Vector3();
  7787. this.mousePosition = new THREE.Vector2();
  7788. this.mouseDownPosition = new THREE.Vector2();
  7789. this.mouseDownTime = null;
  7790. this.resizeObserver = null;
  7791. this.mouseMoveListener = null;
  7792. this.mouseDownListener = null;
  7793. this.mouseUpListener = null;
  7794. this.keyDownListener = null;
  7795. this.sortPromise = null;
  7796. this.sortPromiseResolver = null;
  7797. this.splatSceneDownloadPromises = {};
  7798. this.splatSceneDownloadAndBuildPromise = null;
  7799. this.splatSceneRemovalPromise = null;
  7800. this.loadingSpinner = new LoadingSpinner(null, this.rootElement || document.body);
  7801. this.loadingSpinner.hide();
  7802. this.loadingProgressBar = new LoadingProgressBar(this.rootElement || document.body);
  7803. this.loadingProgressBar.hide();
  7804. this.infoPanel = new InfoPanel(this.rootElement || document.body);
  7805. this.infoPanel.hide();
  7806. this.usingExternalCamera = (this.dropInMode || this.camera) ? true : false;
  7807. this.usingExternalRenderer = (this.dropInMode || this.renderer) ? true : false;
  7808. this.initialized = false;
  7809. this.disposing = false;
  7810. this.disposed = false;
  7811. if (!this.dropInMode) this.init();
  7812. }
  7813. createSplatMesh() {
  7814. this.splatMesh = new SplatMesh(this.dynamicScene, this.halfPrecisionCovariancesOnGPU, this.devicePixelRatio,
  7815. this.gpuAcceleratedSort, this.integerBasedSort, this.antialiased,
  7816. this.maxScreenSpaceSplatSize, this.logLevel, this.sphericalHarmonicsDegree);
  7817. this.splatMesh.frustumCulled = false;
  7818. }
  7819. init() {
  7820. if (this.initialized) return;
  7821. if (!this.rootElement) {
  7822. if (!this.usingExternalRenderer) {
  7823. this.rootElement = document.createElement('div');
  7824. this.rootElement.style.width = '100%';
  7825. this.rootElement.style.height = '100%';
  7826. this.rootElement.style.position = 'absolute';
  7827. document.body.appendChild(this.rootElement);
  7828. } else {
  7829. this.rootElement = this.renderer.domElement.parentElement || document.body;
  7830. }
  7831. }
  7832. this.setupCamera();
  7833. this.setupRenderer();
  7834. this.setupWebXR();
  7835. this.setupControls();
  7836. this.setupEventHandlers();
  7837. this.threeScene = this.threeScene || new THREE.Scene();
  7838. this.sceneHelper = new SceneHelper(this.threeScene);
  7839. this.sceneHelper.setupMeshCursor();
  7840. this.sceneHelper.setupFocusMarker();
  7841. this.sceneHelper.setupControlPlane();
  7842. this.loadingProgressBar.setContainer(this.rootElement);
  7843. this.loadingSpinner.setContainer(this.rootElement);
  7844. this.infoPanel.setContainer(this.rootElement);
  7845. this.initialized = true;
  7846. }
  7847. setupCamera() {
  7848. if (!this.usingExternalCamera) {
  7849. const renderDimensions = new THREE.Vector2();
  7850. this.getRenderDimensions(renderDimensions);
  7851. this.perspectiveCamera = new THREE.PerspectiveCamera(THREE_CAMERA_FOV, renderDimensions.x / renderDimensions.y, 0.1, 1000);
  7852. this.orthographicCamera = new THREE.OrthographicCamera(renderDimensions.x / -2, renderDimensions.x / 2,
  7853. renderDimensions.y / 2, renderDimensions.y / -2, 0.1, 1000 );
  7854. this.camera = this.startInOrthographicMode ? this.orthographicCamera : this.perspectiveCamera;
  7855. this.camera.position.copy(this.initialCameraPosition);
  7856. this.camera.up.copy(this.cameraUp).normalize();
  7857. this.camera.lookAt(this.initialCameraLookAt);
  7858. }
  7859. }
  7860. setupRenderer() {
  7861. if (!this.usingExternalRenderer) {
  7862. const renderDimensions = new THREE.Vector2();
  7863. this.getRenderDimensions(renderDimensions);
  7864. this.renderer = new THREE.WebGLRenderer({
  7865. antialias: false,
  7866. precision: 'highp'
  7867. });
  7868. this.renderer.setPixelRatio(this.devicePixelRatio);
  7869. this.renderer.autoClear = true;
  7870. this.renderer.setClearColor(new THREE.Color( 0x000000 ), 0.0);
  7871. this.renderer.setSize(renderDimensions.x, renderDimensions.y);
  7872. this.resizeObserver = new ResizeObserver(() => {
  7873. this.getRenderDimensions(renderDimensions);
  7874. this.renderer.setSize(renderDimensions.x, renderDimensions.y);
  7875. this.forceRenderNextFrame();
  7876. });
  7877. this.resizeObserver.observe(this.rootElement);
  7878. this.rootElement.appendChild(this.renderer.domElement);
  7879. }
  7880. }
  7881. setupWebXR() {
  7882. if (this.webXRMode) {
  7883. if (this.webXRMode === WebXRMode.VR) {
  7884. this.rootElement.appendChild(VRButton.createButton(this.renderer));
  7885. } else if (this.webXRMode === WebXRMode.AR) {
  7886. this.rootElement.appendChild(ARButton.createButton(this.renderer));
  7887. }
  7888. this.renderer.xr.addEventListener('sessionstart', (e) => {
  7889. this.webXRActive = true;
  7890. });
  7891. this.renderer.xr.addEventListener('sessionend', (e) => {
  7892. this.webXRActive = false;
  7893. });
  7894. this.renderer.xr.enabled = true;
  7895. this.camera.position.copy(this.initialCameraPosition);
  7896. this.camera.up.copy(this.cameraUp).normalize();
  7897. this.camera.lookAt(this.initialCameraLookAt);
  7898. }
  7899. }
  7900. setupControls() {
  7901. if (this.useBuiltInControls && this.webXRMode === WebXRMode.None) {
  7902. if (!this.usingExternalCamera) {
  7903. this.perspectiveControls = new OrbitControls(this.perspectiveCamera, this.renderer.domElement);
  7904. this.orthographicControls = new OrbitControls(this.orthographicCamera, this.renderer.domElement);
  7905. } else {
  7906. if (this.camera.isOrthographicCamera) {
  7907. this.orthographicControls = new OrbitControls(this.camera, this.renderer.domElement);
  7908. } else {
  7909. this.perspectiveControls = new OrbitControls(this.camera, this.renderer.domElement);
  7910. }
  7911. }
  7912. for (let controls of [this.perspectiveControls, this.orthographicControls]) {
  7913. if (controls) {
  7914. controls.listenToKeyEvents(window);
  7915. controls.rotateSpeed = 0.5;
  7916. controls.maxPolarAngle = Math.PI * .75;
  7917. controls.minPolarAngle = 0.1;
  7918. controls.enableDamping = true;
  7919. controls.dampingFactor = 0.05;
  7920. controls.target.copy(this.initialCameraLookAt);
  7921. }
  7922. }
  7923. this.controls = this.camera.isOrthographicCamera ? this.orthographicControls : this.perspectiveControls;
  7924. }
  7925. }
  7926. setupEventHandlers() {
  7927. if (this.useBuiltInControls && this.webXRMode === WebXRMode.None) {
  7928. this.mouseMoveListener = this.onMouseMove.bind(this);
  7929. this.renderer.domElement.addEventListener('pointermove', this.mouseMoveListener, false);
  7930. this.mouseDownListener = this.onMouseDown.bind(this);
  7931. this.renderer.domElement.addEventListener('pointerdown', this.mouseDownListener, false);
  7932. this.mouseUpListener = this.onMouseUp.bind(this);
  7933. this.renderer.domElement.addEventListener('pointerup', this.mouseUpListener, false);
  7934. this.keyDownListener = this.onKeyDown.bind(this);
  7935. window.addEventListener('keydown', this.keyDownListener, false);
  7936. }
  7937. }
  7938. removeEventHandlers() {
  7939. if (this.useBuiltInControls) {
  7940. this.renderer.domElement.removeEventListener('pointermove', this.mouseMoveListener);
  7941. this.mouseMoveListener = null;
  7942. this.renderer.domElement.removeEventListener('pointerdown', this.mouseDownListener);
  7943. this.mouseDownListener = null;
  7944. this.renderer.domElement.removeEventListener('pointerup', this.mouseUpListener);
  7945. this.mouseUpListener = null;
  7946. window.removeEventListener('keydown', this.keyDownListener);
  7947. this.keyDownListener = null;
  7948. }
  7949. }
  7950. setRenderMode(renderMode) {
  7951. this.renderMode = renderMode;
  7952. }
  7953. onKeyDown = function() {
  7954. const forward = new THREE.Vector3();
  7955. const tempMatrixLeft = new THREE.Matrix4();
  7956. const tempMatrixRight = new THREE.Matrix4();
  7957. return function(e) {
  7958. forward.set(0, 0, -1);
  7959. forward.transformDirection(this.camera.matrixWorld);
  7960. tempMatrixLeft.makeRotationAxis(forward, Math.PI / 128);
  7961. tempMatrixRight.makeRotationAxis(forward, -Math.PI / 128);
  7962. switch (e.code) {
  7963. case 'KeyG':
  7964. this.focalAdjustment += 0.02;
  7965. this.forceRenderNextFrame();
  7966. break;
  7967. case 'KeyF':
  7968. this.focalAdjustment -= 0.02;
  7969. this.forceRenderNextFrame();
  7970. break;
  7971. case 'ArrowLeft':
  7972. this.camera.up.transformDirection(tempMatrixLeft);
  7973. break;
  7974. case 'ArrowRight':
  7975. this.camera.up.transformDirection(tempMatrixRight);
  7976. break;
  7977. case 'KeyC':
  7978. this.showMeshCursor = !this.showMeshCursor;
  7979. break;
  7980. case 'KeyU':
  7981. this.showControlPlane = !this.showControlPlane;
  7982. break;
  7983. case 'KeyI':
  7984. this.showInfo = !this.showInfo;
  7985. if (this.showInfo) {
  7986. this.infoPanel.show();
  7987. } else {
  7988. this.infoPanel.hide();
  7989. }
  7990. break;
  7991. case 'KeyO':
  7992. if (!this.usingExternalCamera) {
  7993. this.setOrthographicMode(!this.camera.isOrthographicCamera);
  7994. }
  7995. break;
  7996. case 'KeyP':
  7997. if (!this.usingExternalCamera) {
  7998. this.splatMesh.setPointCloudModeEnabled(!this.splatMesh.getPointCloudModeEnabled());
  7999. }
  8000. break;
  8001. case 'Equal':
  8002. if (!this.usingExternalCamera) {
  8003. this.splatMesh.setSplatScale(this.splatMesh.getSplatScale() + 0.05);
  8004. }
  8005. break;
  8006. case 'Minus':
  8007. if (!this.usingExternalCamera) {
  8008. this.splatMesh.setSplatScale(Math.max(this.splatMesh.getSplatScale() - 0.05, 0.0));
  8009. }
  8010. break;
  8011. }
  8012. };
  8013. }();
  8014. onMouseMove(mouse) {
  8015. this.mousePosition.set(mouse.offsetX, mouse.offsetY);
  8016. }
  8017. onMouseDown() {
  8018. this.mouseDownPosition.copy(this.mousePosition);
  8019. this.mouseDownTime = getCurrentTime();
  8020. }
  8021. onMouseUp = function() {
  8022. const clickOffset = new THREE.Vector2();
  8023. return function(mouse) {
  8024. clickOffset.copy(this.mousePosition).sub(this.mouseDownPosition);
  8025. const mouseUpTime = getCurrentTime();
  8026. const wasClick = mouseUpTime - this.mouseDownTime < 0.5 && clickOffset.length() < 2;
  8027. if (wasClick) {
  8028. this.onMouseClick(mouse);
  8029. }
  8030. };
  8031. }();
  8032. onMouseClick(mouse) {
  8033. this.mousePosition.set(mouse.offsetX, mouse.offsetY);
  8034. this.checkForFocalPointChange();
  8035. }
  8036. checkForFocalPointChange = function() {
  8037. const renderDimensions = new THREE.Vector2();
  8038. const toNewFocalPoint = new THREE.Vector3();
  8039. const outHits = [];
  8040. return function() {
  8041. if (!this.transitioningCameraTarget) {
  8042. this.getRenderDimensions(renderDimensions);
  8043. outHits.length = 0;
  8044. this.raycaster.setFromCameraAndScreenPosition(this.camera, this.mousePosition, renderDimensions);
  8045. this.raycaster.intersectSplatMesh(this.splatMesh, outHits);
  8046. if (outHits.length > 0) {
  8047. const hit = outHits[0];
  8048. const intersectionPoint = hit.origin;
  8049. toNewFocalPoint.copy(intersectionPoint).sub(this.camera.position);
  8050. if (toNewFocalPoint.length() > MINIMUM_DISTANCE_TO_NEW_FOCAL_POINT) {
  8051. this.previousCameraTarget.copy(this.controls.target);
  8052. this.nextCameraTarget.copy(intersectionPoint);
  8053. this.transitioningCameraTarget = true;
  8054. this.transitioningCameraTargetStartTime = getCurrentTime();
  8055. }
  8056. }
  8057. }
  8058. };
  8059. }();
  8060. getRenderDimensions(outDimensions) {
  8061. if (this.rootElement) {
  8062. outDimensions.x = this.rootElement.offsetWidth;
  8063. outDimensions.y = this.rootElement.offsetHeight;
  8064. } else {
  8065. this.renderer.getSize(outDimensions);
  8066. }
  8067. }
  8068. setOrthographicMode(orthographicMode) {
  8069. if (orthographicMode === this.camera.isOrthographicCamera) return;
  8070. const fromCamera = this.camera;
  8071. const toCamera = orthographicMode ? this.orthographicCamera : this.perspectiveCamera;
  8072. toCamera.position.copy(fromCamera.position);
  8073. toCamera.up.copy(fromCamera.up);
  8074. toCamera.rotation.copy(fromCamera.rotation);
  8075. toCamera.quaternion.copy(fromCamera.quaternion);
  8076. toCamera.matrix.copy(fromCamera.matrix);
  8077. this.camera = toCamera;
  8078. if (this.controls) {
  8079. const resetControls = (controls) => {
  8080. controls.saveState();
  8081. controls.reset();
  8082. };
  8083. const fromControls = this.controls;
  8084. const toControls = orthographicMode ? this.orthographicControls : this.perspectiveControls;
  8085. resetControls(toControls);
  8086. resetControls(fromControls);
  8087. toControls.target.copy(fromControls.target);
  8088. if (orthographicMode) {
  8089. Viewer.setCameraZoomFromPosition(toCamera, fromCamera, fromControls);
  8090. } else {
  8091. Viewer.setCameraPositionFromZoom(toCamera, fromCamera, toControls);
  8092. }
  8093. this.controls = toControls;
  8094. this.camera.lookAt(this.controls.target);
  8095. }
  8096. }
  8097. static setCameraPositionFromZoom = function() {
  8098. const tempVector = new THREE.Vector3();
  8099. return function(positionCamera, zoomedCamera, controls) {
  8100. const toLookAtDistance = 1 / (zoomedCamera.zoom * 0.001);
  8101. tempVector.copy(controls.target).sub(positionCamera.position).normalize().multiplyScalar(toLookAtDistance).negate();
  8102. positionCamera.position.copy(controls.target).add(tempVector);
  8103. };
  8104. }();
  8105. static setCameraZoomFromPosition = function() {
  8106. const tempVector = new THREE.Vector3();
  8107. return function(zoomCamera, positionZamera, controls) {
  8108. const toLookAtDistance = tempVector.copy(controls.target).sub(positionZamera.position).length();
  8109. zoomCamera.zoom = 1 / (toLookAtDistance * .001);
  8110. };
  8111. }();
  8112. updateSplatMesh = function() {
  8113. const renderDimensions = new THREE.Vector2();
  8114. return function() {
  8115. if (!this.splatMesh) return;
  8116. const splatCount = this.splatMesh.getSplatCount();
  8117. if (splatCount > 0) {
  8118. this.splatMesh.updateTransforms();
  8119. this.getRenderDimensions(renderDimensions);
  8120. const focalLengthX = this.camera.projectionMatrix.elements[0] * 0.5 *
  8121. this.devicePixelRatio * renderDimensions.x;
  8122. const focalLengthY = this.camera.projectionMatrix.elements[5] * 0.5 *
  8123. this.devicePixelRatio * renderDimensions.y;
  8124. //this.camera.projectionMatrix.elements[0] 等效于 (2 * n) / (r - l),其中n是焦距(到近裁剪面的距离)。当它乘以0.5,然后再乘以renderDimensions.x(即视口宽度)时,你得到的是 (n * 宽度) / (r - l)。由于r - l等于宽度,最终就只剩下了n,即焦距。对于cameraFocalLengthY的计算应该得出相同的结果,所以实际上我只需要向updateUniforms()函数传递一个单独的焦距值即可。(see:https://github.com/mkkellogg/GaussianSplats3D/issues/108)
  8125. const focalMultiplier = this.camera.isOrthographicCamera ? (1.0 / this.devicePixelRatio) : 1.0;
  8126. const focalAdjustment = this.focalAdjustment * focalMultiplier;
  8127. const inverseFocalAdjustment = 1.0 / focalAdjustment;
  8128. this.adjustForWebXRStereo(renderDimensions);
  8129. this.splatMesh.updateUniforms(renderDimensions, focalLengthX * focalAdjustment, focalLengthY * focalAdjustment,
  8130. this.camera.isOrthographicCamera, this.camera.zoom || 1.0, inverseFocalAdjustment);
  8131. }
  8132. };
  8133. }();
  8134. adjustForWebXRStereo(renderDimensions) {
  8135. // TODO: Figure out a less hacky way to determine if stereo rendering is active
  8136. if (this.camera && this.webXRActive) {
  8137. const xrCamera = this.renderer.xr.getCamera();
  8138. const xrCameraProj00 = xrCamera.projectionMatrix.elements[0];
  8139. const cameraProj00 = this.camera.projectionMatrix.elements[0];
  8140. renderDimensions.x *= (cameraProj00 / xrCameraProj00);
  8141. }
  8142. }
  8143. isLoadingOrUnloading() {
  8144. return Object.keys(this.splatSceneDownloadPromises).length > 0 || this.splatSceneDownloadAndBuildPromise !== null ||
  8145. this.splatSceneRemovalPromise !== null;
  8146. }
  8147. isDisposingOrDisposed() {
  8148. return this.disposing || this.disposed;
  8149. }
  8150. addSplatSceneDownloadPromise(promise) {
  8151. this.splatSceneDownloadPromises[promise.id] = promise;
  8152. }
  8153. removeSplatSceneDownloadPromise(promise) {
  8154. delete this.splatSceneDownloadPromises[promise.id];
  8155. }
  8156. setSplatSceneDownloadAndBuildPromise(promise) {
  8157. this.splatSceneDownloadAndBuildPromise = promise;
  8158. }
  8159. clearSplatSceneDownloadAndBuildPromise() {
  8160. this.splatSceneDownloadAndBuildPromise = null;
  8161. }
  8162. /**
  8163. * Add a splat scene to the viewer and display any loading UI if appropriate.
  8164. * @param {string} path Path to splat scene to be loaded
  8165. * @param {object} options {
  8166. *
  8167. * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
  8168. * value (valid range: 0 - 255), defaults to 1
  8169. *
  8170. * showLoadingUI: Display a loading spinner while the scene is loading, defaults to true
  8171. *
  8172. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  8173. *
  8174. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  8175. *
  8176. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  8177. *
  8178. * onProgress: Function to be called as file data are received, or other processing occurs
  8179. *
  8180. * }
  8181. * @return {AbortablePromise}
  8182. */
  8183. addSplatScene(path, options = {}) {
  8184. if (this.isLoadingOrUnloading()) {
  8185. throw new Error('Cannot add splat scene while another load or unload is already in progress.');
  8186. }
  8187. if (this.isDisposingOrDisposed()) {
  8188. throw new Error('Cannot add splat scene after dispose() is called.');
  8189. }
  8190. const format = (options.format !== undefined && options.format !== null) ? options.format : sceneFormatFromPath(path);
  8191. const streamBuildSections = Viewer.isStreamable(format) && options.streamView;
  8192. const showLoadingUI = (options.showLoadingUI !== undefined && options.showLoadingUI !== null) ? options.showLoadingUI : true;
  8193. let loadingUITaskId = null;
  8194. if (showLoadingUI) {
  8195. this.loadingSpinner.removeAllTasks();
  8196. loadingUITaskId = this.loadingSpinner.addTask('Downloading...');
  8197. }
  8198. const hideLoadingUI = () => {
  8199. this.loadingProgressBar.hide();
  8200. this.loadingSpinner.removeAllTasks();
  8201. };
  8202. const onProgressUIUpdate = (percentComplete, percentCompleteLabel, loaderStatus) => {
  8203. if (showLoadingUI) {
  8204. if (loaderStatus === LoaderStatus.Downloading) {
  8205. if (percentComplete == 100) {
  8206. this.loadingSpinner.setMessageForTask(loadingUITaskId, 'Download complete!');
  8207. } else {
  8208. if (streamBuildSections) {
  8209. this.loadingSpinner.setMessageForTask(loadingUITaskId, 'Downloading splats...');
  8210. } else {
  8211. const suffix = percentCompleteLabel ? `: ${percentCompleteLabel}` : `...`;
  8212. this.loadingSpinner.setMessageForTask(loadingUITaskId, `Downloading${suffix}`);
  8213. }
  8214. }
  8215. } else if (loaderStatus === LoaderStatus.Processing) {
  8216. this.loadingSpinner.setMessageForTask(loadingUITaskId, 'Processing splats...');
  8217. } else {
  8218. this.loadingSpinner.setMessageForTask(loadingUITaskId, 'Ready!');
  8219. }
  8220. }
  8221. };
  8222. let downloadDone = false;
  8223. let downloadedPercentage = 0;
  8224. const splatBuffersAddedUIUpdate = (firstBuild, finalBuild) => {
  8225. if (showLoadingUI) {
  8226. if (firstBuild && streamBuildSections || finalBuild && !streamBuildSections) {
  8227. this.runAfterFirstSort.push(() => {
  8228. this.loadingSpinner.removeTask(loadingUITaskId);
  8229. if (!finalBuild && !downloadDone) this.loadingProgressBar.show();
  8230. });
  8231. }
  8232. if (streamBuildSections) {
  8233. if (finalBuild) {
  8234. downloadDone = true;
  8235. this.loadingProgressBar.hide();
  8236. } else {
  8237. this.loadingProgressBar.setProgress(downloadedPercentage);
  8238. }
  8239. }
  8240. }
  8241. };
  8242. const onProgress = (percentComplete, percentCompleteLabel, loaderStatus) => {
  8243. downloadedPercentage = percentComplete;
  8244. onProgressUIUpdate(percentComplete, percentCompleteLabel, loaderStatus);
  8245. if (options.onProgress) options.onProgress(percentComplete, percentCompleteLabel, loaderStatus);
  8246. };
  8247. const buildSection = (splatBuffer, firstBuild, finalBuild) => {
  8248. if (!streamBuildSections && options.onProgress) options.onProgress(0, '0%', LoaderStatus.Processing);
  8249. const addSplatBufferOptions = {
  8250. 'rotation': options.rotation || options.orientation,
  8251. 'position': options.position,
  8252. 'scale': options.scale,
  8253. 'splatAlphaRemovalThreshold': options.splatAlphaRemovalThreshold,
  8254. };
  8255. return this.addSplatBuffers([splatBuffer], [addSplatBufferOptions],
  8256. finalBuild, firstBuild && showLoadingUI, showLoadingUI).then(() => {
  8257. if (!streamBuildSections && options.onProgress) options.onProgress(100, '100%', LoaderStatus.Processing);
  8258. splatBuffersAddedUIUpdate(firstBuild, finalBuild);
  8259. });
  8260. };
  8261. const loadFunc = streamBuildSections ? this.downloadAndBuildSingleSplatSceneStreaming.bind(this) :
  8262. this.downloadAndBuildSingleSplatSceneNonStreaming.bind(this);
  8263. return loadFunc(path, format, options.splatAlphaRemovalThreshold, buildSection.bind(this), onProgress, hideLoadingUI.bind(this));
  8264. }
  8265. /**
  8266. * Download a single non-streamed splat scene, convert to splat buffer and then rebuild the viewer's splat mesh
  8267. * by calling 'buildFunc'. Also sets/clears relevant instance synchronization objects, and calls appropriate functions
  8268. * on success or failure.
  8269. * @param {string} path Path to splat scene to be loaded
  8270. * @param {SceneFormat} format Format of the splat scene file
  8271. * @param {number} splatAlphaRemovalThreshold Ignore any splats with an alpha less than the specified value (valid range: 0 - 255)
  8272. * @param {function} buildFunc Function to build the viewer's splat mesh with the downloaded splat buffer
  8273. * @param {function} onProgress Function to be called as file data are received, or other processing occurs
  8274. * @param {function} onException Function to be called when exception occurs
  8275. * @return {AbortablePromise}
  8276. */
  8277. downloadAndBuildSingleSplatSceneNonStreaming(path, format, splatAlphaRemovalThreshold, buildFunc, onProgress, onException) {
  8278. const downloadPromise = this.downloadSplatSceneToSplatBuffer(path, splatAlphaRemovalThreshold, onProgress, false, undefined, format)
  8279. .then((splatBuffer) => {
  8280. this.removeSplatSceneDownloadPromise(downloadPromise);
  8281. return buildFunc(splatBuffer, true, true).then(() => {
  8282. this.clearSplatSceneDownloadAndBuildPromise();
  8283. });
  8284. })
  8285. .catch((e) => {
  8286. if (onException) onException();
  8287. this.clearSplatSceneDownloadAndBuildPromise();
  8288. this.removeSplatSceneDownloadPromise(downloadPromise);
  8289. if (!(e instanceof AbortedPromiseError)) {
  8290. throw (new Error(`Viewer::addSplatScene -> Could not load file ${path}`));
  8291. }
  8292. });
  8293. this.addSplatSceneDownloadPromise(downloadPromise);
  8294. this.setSplatSceneDownloadAndBuildPromise(downloadPromise);
  8295. return downloadPromise;
  8296. }
  8297. /**
  8298. * Download a single splat scene and convert to splat buffer in a streamed manner, allowing rendering as the file downloads.
  8299. * As each section is downloaded, the viewer's splat mesh is rebuilt by calling 'buildFunc'
  8300. * Also sets/clears relevant instance synchronization objects, and calls appropriate functions on success or failure.
  8301. * @param {string} path Path to splat scene to be loaded
  8302. * @param {SceneFormat} format Format of the splat scene file
  8303. * @param {number} splatAlphaRemovalThreshold Ignore any splats with an alpha less than the specified value (valid range: 0 - 255)
  8304. * @param {function} buildFunc Function to rebuild the viewer's splat mesh after a new splat buffer section is downloaded
  8305. * @param {function} onDownloadProgress Function to be called as file data are received
  8306. * @param {function} onDownloadException Function to be called when exception occurs at any point during the full download
  8307. * @return {AbortablePromise}
  8308. */
  8309. downloadAndBuildSingleSplatSceneStreaming(path, format, splatAlphaRemovalThreshold, buildFunc,
  8310. onDownloadProgress, onDownloadException) {
  8311. let firstStreamedSectionDownloadAndBuildResolver;
  8312. let firstStreamedSectionDownloadAndBuildRejecter;
  8313. let splatSceneDownloadAndBuildResolver;
  8314. let splatSceneDownloadAndBuildRejecter;
  8315. let steamedSectionBuildCount = 0;
  8316. let streamedSectionBuilding = false;
  8317. const queuedStreamedSectionBuilds = [];
  8318. const checkAndBuildStreamedSections = () => {
  8319. if (queuedStreamedSectionBuilds.length > 0 && !streamedSectionBuilding && !this.isDisposingOrDisposed()) {
  8320. streamedSectionBuilding = true;
  8321. const queuedBuild = queuedStreamedSectionBuilds.shift();
  8322. buildFunc(queuedBuild.splatBuffer, queuedBuild.firstBuild, queuedBuild.finalBuild)
  8323. .then(() => {
  8324. streamedSectionBuilding = false;
  8325. if (queuedBuild.firstBuild) {
  8326. firstStreamedSectionDownloadAndBuildRejecter = null;
  8327. firstStreamedSectionDownloadAndBuildResolver();
  8328. } else if (queuedBuild.finalBuild) {
  8329. splatSceneDownloadAndBuildResolver();
  8330. this.clearSplatSceneDownloadAndBuildPromise();
  8331. }
  8332. if (queuedStreamedSectionBuilds.length > 0) delayedExecute(() => checkAndBuildStreamedSections());
  8333. });
  8334. }
  8335. };
  8336. const onStreamedSectionProgress = (splatBuffer, finalBuild) => {
  8337. if (!this.isDisposingOrDisposed()) {
  8338. if (finalBuild || queuedStreamedSectionBuilds.length === 0 ||
  8339. splatBuffer.getSplatCount() > queuedStreamedSectionBuilds[0].splatBuffer.getSplatCount()) {
  8340. queuedStreamedSectionBuilds.push({
  8341. splatBuffer,
  8342. firstBuild: steamedSectionBuildCount === 0,
  8343. finalBuild
  8344. });
  8345. steamedSectionBuildCount++;
  8346. checkAndBuildStreamedSections();
  8347. }
  8348. }
  8349. };
  8350. let splatSceneDownloadPromise = this.downloadSplatSceneToSplatBuffer(path, splatAlphaRemovalThreshold,
  8351. onDownloadProgress, true, onStreamedSectionProgress, format);
  8352. const firstStreamedSectionBuildPromise = new AbortablePromise((resolver, rejecter) => {
  8353. firstStreamedSectionDownloadAndBuildResolver = resolver;
  8354. firstStreamedSectionDownloadAndBuildRejecter = rejecter;
  8355. }, splatSceneDownloadPromise.abortHandler);
  8356. const splatSceneDownloadAndBuildPromise = new AbortablePromise((resolver, rejecter) => {
  8357. splatSceneDownloadAndBuildResolver = resolver;
  8358. splatSceneDownloadAndBuildRejecter = rejecter;
  8359. });
  8360. this.addSplatSceneDownloadPromise(splatSceneDownloadPromise);
  8361. this.setSplatSceneDownloadAndBuildPromise(splatSceneDownloadAndBuildPromise);
  8362. splatSceneDownloadPromise.then(() => {
  8363. this.removeSplatSceneDownloadPromise(splatSceneDownloadPromise);
  8364. })
  8365. .catch((e) => {
  8366. this.clearSplatSceneDownloadAndBuildPromise();
  8367. this.removeSplatSceneDownloadPromise(splatSceneDownloadPromise);
  8368. if (!(e instanceof AbortedPromiseError)) {
  8369. splatSceneDownloadAndBuildRejecter(e);
  8370. if (firstStreamedSectionDownloadAndBuildRejecter) firstStreamedSectionDownloadAndBuildRejecter(e);
  8371. if (onDownloadException) onDownloadException(e);
  8372. }
  8373. });
  8374. return firstStreamedSectionBuildPromise;
  8375. }
  8376. /**
  8377. * Add multiple splat scenes to the viewer and display any loading UI if appropriate.
  8378. * @param {Array<object>} sceneOptions Array of per-scene options: {
  8379. *
  8380. * path: Path to splat scene to be loaded
  8381. *
  8382. * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
  8383. * value (valid range: 0 - 255), defaults to 1
  8384. *
  8385. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  8386. *
  8387. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  8388. *
  8389. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  8390. * }
  8391. * @param {boolean} showLoadingUI Display a loading spinner while the scene is loading, defaults to true
  8392. * @param {function} onProgress Function to be called as file data are received
  8393. * @return {AbortablePromise}
  8394. */
  8395. addSplatScenes(sceneOptions, showLoadingUI = true, onProgress = undefined) {
  8396. if (this.isLoadingOrUnloading()) {
  8397. throw new Error('Cannot add splat scene while another load or unload is already in progress.');
  8398. }
  8399. if (this.isDisposingOrDisposed()) {
  8400. throw new Error('Cannot add splat scene after dispose() is called.');
  8401. }
  8402. const fileCount = sceneOptions.length;
  8403. const percentComplete = [];
  8404. if (showLoadingUI) {
  8405. this.loadingSpinner.removeAllTasks();
  8406. this.loadingSpinner.show();
  8407. }
  8408. const onLoadProgress = (fileIndex, percent, percentLabel) => {
  8409. percentComplete[fileIndex] = percent;
  8410. let totalPercent = 0;
  8411. for (let i = 0; i < fileCount; i++) totalPercent += percentComplete[i] || 0;
  8412. totalPercent = totalPercent / fileCount;
  8413. percentLabel = `${totalPercent.toFixed(2)}%`;
  8414. if (showLoadingUI) {
  8415. this.loadingSpinner.setMessage(totalPercent == 100 ? `Download complete!` : `Downloading: ${percentLabel}`);
  8416. }
  8417. if (onProgress) onProgress(totalPercent, percentLabel, LoaderStatus.Downloading);
  8418. };
  8419. const downloadPromises = [];
  8420. const nativeLoadPromises = [];
  8421. const abortHandlers = [];
  8422. for (let i = 0; i < sceneOptions.length; i++) {
  8423. const options = sceneOptions[i];
  8424. const format = (options.format !== undefined && options.format !== null) ? options.format : sceneFormatFromPath(options.path);
  8425. const downloadPromise = this.downloadSplatSceneToSplatBuffer(options.path, options.splatAlphaRemovalThreshold,
  8426. onLoadProgress.bind(this, i), false, undefined, format);
  8427. abortHandlers.push(downloadPromise.abortHandler);
  8428. downloadPromises.push(downloadPromise);
  8429. nativeLoadPromises.push(downloadPromise.promise);
  8430. this.addSplatSceneDownloadPromise(downloadPromise);
  8431. }
  8432. const downloadPromise = new AbortablePromise((resolve, reject) => {
  8433. Promise.all(nativeLoadPromises)
  8434. .then((splatBuffers) => {
  8435. if (showLoadingUI) this.loadingSpinner.hide();
  8436. if (onProgress) options.onProgress(0, '0%', LoaderStatus.Processing);
  8437. this.addSplatBuffers(splatBuffers, sceneOptions, true, showLoadingUI, showLoadingUI).then(() => {
  8438. if (onProgress) onProgress(100, '100%', LoaderStatus.Processing);
  8439. this.clearSplatSceneDownloadAndBuildPromise();
  8440. resolve();
  8441. });
  8442. })
  8443. .catch((e) => {
  8444. if (showLoadingUI) this.loadingSpinner.hide();
  8445. this.clearSplatSceneDownloadAndBuildPromise();
  8446. if (!(e instanceof AbortedPromiseError)) {
  8447. reject(new Error(`Viewer::addSplatScenes -> Could not load one or more splat scenes.`));
  8448. } else {
  8449. resolve();
  8450. }
  8451. })
  8452. .finally(() => {
  8453. for (let downloadPromise of downloadPromises) {
  8454. this.removeSplatSceneDownloadPromise(downloadPromise);
  8455. }
  8456. });
  8457. }, () => {
  8458. for (let abortHandler of abortHandlers) abortHandler();
  8459. });
  8460. this.setSplatSceneDownloadAndBuildPromise(downloadPromise);
  8461. return downloadPromise;
  8462. }
  8463. /**
  8464. * Download a splat scene and convert to SplatBuffer instance.
  8465. * @param {string} path Path to splat scene to be loaded
  8466. * @param {number} splatAlphaRemovalThreshold Ignore any splats with an alpha less than the specified
  8467. * value (valid range: 0 - 255), defaults to 1
  8468. *
  8469. * @param {function} onProgress Function to be called as file data are received
  8470. * @param {boolean} streamBuiltSections Construct file sections into splat buffers as they are downloaded
  8471. * @param {function} onSectionBuilt Function to be called when new section is added to the file
  8472. * @param {string} format File format of the scene
  8473. * @return {AbortablePromise}
  8474. */
  8475. downloadSplatSceneToSplatBuffer(path, splatAlphaRemovalThreshold = 1, onProgress = undefined,
  8476. streamBuiltSections = false, onSectionBuilt = undefined, format) {
  8477. if (format === SceneFormat.Splat) {
  8478. return SplatLoader.loadFromURL(path, onProgress, streamBuiltSections, onSectionBuilt, splatAlphaRemovalThreshold, 0, false);
  8479. } else if (format === SceneFormat.KSplat) {
  8480. return KSplatLoader.loadFromURL(path, onProgress, streamBuiltSections, onSectionBuilt);
  8481. } else if (format === SceneFormat.Ply) {
  8482. return PlyLoader.loadFromURL(path, onProgress, streamBuiltSections, onSectionBuilt,
  8483. splatAlphaRemovalThreshold, 0, this.sphericalHarmonicsDegree);
  8484. }
  8485. return AbortablePromise.reject(new Error(`Viewer::downloadSplatSceneToSplatBuffer -> File format not supported: ${path}`));
  8486. }
  8487. static isStreamable(format) {
  8488. return format === SceneFormat.Splat || format === SceneFormat.KSplat || format === SceneFormat.Ply;
  8489. }
  8490. /**
  8491. * Add one or more instances of SplatBuffer to the SplatMesh instance managed by the viewer and set up the sorting web worker.
  8492. * This function will terminate the existing sort worker (if there is one).
  8493. */
  8494. addSplatBuffers = function() {
  8495. return function(splatBuffers, splatBufferOptions = [], finalBuild = true,
  8496. showLoadingUI = true, showLoadingUIForSplatTreeBuild = true) {
  8497. if (this.isDisposingOrDisposed()) return Promise.resolve();
  8498. this.splatRenderReady = false;
  8499. let splatProcessingTaskId = null;
  8500. const finish = (buildResults) => {
  8501. if (this.isDisposingOrDisposed()) return;
  8502. if (splatProcessingTaskId !== null) {
  8503. this.loadingSpinner.removeTask(splatProcessingTaskId);
  8504. splatProcessingTaskId = null;
  8505. }
  8506. // If we aren't calculating the splat distances from the center on the GPU, the sorting worker needs splat centers and
  8507. // transform indexes so that it can calculate those distance values.
  8508. if (!this.gpuAcceleratedSort && this.sortWorker) {
  8509. this.sortWorker.postMessage({
  8510. 'centers': buildResults.centers.buffer,
  8511. 'transformIndexes': buildResults.sceneIndexes.buffer,
  8512. 'range': {
  8513. 'from': buildResults.from,
  8514. 'to': buildResults.to,
  8515. 'count': buildResults.count
  8516. }
  8517. });
  8518. }
  8519. this.splatRenderReady = true;
  8520. this.sortNeededForSceneChange = true;
  8521. };
  8522. return new Promise((resolve) => {
  8523. if (showLoadingUI) {
  8524. splatProcessingTaskId = this.loadingSpinner.addTask('Processing splats...');
  8525. }
  8526. delayedExecute(() => {
  8527. if (this.isDisposingOrDisposed()) {
  8528. resolve();
  8529. } else {
  8530. const buildResults = this.addSplatBuffersToMesh(splatBuffers, splatBufferOptions,
  8531. finalBuild, showLoadingUIForSplatTreeBuild);
  8532. const maxSplatCount = this.splatMesh.getMaxSplatCount();
  8533. if (this.sortWorker && this.sortWorker.maxSplatCount !== maxSplatCount) this.disposeSortWorker();
  8534. const sortWorkerSetupPromise = (!this.sortWorker && maxSplatCount > 0) ?
  8535. this.setupSortWorker(this.splatMesh) : Promise.resolve();
  8536. sortWorkerSetupPromise.then(() => {
  8537. finish(buildResults);
  8538. resolve();
  8539. });
  8540. }
  8541. }, true);
  8542. });
  8543. };
  8544. }();
  8545. /**
  8546. * Add one or more instances of SplatBuffer to the SplatMesh instance managed by the viewer. This function is additive; all splat
  8547. * buffers contained by the viewer's splat mesh before calling this function will be preserved.
  8548. * @param {Array<SplatBuffer>} splatBuffers SplatBuffer instances
  8549. * @param {Array<object>} splatBufferOptions Array of options objects: {
  8550. *
  8551. * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
  8552. * value (valid range: 0 - 255), defaults to 1
  8553. *
  8554. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  8555. *
  8556. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  8557. *
  8558. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  8559. * }
  8560. * @param {boolean} finalBuild Will the splat mesh be in its final state after this build?
  8561. * @param {boolean} showLoadingUIForSplatTreeBuild Whether or not to show the loading spinner during construction of the splat tree.
  8562. * @return {object} Object containing info about the splats that are updated
  8563. */
  8564. addSplatBuffersToMesh(splatBuffers, splatBufferOptions, finalBuild = true, showLoadingUIForSplatTreeBuild = false) {
  8565. if (this.isDisposingOrDisposed()) return;
  8566. const allSplatBuffers = this.splatMesh.splatBuffers || [];
  8567. const allSplatBufferOptions = this.splatMesh.splatBufferOptions || [];
  8568. allSplatBuffers.push(...splatBuffers);
  8569. allSplatBufferOptions.push(...splatBufferOptions);
  8570. if (this.renderer) this.splatMesh.setRenderer(this.renderer);
  8571. let splatOptimizingTaskId;
  8572. const onSplatTreeIndexesUpload = (finished) => {
  8573. if (this.isDisposingOrDisposed()) return;
  8574. const splatCount = this.splatMesh.getSplatCount();
  8575. if (showLoadingUIForSplatTreeBuild && splatCount >= MIN_SPLAT_COUNT_TO_SHOW_SPLAT_TREE_LOADING_SPINNER) {
  8576. if (!finished && !splatOptimizingTaskId) {
  8577. this.loadingSpinner.setMinimized(true, true);
  8578. splatOptimizingTaskId = this.loadingSpinner.addTask('Optimizing splats...');
  8579. }
  8580. }
  8581. };
  8582. const onSplatTreeReady = (finished) => {
  8583. if (this.isDisposingOrDisposed()) return;
  8584. if (finished && splatOptimizingTaskId) {
  8585. this.loadingSpinner.removeTask(splatOptimizingTaskId);
  8586. }
  8587. };
  8588. return this.splatMesh.build(allSplatBuffers, allSplatBufferOptions, true, finalBuild, onSplatTreeIndexesUpload, onSplatTreeReady);
  8589. }
  8590. /**
  8591. * Set up the splat sorting web worker.
  8592. * @param {SplatMesh} splatMesh SplatMesh instance that contains the splats to be sorted
  8593. * @return {Promise}
  8594. */
  8595. setupSortWorker(splatMesh) {
  8596. if (this.isDisposingOrDisposed()) return;
  8597. return new Promise((resolve) => {
  8598. const DistancesArrayType = this.integerBasedSort ? Int32Array : Float32Array;
  8599. const splatCount = splatMesh.getSplatCount();
  8600. const maxSplatCount = splatMesh.getMaxSplatCount();
  8601. this.sortWorker = createSortWorker(maxSplatCount, this.sharedMemoryForWorkers,
  8602. this.integerBasedSort, this.splatMesh.dynamicMode);
  8603. let sortCount = 0;
  8604. this.sortWorker.onmessage = (e) => {
  8605. if (e.data.sortDone) {
  8606. this.sortRunning = false;
  8607. if (this.sharedMemoryForWorkers) {
  8608. this.splatMesh.updateRenderIndexes(this.sortWorkerSortedIndexes, e.data.splatRenderCount);
  8609. } else {
  8610. const sortedIndexes = new Uint32Array(e.data.sortedIndexes.buffer, 0, e.data.splatRenderCount);
  8611. this.splatMesh.updateRenderIndexes(sortedIndexes, e.data.splatRenderCount);
  8612. }
  8613. this.lastSortTime = e.data.sortTime;
  8614. this.sortPromiseResolver();
  8615. this.sortPromiseResolver = null;
  8616. this.forceRenderNextFrame();
  8617. if (sortCount === 0) {
  8618. this.runAfterFirstSort.forEach((func) => {
  8619. func();
  8620. });
  8621. this.runAfterFirstSort.length = 0;
  8622. }
  8623. sortCount++;
  8624. } else if (e.data.sortCanceled) {
  8625. this.sortRunning = false;
  8626. } else if (e.data.sortSetupPhase1Complete) {//划分好共享数据
  8627. if (this.logLevel >= LogLevel.Info) console.log('Sorting web worker WASM setup complete.');
  8628. if (this.sharedMemoryForWorkers) {
  8629. this.sortWorkerSortedIndexes = new Uint32Array(e.data.sortedIndexesBuffer, //这是以后就是每次要渲染的indexes
  8630. e.data.sortedIndexesOffset, maxSplatCount);
  8631. this.sortWorkerIndexesToSort = new Uint32Array(e.data.indexesToSortBuffer, //准备sort的点,每次镜头改变时先选出要渲染的点,再排序
  8632. e.data.indexesToSortOffset, maxSplatCount);
  8633. this.sortWorkerPrecomputedDistances = new DistancesArrayType(e.data.precomputedDistancesBuffer,
  8634. e.data.precomputedDistancesOffset,
  8635. maxSplatCount);
  8636. this.sortWorkerTransforms = new Float32Array(e.data.transformsBuffer,
  8637. e.data.transformsOffset, Constants.MaxScenes * 16);
  8638. } else {
  8639. this.sortWorkerIndexesToSort = new Uint32Array(maxSplatCount);
  8640. this.sortWorkerPrecomputedDistances = new DistancesArrayType(maxSplatCount);
  8641. this.sortWorkerTransforms = new Float32Array(Constants.MaxScenes * 16);
  8642. }
  8643. for (let i = 0; i < splatCount; i++) this.sortWorkerIndexesToSort[i] = i; //初始化待sort的所有index,按顺序赋值就行
  8644. this.sortWorker.maxSplatCount = maxSplatCount;//总点数
  8645. if (this.logLevel >= LogLevel.Info) {
  8646. console.log('Sorting web worker ready.');
  8647. const splatDataTextures = this.splatMesh.getSplatDataTextures();
  8648. const covariancesTextureSize = splatDataTextures.covariances.size;
  8649. const centersColorsTextureSize = splatDataTextures.centerColors.size;
  8650. console.log('Covariances texture size: ' + covariancesTextureSize.x + ' x ' + covariancesTextureSize.y);
  8651. console.log('Centers/colors texture size: ' + centersColorsTextureSize.x + ' x ' + centersColorsTextureSize.y);
  8652. }
  8653. resolve();
  8654. }
  8655. };
  8656. });
  8657. }
  8658. disposeSortWorker() {
  8659. if (this.sortWorker) this.sortWorker.terminate();
  8660. this.sortWorker = null;
  8661. this.sortPromise = null;
  8662. if (this.sortPromiseResolver) {
  8663. this.sortPromiseResolver();
  8664. this.sortPromiseResolver = null;
  8665. }
  8666. this.sortRunning = false;
  8667. }
  8668. removeSplatScene(index, showLoadingUI = true) {
  8669. if (this.isLoadingOrUnloading()) {
  8670. throw new Error('Cannot remove splat scene while another load or unload is already in progress.');
  8671. }
  8672. if (this.isDisposingOrDisposed()) {
  8673. throw new Error('Cannot remove splat scene after dispose() is called.');
  8674. }
  8675. let sortPromise;
  8676. this.splatSceneRemovalPromise = new Promise((resolve, reject) => {
  8677. let revmovalTaskId;
  8678. if (showLoadingUI) {
  8679. this.loadingSpinner.removeAllTasks();
  8680. this.loadingSpinner.show();
  8681. revmovalTaskId = this.loadingSpinner.addTask('Removing splat scene...');
  8682. }
  8683. const checkAndHideLoadingUI = () => {
  8684. if (showLoadingUI) {
  8685. this.loadingSpinner.hide();
  8686. this.loadingSpinner.removeTask(revmovalTaskId);
  8687. }
  8688. };
  8689. const onDone = (error) => {
  8690. checkAndHideLoadingUI();
  8691. this.splatSceneRemovalPromise = null;
  8692. if (!error) resolve();
  8693. else reject(error);
  8694. };
  8695. const checkForEarlyExit = () => {
  8696. if (this.isDisposingOrDisposed()) {
  8697. onDone();
  8698. return true;
  8699. }
  8700. return false;
  8701. };
  8702. sortPromise = this.sortPromise || Promise.resolve();
  8703. sortPromise.then(() => {
  8704. if (checkForEarlyExit()) return;
  8705. const savedSplatBuffers = [];
  8706. const savedSceneOptions = [];
  8707. const savedSceneTransformComponents = [];
  8708. const savedVisibleRegionFadeStartRadius = this.splatMesh.visibleRegionFadeStartRadius;
  8709. for (let i = 0; i < this.splatMesh.scenes.length; i++) {
  8710. if (i !== index) {
  8711. const scene = this.splatMesh.scenes[i];
  8712. savedSplatBuffers.push(scene.splatBuffer);
  8713. savedSceneOptions.push(this.splatMesh.sceneOptions[i]);
  8714. savedSceneTransformComponents.push({
  8715. 'position': scene.position.clone(),
  8716. 'quaternion': scene.quaternion.clone(),
  8717. 'scale': scene.scale.clone()
  8718. });
  8719. }
  8720. }
  8721. this.disposeSortWorker();
  8722. this.splatMesh.dispose();
  8723. this.createSplatMesh();
  8724. this.addSplatBuffers(savedSplatBuffers, savedSceneOptions, true, false, true)
  8725. .then(() => {
  8726. if (checkForEarlyExit()) return;
  8727. checkAndHideLoadingUI();
  8728. this.splatMesh.visibleRegionFadeStartRadius = savedVisibleRegionFadeStartRadius;
  8729. this.splatMesh.scenes.forEach((scene, index) => {
  8730. scene.position.copy(savedSceneTransformComponents[index].position);
  8731. scene.quaternion.copy(savedSceneTransformComponents[index].quaternion);
  8732. scene.scale.copy(savedSceneTransformComponents[index].scale);
  8733. });
  8734. this.splatMesh.updateTransforms();
  8735. this.splatRenderReady = false;
  8736. this.updateSplatSort(true)
  8737. .then(() => {
  8738. if (checkForEarlyExit()) {
  8739. this.splatRenderReady = true;
  8740. return;
  8741. }
  8742. sortPromise = this.sortPromise || Promise.resolve();
  8743. sortPromise.then(() => {
  8744. this.splatRenderReady = true;
  8745. onDone();
  8746. });
  8747. });
  8748. })
  8749. .catch((e) => {
  8750. onDone(e);
  8751. });
  8752. });
  8753. });
  8754. return this.splatSceneRemovalPromise;
  8755. }
  8756. /**
  8757. * Start self-driven mode
  8758. */
  8759. start() {
  8760. if (this.selfDrivenMode) {
  8761. if (this.webXRMode) {
  8762. this.renderer.setAnimationLoop(this.selfDrivenUpdateFunc);
  8763. } else {
  8764. this.requestFrameId = requestAnimationFrame(this.selfDrivenUpdateFunc);
  8765. }
  8766. this.selfDrivenModeRunning = true;
  8767. } else {
  8768. throw new Error('Cannot start viewer unless it is in self driven mode.');
  8769. }
  8770. }
  8771. /**
  8772. * Stop self-driven mode
  8773. */
  8774. stop() {
  8775. if (this.selfDrivenMode && this.selfDrivenModeRunning) {
  8776. if (!this.webXRMode) {
  8777. cancelAnimationFrame(this.requestFrameId);
  8778. }
  8779. this.selfDrivenModeRunning = false;
  8780. }
  8781. }
  8782. /**
  8783. * Dispose of all resources held directly and indirectly by this viewer.
  8784. */
  8785. async dispose() {
  8786. this.disposing = true;
  8787. let waitPromises = [];
  8788. let promisesToAbort = [];
  8789. for (let promiseKey in this.splatSceneDownloadPromises) {
  8790. if (this.splatSceneDownloadPromises.hasOwnProperty(promiseKey)) {
  8791. const downloadPromiseToAbort = this.splatSceneDownloadPromises[promiseKey];
  8792. promisesToAbort.push(downloadPromiseToAbort);
  8793. waitPromises.push(downloadPromiseToAbort.promise);
  8794. }
  8795. }
  8796. if (this.sortPromise) {
  8797. waitPromises.push(this.sortPromise);
  8798. }
  8799. const disposePromise = Promise.all(waitPromises).finally(() => {
  8800. this.stop();
  8801. if (this.controls) {
  8802. this.controls.dispose();
  8803. this.controls = null;
  8804. }
  8805. if (this.splatMesh) {
  8806. this.splatMesh.dispose();
  8807. this.splatMesh = null;
  8808. }
  8809. if (this.sceneHelper) {
  8810. this.sceneHelper.dispose();
  8811. this.sceneHelper = null;
  8812. }
  8813. if (this.resizeObserver) {
  8814. this.resizeObserver.unobserve(this.rootElement);
  8815. this.resizeObserver = null;
  8816. }
  8817. this.disposeSortWorker();
  8818. this.removeEventHandlers();
  8819. this.loadingSpinner.removeAllTasks();
  8820. this.loadingSpinner.setContainer(null);
  8821. this.loadingProgressBar.hide();
  8822. this.loadingProgressBar.setContainer(null);
  8823. this.infoPanel.setContainer(null);
  8824. this.camera = null;
  8825. this.threeScene = null;
  8826. this.splatRenderReady = false;
  8827. this.initialized = false;
  8828. if (this.renderer) {
  8829. if (!this.usingExternalRenderer) {
  8830. this.rootElement.removeChild(this.renderer.domElement);
  8831. this.renderer.dispose();
  8832. }
  8833. this.renderer = null;
  8834. }
  8835. if (!this.usingExternalRenderer) {
  8836. document.body.removeChild(this.rootElement);
  8837. }
  8838. this.sortWorkerSortedIndexes = null;
  8839. this.sortWorkerIndexesToSort = null;
  8840. this.sortWorkerPrecomputedDistances = null;
  8841. this.sortWorkerTransforms = null;
  8842. this.disposed = true;
  8843. this.disposing = false;
  8844. });
  8845. promisesToAbort.forEach((toAbort) => {
  8846. toAbort.abort();
  8847. });
  8848. return disposePromise;
  8849. }
  8850. selfDrivenUpdate() {
  8851. /* if (this.selfDrivenMode && !this.webXRMode) { //xzw delete
  8852. this.requestFrameId = requestAnimationFrame(this.selfDrivenUpdateFunc);
  8853. } */
  8854. this.update();
  8855. if (this.shouldRender()) {
  8856. this.render();
  8857. this.consecutiveRenderFrames++;
  8858. } else {
  8859. this.consecutiveRenderFrames = 0;
  8860. }
  8861. this.renderNextFrame = false;
  8862. }
  8863. forceRenderNextFrame() {
  8864. this.renderNextFrame = true;
  8865. }
  8866. shouldRender = function() {
  8867. let renderCount = 0;
  8868. const lastCameraPosition = new THREE.Vector3();
  8869. const lastCameraOrientation = new THREE.Quaternion();
  8870. const changeEpsilon = 0.0001;
  8871. return function() {
  8872. let shouldRender = false;
  8873. let cameraChanged = false;
  8874. if (this.camera) {
  8875. const cp = this.camera.position;
  8876. const co = this.camera.quaternion;
  8877. cameraChanged = Math.abs(cp.x - lastCameraPosition.x) > changeEpsilon ||
  8878. Math.abs(cp.y - lastCameraPosition.y) > changeEpsilon ||
  8879. Math.abs(cp.z - lastCameraPosition.z) > changeEpsilon ||
  8880. Math.abs(co.x - lastCameraOrientation.x) > changeEpsilon ||
  8881. Math.abs(co.y - lastCameraOrientation.y) > changeEpsilon ||
  8882. Math.abs(co.z - lastCameraOrientation.z) > changeEpsilon ||
  8883. Math.abs(co.w - lastCameraOrientation.w) > changeEpsilon;
  8884. }
  8885. shouldRender = this.renderMode !== RenderMode.Never && (renderCount === 0 || this.splatMesh.visibleRegionChanging ||
  8886. cameraChanged || this.renderMode === RenderMode.Always || this.dynamicMode === true || this.renderNextFrame);
  8887. if (this.camera) {
  8888. lastCameraPosition.copy(this.camera.position);
  8889. lastCameraOrientation.copy(this.camera.quaternion);
  8890. }
  8891. renderCount++;
  8892. return shouldRender;
  8893. };
  8894. }();
  8895. render = function() {
  8896. return function() {
  8897. if (!this.initialized || !this.splatRenderReady) return;
  8898. const hasRenderables = (threeScene) => {
  8899. for (let child of threeScene.children) {
  8900. if (child.visible) return true;
  8901. }
  8902. return false;
  8903. };
  8904. let oldLayer = this.camera.layers.mask
  8905. this.camera.layers.set(0)//xzw add
  8906. const savedAuoClear = this.renderer.autoClear;
  8907. /* if (hasRenderables(this.threeScene)) {//xzw
  8908. this.renderer.render(this.threeScene, this.camera);
  8909. this.renderer.autoClear = false;
  8910. } */
  8911. this.renderer.render(this.splatMesh, this.camera);
  8912. this.renderer.autoClear = false;
  8913. if (this.sceneHelper.getFocusMarkerOpacity() > 0.0) this.renderer.render(this.sceneHelper.focusMarker, this.camera);
  8914. if (this.showControlPlane) this.renderer.render(this.sceneHelper.controlPlane, this.camera);
  8915. this.renderer.autoClear = savedAuoClear;
  8916. this.camera.layers.mask = oldLayer
  8917. };
  8918. }();
  8919. update(renderer, camera) {
  8920. if (this.dropInMode) this.updateForDropInMode(renderer, camera);
  8921. if (!this.initialized || !this.splatRenderReady) return;
  8922. if (this.controls) {
  8923. this.controls.update();
  8924. if (this.camera.isOrthographicCamera && !this.usingExternalCamera) {
  8925. Viewer.setCameraPositionFromZoom(this.camera, this.camera, this.controls);
  8926. }
  8927. }
  8928. this.splatMesh.updateVisibleRegionFadeDistance(this.sceneRevealMode);
  8929. this.updateSplatSort();
  8930. this.updateForRendererSizeChanges();
  8931. this.updateSplatMesh();
  8932. this.updateMeshCursor();
  8933. this.updateFPS();
  8934. this.timingSensitiveUpdates();
  8935. this.updateInfoPanel();
  8936. this.updateControlPlane();
  8937. }
  8938. updateForDropInMode(renderer, camera) {
  8939. this.renderer = renderer;
  8940. if (this.splatMesh) this.splatMesh.setRenderer(this.renderer);
  8941. this.camera = camera;
  8942. if (this.controls) this.controls.object = camera;
  8943. this.init();
  8944. }
  8945. updateFPS = function() {
  8946. let lastCalcTime = getCurrentTime();
  8947. let frameCount = 0;
  8948. return function() {
  8949. if (this.consecutiveRenderFrames > CONSECUTIVE_RENDERED_FRAMES_FOR_FPS_CALCULATION) {
  8950. const currentTime = getCurrentTime();
  8951. const calcDelta = currentTime - lastCalcTime;
  8952. if (calcDelta >= 1.0) {
  8953. this.currentFPS = frameCount;
  8954. frameCount = 0;
  8955. lastCalcTime = currentTime;
  8956. } else {
  8957. frameCount++;
  8958. }
  8959. } else {
  8960. this.currentFPS = null;
  8961. }
  8962. };
  8963. }();
  8964. updateForRendererSizeChanges = function() {
  8965. const lastRendererSize = new THREE.Vector2();
  8966. const currentRendererSize = new THREE.Vector2();
  8967. let lastCameraOrthographic;
  8968. return function() {
  8969. if (!this.usingExternalCamera) {
  8970. this.renderer.getSize(currentRendererSize);
  8971. if (lastCameraOrthographic === undefined || lastCameraOrthographic !== this.camera.isOrthographicCamera ||
  8972. currentRendererSize.x !== lastRendererSize.x || currentRendererSize.y !== lastRendererSize.y) {
  8973. if (this.camera.isOrthographicCamera) {
  8974. this.camera.left = -currentRendererSize.x / 2.0;
  8975. this.camera.right = currentRendererSize.x / 2.0;
  8976. this.camera.top = currentRendererSize.y / 2.0;
  8977. this.camera.bottom = -currentRendererSize.y / 2.0;
  8978. } else {
  8979. this.camera.aspect = currentRendererSize.x / currentRendererSize.y;
  8980. }
  8981. this.camera.updateProjectionMatrix();
  8982. lastRendererSize.copy(currentRendererSize);
  8983. lastCameraOrthographic = this.camera.isOrthographicCamera;
  8984. }
  8985. }
  8986. };
  8987. }();
  8988. timingSensitiveUpdates = function() {
  8989. let lastUpdateTime;
  8990. return function() {
  8991. const currentTime = getCurrentTime();
  8992. if (!lastUpdateTime) lastUpdateTime = currentTime;
  8993. const timeDelta = currentTime - lastUpdateTime;
  8994. this.updateCameraTransition(currentTime);
  8995. this.updateFocusMarker(timeDelta);
  8996. lastUpdateTime = currentTime;
  8997. };
  8998. }();
  8999. updateCameraTransition = function() {
  9000. let tempCameraTarget = new THREE.Vector3();
  9001. let toPreviousTarget = new THREE.Vector3();
  9002. let toNextTarget = new THREE.Vector3();
  9003. return function(currentTime) {
  9004. if (this.transitioningCameraTarget) {
  9005. toPreviousTarget.copy(this.previousCameraTarget).sub(this.camera.position).normalize();
  9006. toNextTarget.copy(this.nextCameraTarget).sub(this.camera.position).normalize();
  9007. const rotationAngle = Math.acos(toPreviousTarget.dot(toNextTarget));
  9008. const rotationSpeed = rotationAngle / (Math.PI / 3) * .65 + .3;
  9009. const t = (rotationSpeed / rotationAngle * (currentTime - this.transitioningCameraTargetStartTime));
  9010. tempCameraTarget.copy(this.previousCameraTarget).lerp(this.nextCameraTarget, t);
  9011. this.camera.lookAt(tempCameraTarget);
  9012. this.controls.target.copy(tempCameraTarget);
  9013. if (t >= 1.0) {
  9014. this.transitioningCameraTarget = false;
  9015. }
  9016. }
  9017. };
  9018. }();
  9019. updateFocusMarker = function() {
  9020. const renderDimensions = new THREE.Vector2();
  9021. let wasTransitioning = false;
  9022. return function(timeDelta) {
  9023. this.getRenderDimensions(renderDimensions);
  9024. if (this.transitioningCameraTarget) {
  9025. this.sceneHelper.setFocusMarkerVisibility(true);
  9026. const currentFocusMarkerOpacity = Math.max(this.sceneHelper.getFocusMarkerOpacity(), 0.0);
  9027. let newFocusMarkerOpacity = Math.min(currentFocusMarkerOpacity + FOCUS_MARKER_FADE_IN_SPEED * timeDelta, 1.0);
  9028. this.sceneHelper.setFocusMarkerOpacity(newFocusMarkerOpacity);
  9029. this.sceneHelper.updateFocusMarker(this.nextCameraTarget, this.camera, renderDimensions);
  9030. wasTransitioning = true;
  9031. this.forceRenderNextFrame();
  9032. } else {
  9033. let currentFocusMarkerOpacity;
  9034. if (wasTransitioning) currentFocusMarkerOpacity = 1.0;
  9035. else currentFocusMarkerOpacity = Math.min(this.sceneHelper.getFocusMarkerOpacity(), 1.0);
  9036. if (currentFocusMarkerOpacity > 0) {
  9037. this.sceneHelper.updateFocusMarker(this.nextCameraTarget, this.camera, renderDimensions);
  9038. let newFocusMarkerOpacity = Math.max(currentFocusMarkerOpacity - FOCUS_MARKER_FADE_OUT_SPEED * timeDelta, 0.0);
  9039. this.sceneHelper.setFocusMarkerOpacity(newFocusMarkerOpacity);
  9040. if (newFocusMarkerOpacity === 0.0) this.sceneHelper.setFocusMarkerVisibility(false);
  9041. }
  9042. if (currentFocusMarkerOpacity > 0.0) this.forceRenderNextFrame();
  9043. wasTransitioning = false;
  9044. }
  9045. };
  9046. }();
  9047. updateMeshCursor = function() {
  9048. const outHits = [];
  9049. const renderDimensions = new THREE.Vector2();
  9050. return function() {
  9051. if (this.showMeshCursor) {
  9052. this.forceRenderNextFrame();
  9053. this.getRenderDimensions(renderDimensions);
  9054. outHits.length = 0;
  9055. this.raycaster.setFromCameraAndScreenPosition(this.camera, this.mousePosition, renderDimensions);
  9056. this.raycaster.intersectSplatMesh(this.splatMesh, outHits);
  9057. if (outHits.length > 0) {
  9058. this.sceneHelper.setMeshCursorVisibility(true);
  9059. this.sceneHelper.positionAndOrientMeshCursor(outHits[0].origin, this.camera);
  9060. } else {
  9061. this.sceneHelper.setMeshCursorVisibility(false);
  9062. }
  9063. } else {
  9064. if (this.sceneHelper.getMeschCursorVisibility()) this.forceRenderNextFrame();
  9065. this.sceneHelper.setMeshCursorVisibility(false);
  9066. }
  9067. };
  9068. }();
  9069. updateInfoPanel = function() {
  9070. const renderDimensions = new THREE.Vector2();
  9071. return function() {
  9072. if (!this.showInfo) return;
  9073. const splatCount = this.splatMesh.getSplatCount();
  9074. this.getRenderDimensions(renderDimensions);
  9075. const cameraLookAtPosition = this.controls ? this.controls.target : null;
  9076. const meshCursorPosition = this.showMeshCursor ? this.sceneHelper.meshCursor.position : null;
  9077. const splatRenderCountPct = splatCount > 0 ? this.splatRenderCount / splatCount * 100 : 0;
  9078. this.infoPanel.update(renderDimensions, this.camera.position, cameraLookAtPosition,
  9079. this.camera.up, this.camera.isOrthographicCamera, meshCursorPosition,
  9080. this.currentFPS || 'N/A', splatCount, this.splatRenderCount, splatRenderCountPct,
  9081. this.lastSortTime, this.focalAdjustment, this.splatMesh.getSplatScale(),
  9082. this.splatMesh.getPointCloudModeEnabled());
  9083. };
  9084. }();
  9085. updateControlPlane() {
  9086. if (this.showControlPlane) {
  9087. this.sceneHelper.setControlPlaneVisibility(true);
  9088. this.sceneHelper.positionAndOrientControlPlane(this.controls.target, this.camera.up);
  9089. } else {
  9090. this.sceneHelper.setControlPlaneVisibility(false);
  9091. }
  9092. }
  9093. updateSplatSort = function() {
  9094. const mvpMatrix = new THREE.Matrix4();
  9095. const cameraPositionArray = [];
  9096. const lastSortViewDir = new THREE.Vector3(0, 0, -1);
  9097. const sortViewDir = new THREE.Vector3(0, 0, -1);
  9098. const lastSortViewPos = new THREE.Vector3();
  9099. const sortViewOffset = new THREE.Vector3();
  9100. const queuedSorts = [];
  9101. const partialSorts = [
  9102. {
  9103. 'angleThreshold': 0.55,
  9104. 'sortFractions': [0.125, 0.33333, 0.75]
  9105. },
  9106. {
  9107. 'angleThreshold': 0.65,
  9108. 'sortFractions': [0.33333, 0.66667]
  9109. },
  9110. {
  9111. 'angleThreshold': 0.8,
  9112. 'sortFractions': [0.5]
  9113. }
  9114. ];
  9115. return async function(force = false) {
  9116. if (this.sortRunning) return;
  9117. if (this.splatMesh.getSplatCount() <= 0) return;
  9118. let angleDiff = 0;
  9119. let positionDiff = 0;
  9120. let needsRefreshForRotation = false;
  9121. let needsRefreshForPosition = false;
  9122. sortViewDir.set(0, 0, -1).applyQuaternion(this.camera.quaternion);
  9123. angleDiff = sortViewDir.dot(lastSortViewDir);
  9124. positionDiff = sortViewOffset.copy(this.camera.position).sub(lastSortViewPos).length();
  9125. if (!force) {
  9126. if (!this.sortNeededForSceneChange && !this.splatMesh.dynamicMode && queuedSorts.length === 0) {
  9127. if (angleDiff <= 0.99) needsRefreshForRotation = true;
  9128. if (positionDiff >= 1.0) needsRefreshForPosition = true;
  9129. if (!needsRefreshForRotation && !needsRefreshForPosition) return;
  9130. }
  9131. }
  9132. this.sortRunning = true;
  9133. const { splatRenderCount, shouldSortAll } = this.gatherSceneNodesForSort();
  9134. this.splatRenderCount = splatRenderCount;
  9135. mvpMatrix.copy(this.camera.matrixWorld).invert();
  9136. const mvpCamera = this.perspectiveCamera || this.camera;
  9137. mvpMatrix.premultiply(mvpCamera.projectionMatrix);
  9138. mvpMatrix.multiply(this.splatMesh.matrixWorld);
  9139. if (this.gpuAcceleratedSort && (queuedSorts.length <= 1 || queuedSorts.length % 2 === 0)) {
  9140. await this.splatMesh.computeDistancesOnGPU(mvpMatrix, this.sortWorkerPrecomputedDistances);
  9141. }
  9142. if (this.splatMesh.dynamicMode || shouldSortAll) {
  9143. queuedSorts.push(this.splatRenderCount);
  9144. } else {
  9145. if (queuedSorts.length === 0) {
  9146. for (let partialSort of partialSorts) {
  9147. if (angleDiff < partialSort.angleThreshold) {
  9148. for (let sortFraction of partialSort.sortFractions) {
  9149. queuedSorts.push(Math.floor(this.splatRenderCount * sortFraction));
  9150. }
  9151. break;
  9152. }
  9153. }
  9154. queuedSorts.push(this.splatRenderCount);
  9155. }
  9156. }
  9157. let sortCount = Math.min(queuedSorts.shift(), this.splatRenderCount);
  9158. cameraPositionArray[0] = this.camera.position.x;
  9159. cameraPositionArray[1] = this.camera.position.y;
  9160. cameraPositionArray[2] = this.camera.position.z;
  9161. const sortMessage = {
  9162. 'modelViewProj': mvpMatrix.elements,
  9163. 'cameraPosition': cameraPositionArray,
  9164. 'splatRenderCount': this.splatRenderCount,
  9165. 'splatSortCount': sortCount,
  9166. 'usePrecomputedDistances': this.gpuAcceleratedSort
  9167. };
  9168. if (this.splatMesh.dynamicMode) {
  9169. this.splatMesh.fillTransformsArray(this.sortWorkerTransforms);
  9170. }
  9171. if (!this.sharedMemoryForWorkers) {
  9172. sortMessage.indexesToSort = this.sortWorkerIndexesToSort;
  9173. sortMessage.transforms = this.sortWorkerTransforms;
  9174. if (this.gpuAcceleratedSort) {
  9175. sortMessage.precomputedDistances = this.sortWorkerPrecomputedDistances;
  9176. }
  9177. }
  9178. this.sortPromise = new Promise((resolve) => {
  9179. this.sortPromiseResolver = resolve;
  9180. });
  9181. this.sortWorker.postMessage({
  9182. 'sort': sortMessage
  9183. });
  9184. if (queuedSorts.length === 0) {
  9185. lastSortViewPos.copy(this.camera.position);
  9186. lastSortViewDir.copy(sortViewDir);
  9187. }
  9188. this.sortNeededForSceneChange = false;
  9189. };
  9190. }();
  9191. /**
  9192. * Determine which splats to render by checking which are inside or close to the view frustum
  9193. */
  9194. gatherSceneNodesForSort = function() {
  9195. const nodeRenderList = [];
  9196. let allSplatsSortBuffer = null;
  9197. const tempVectorYZ = new THREE.Vector3();
  9198. const tempVectorXZ = new THREE.Vector3();
  9199. const tempVector = new THREE.Vector3();
  9200. const modelView = new THREE.Matrix4();
  9201. const baseModelView = new THREE.Matrix4();
  9202. const sceneTransform = new THREE.Matrix4();
  9203. const renderDimensions = new THREE.Vector3();
  9204. const forward = new THREE.Vector3(0, 0, -1);
  9205. const tempMax = new THREE.Vector3();
  9206. const nodeSize = (node) => {
  9207. return tempMax.copy(node.max).sub(node.min).length();
  9208. };
  9209. return function(gatherAllNodes = false) {
  9210. this.getRenderDimensions(renderDimensions);
  9211. const cameraFocalLength = (renderDimensions.y / 2.0) / Math.tan(this.camera.fov / 2.0 * THREE.MathUtils.DEG2RAD);
  9212. const fovXOver2 = Math.atan(renderDimensions.x / 2.0 / cameraFocalLength);
  9213. const fovYOver2 = Math.atan(renderDimensions.y / 2.0 / cameraFocalLength);
  9214. const cosFovXOver2 = Math.cos(fovXOver2);
  9215. const cosFovYOver2 = Math.cos(fovYOver2);
  9216. const splatTree = this.splatMesh.getSplatTree();
  9217. if (splatTree) {
  9218. baseModelView.copy(this.camera.matrixWorld).invert();
  9219. baseModelView.multiply(this.splatMesh.matrixWorld);
  9220. let nodeRenderCount = 0;
  9221. let splatRenderCount = 0;
  9222. for (let s = 0; s < splatTree.subTrees.length; s++) {
  9223. const subTree = splatTree.subTrees[s];
  9224. modelView.copy(baseModelView);
  9225. if (this.splatMesh.dynamicMode) {
  9226. this.splatMesh.getSceneTransform(s, sceneTransform);
  9227. modelView.multiply(sceneTransform);
  9228. }
  9229. const nodeCount = subTree.nodesWithIndexes.length;
  9230. for (let i = 0; i < nodeCount; i++) {
  9231. const node = subTree.nodesWithIndexes[i];
  9232. if (!node.data || !node.data.indexes || node.data.indexes.length === 0) continue;
  9233. tempVector.copy(node.center).applyMatrix4(modelView);
  9234. const distanceToNode = tempVector.length();
  9235. tempVector.normalize();
  9236. tempVectorYZ.copy(tempVector).setX(0).normalize();
  9237. tempVectorXZ.copy(tempVector).setY(0).normalize();
  9238. const cameraAngleXZDot = forward.dot(tempVectorXZ);
  9239. const cameraAngleYZDot = forward.dot(tempVectorYZ);
  9240. const ns = nodeSize(node);
  9241. const outOfFovY = cameraAngleYZDot < (cosFovYOver2 - .6); //角度超出
  9242. const outOfFovX = cameraAngleXZDot < (cosFovXOver2 - .6);
  9243. if (!gatherAllNodes && ((outOfFovX || outOfFovY) && distanceToNode > ns)) {
  9244. continue;
  9245. }
  9246. splatRenderCount += node.data.indexes.length;
  9247. nodeRenderList[nodeRenderCount] = node;
  9248. node.data.distanceToNode = distanceToNode;
  9249. nodeRenderCount++;
  9250. }
  9251. }
  9252. nodeRenderList.length = nodeRenderCount;
  9253. nodeRenderList.sort((a, b) => { //从近到远排序。去掉后也不影响,毕竟之后还要sort index
  9254. if (a.data.distanceToNode < b.data.distanceToNode) return -1;
  9255. else return 1;
  9256. });
  9257. //将要渲染的点的index写入sortWorkerIndexesToSort的buffer
  9258. let currentByteOffset = splatRenderCount * Constants.BytesPerInt;
  9259. for (let i = 0; i < nodeRenderCount; i++) {
  9260. const node = nodeRenderList[i];
  9261. const windowSizeInts = node.data.indexes.length;
  9262. const windowSizeBytes = windowSizeInts * Constants.BytesPerInt;
  9263. let destView = new Uint32Array(this.sortWorkerIndexesToSort.buffer, //写入sortWorkerIndexesToSort
  9264. currentByteOffset - windowSizeBytes, windowSizeInts);
  9265. destView.set(node.data.indexes);
  9266. currentByteOffset -= windowSizeBytes;
  9267. }
  9268. return {
  9269. 'splatRenderCount': splatRenderCount,
  9270. 'shouldSortAll': false
  9271. };
  9272. } else {
  9273. const totalSplatCount = this.splatMesh.getSplatCount();
  9274. if (!allSplatsSortBuffer || allSplatsSortBuffer.length !== totalSplatCount) {
  9275. allSplatsSortBuffer = new Uint32Array(totalSplatCount);
  9276. for (let i = 0; i < totalSplatCount; i++) {
  9277. allSplatsSortBuffer[i] = i;
  9278. }
  9279. }
  9280. this.sortWorkerIndexesToSort.set(allSplatsSortBuffer);
  9281. return {
  9282. 'splatRenderCount': totalSplatCount,
  9283. 'shouldSortAll': true
  9284. };
  9285. }
  9286. };
  9287. }();
  9288. getSplatMesh() {
  9289. return this.splatMesh;
  9290. }
  9291. /**
  9292. * Get a reference to a splat scene.
  9293. * @param {number} sceneIndex The index of the scene to which the reference will be returned
  9294. * @return {SplatScene}
  9295. */
  9296. getSplatScene(sceneIndex) {
  9297. return this.splatMesh.getScene(sceneIndex);
  9298. }
  9299. isMobile() {
  9300. return navigator.userAgent.includes('Mobi');
  9301. }
  9302. }
  9303. /**
  9304. * DropInViewer: Wrapper for a Viewer instance that enables it to be added to a Three.js scene like
  9305. * any other Three.js scene object (Mesh, Object3D, etc.)
  9306. */
  9307. class DropInViewer extends THREE.Group {
  9308. constructor(options = {}) {
  9309. super();
  9310. options.selfDrivenMode = false;
  9311. options.useBuiltInControls = false;
  9312. options.rootElement = null;
  9313. options.ignoreDevicePixelRatio = false;
  9314. options.dropInMode = true;
  9315. options.camera = undefined;
  9316. options.renderer = undefined;
  9317. this.viewer = new Viewer(options);
  9318. this.splatMesh = null;
  9319. this.callbackMesh = DropInViewer.createCallbackMesh();
  9320. this.add(this.callbackMesh);
  9321. this.callbackMesh.onBeforeRender = DropInViewer.onBeforeRender.bind(this, this.viewer);
  9322. }
  9323. /**
  9324. * Add a single splat scene to the viewer.
  9325. * @param {string} path Path to splat scene to be loaded
  9326. * @param {object} options {
  9327. *
  9328. * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
  9329. * value (valid range: 0 - 255), defaults to 1
  9330. *
  9331. * showLoadingUI: Display a loading spinner while the scene is loading, defaults to true
  9332. *
  9333. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  9334. *
  9335. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  9336. *
  9337. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  9338. *
  9339. * onProgress: Function to be called as file data are received
  9340. *
  9341. * }
  9342. * @return {AbortablePromise}
  9343. */
  9344. addSplatScene(path, options = {}) {
  9345. if (options.showLoadingUI !== false) options.showLoadingUI = true;
  9346. return this.viewer.addSplatScene(path, options);
  9347. }
  9348. /**
  9349. * Add multiple splat scenes to the viewer.
  9350. * @param {Array<object>} sceneOptions Array of per-scene options: {
  9351. *
  9352. * path: Path to splat scene to be loaded
  9353. *
  9354. * splatAlphaRemovalThreshold: Ignore any splats with an alpha less than the specified
  9355. * value (valid range: 0 - 255), defaults to 1
  9356. *
  9357. * position (Array<number>): Position of the scene, acts as an offset from its default position, defaults to [0, 0, 0]
  9358. *
  9359. * rotation (Array<number>): Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1]
  9360. *
  9361. * scale (Array<number>): Scene's scale, defaults to [1, 1, 1]
  9362. * }
  9363. * @param {boolean} showLoadingUI Display a loading spinner while the scene is loading, defaults to true
  9364. * @return {AbortablePromise}
  9365. */
  9366. addSplatScenes(sceneOptions, showLoadingUI) {
  9367. if (showLoadingUI !== false) showLoadingUI = true;
  9368. return this.viewer.addSplatScenes(sceneOptions, showLoadingUI);
  9369. }
  9370. /**
  9371. * Get a reference to a splat scene.
  9372. * @param {number} sceneIndex The index of the scene to which the reference will be returned
  9373. * @return {SplatScene}
  9374. */
  9375. getSplatScene(sceneIndex) {
  9376. return this.viewer.getSplatScene(sceneIndex);
  9377. }
  9378. removeSplatScene(index) {
  9379. return this.viewer.removeSplatScene(index);
  9380. }
  9381. dispose() {
  9382. return this.viewer.dispose();
  9383. }
  9384. static onBeforeRender(viewer, renderer, threeScene, camera) {
  9385. if (this.splatMesh !== this.viewer.splatMesh) {
  9386. if (this.splatMesh) {
  9387. this.remove(this.splatMesh);
  9388. }
  9389. this.splatMesh = this.viewer.splatMesh;
  9390. this.add(this.viewer.splatMesh);
  9391. }
  9392. viewer.update(renderer, camera);
  9393. }
  9394. static createCallbackMesh() {
  9395. const geometry = new THREE.SphereGeometry(1, 8, 8);
  9396. const material = new THREE.MeshBasicMaterial();
  9397. material.colorWrite = false;
  9398. material.depthWrite = false;
  9399. const mesh = new THREE.Mesh(geometry, material);
  9400. mesh.frustumCulled = false;
  9401. return mesh;
  9402. }
  9403. }
  9404. export { AbortablePromise, CompressedPlyParser, DropInViewer, KSplatLoader, Utils as LoaderUtils, LogLevel, OrbitControls, PlyLoader, PlyParser, RenderMode, SceneFormat, SceneRevealMode, SplatBuffer, SplatBufferGenerator, SplatLoader, SplatParser, SplatPartitioner, Viewer, WebXRMode
  9405. ,Raycaster //add
  9406. };
  9407. //# sourceMappingURL=gaussian-splats-3d.module.js.map